题目:
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");
}