题目:
2019 年 ( 单链表 )
41 .( 13 分)设线性表 L ( a 1 , a 2 , a 3 ,…… ,an2, a n 1 , a n ) 采用带头结点的单链表保存,链表中
的结点定义如下:
typedef struct node {
int data;
struct node* next;
} NODE ;
请设计一个空间复杂度为 O (1) 且时间上尽可能高效的算法,重新排列 L 中的各结点,
得到线性表 L ( a 1 , a n , a 2 , a n 1 , a 3 , a n 2 ,…… ) 。
思想:
读题发现本题主要实现前半部分顺序不变,后半部分变为逆序,且最后返回前后前后交叉的链表。
不妨 将前半部分和后半部分分开,单独处理后半部分,最后将后半部分插入里面。
所以:
1.将前半部分和后半部分分开-->即找到中间结点将链表一刀两断。
找中间结点---》不难想到双指针last、fast
(这里一定要注意将last定义last=L->next;fast=L->next;!第一次写的时候忘定值了last,fast=L->next;这样last并未赋值后面last移动不了)
双指针就是fast一下子移动两步,last一步一步走,当fast到终点的时候,last也就到中点了。(
但因为个树奇偶情况不同,所以fast需要移动一个判断一下,而只有移动两次的时候,last才移动,能保证一定在中间)
LinkList last,fast;
last=L->next;fast=L->next;
while(fast){fast=fast->next;if(!fast) break;fast=fast->next;if(!fast) break;last=last->next;}
这里一刀两断,方法太棒了,直接让last->next=NULL;斩断。
L2->next=last->next;
last->next=NULL;
2.单独处理后半部分
因为题目要求 空间复杂度为 O(1),所以考虑原地逆置。
原地逆置:其实就是把--->改为<---;然后原先第一个结点(L->next)的下一个设置为NULL,头结点后接最后一个节点
LinkList reverse(LinkList&L2) {if(!L2||!L2->next) return L2;LinkList pre=L2->next;LinkList p=pre->next;LinkList last;while(p){last=p->next;p->next=pre;pre=p;if(!last) break;p=last;}L2->next->next=NULL;//链表的第一个结点的next要为NULL; L2->next=p;return L2;
}
3.第三步就是合并了,按照前后前后合并就可以。合并的时候,记得设置结点保存后面的一个结点
//链表合并
void merge(LinkList &L,LinkList L2) {LinkList p1,p2;p1=L->next;p2=L2->next;while(p1&&p2){LinkList last1=p1->next;LinkList last2=p2->next;p1->next=p2;p2->next=last1;p1=last1;p2=last2;}if(p1) p1->next=NULL;if(p2) p2->next=NULL;
}
代码汇总:
typedef struct node{int data;struct node* next;
}LNode,*LinkList; //尾插法新建链表
LinkList creat_tail(LinkList &L){int x;scanf("%d",&x);L=(LNode*)malloc(sizeof(LNode));//创建头结点 LNode* tail;tail=L;while(x!=9999){LinkList s=(LNode*)malloc(sizeof(LNode));s->data=x;tail->next=s;s->next=NULL;tail=s;scanf("%d",&x);}return L;
} //找到中间节点--》双指针的运用,拆分巧妙
void find_mid(LinkList L,LinkList&L2){L2=(LNode*)malloc(sizeof(LNode));LinkList last,fast;last=L->next;fast=L->next;while(fast){fast=fast->next;if(!fast) break;fast=fast->next;if(!fast) break;last=last->next;}L2->next=last->next;last->next=NULL;}
//原地逆置
LinkList reverse(LinkList&L2) {if(!L2||!L2->next) return L2;LinkList pre=L2->next;LinkList p=pre->next;LinkList last;while(p){last=p->next;p->next=pre;pre=p;if(!last) break;p=last;}L2->next->next=NULL;//链表的第一个结点的next要为NULL; L2->next=p;return L2;
}
//链表合并
void merge(LinkList &L,LinkList L2) {LinkList p1,p2;p1=L->next;p2=L2->next;while(p1&&p2){LinkList last1=p1->next;LinkList last2=p2->next;p1->next=p2;p2->next=last1;p1=last1;p2=last2;}if(p1) p1->next=NULL;if(p2) p2->next=NULL;
}
//指针打印
void Print(LinkList L) {LNode* P=L->next;while(P){printf("%3d",P->data);P=P->next;}
}
int main(){LinkList L;creat_tail(L);Print(L);LinkList L2=NULL;printf("_____________\n");find_mid(L,L2);printf("_________________\n");Print(L);Print(L2);printf("__________________\n");L2=reverse(L2);Print(L2);printf("____________\n");merge(L,L2);free(L2);Print(L);printf("________________\n");
}