1.设线性表L=(a1,a2,a3,…,a[n-2],a[n-1],an)采用带头结点的单链表保存,链表中结点定义如下:
typedef struct node{
int data;
struct node* next;
}NODE;
请设计一个空间复杂度为O(1)且时间上尽可能高效的算法,重新排列L中的个结点得到线性表L’=(a1,an,a2,a[n-1],a3,a[n-2],…)。
要求:
(1)给出算法的基本设计思想。
(2)根据设计思想,采用c或c++语言描述算法,关键之处给出注释。
(3)说明你所设计的算法的时间复杂度。【2019年全国试题41(13)分】
答:(1)将原有链表从中间分为两部分,分别是(a1,a2,a3,…a[n/2])和(a[n/2+1],a[n/2+2],…,a[n])。将后一部分链表逆置,再将两个链表合并。
(2)核心语句
int i=1;
p=head->next; //设head是指向带头结点的单链表的指针
while(i<n/2){ //n为元素个数(题目有提示)
p=p->next;//找到两个链表的分界点,即中间结点
i++;
}
r=p->next; //r即为第二个链表的头结点
p->next=null;//p结点是前一半的尾结点
q=null; //为逆置做准备,q指向逆置表的首元结点
while(r){//链表逆置
p=r->next;//暂存后继
r->next=q;//就地逆置
q=r; //r(或q)指向链表新首元
r=p; //指向待逆置结点
}
p=head->next;//head是指向带头结点的单链表的指针,p指向链表的第一个元素
while(p->next&&q->next){
p->next=r; //暂存两链表后继
q->next=s;
p->next=q->next; //合并链表,将q结点插入到对应p结点的后面
p=r; //恢复指向待合并结点
q=s;
}
if(p->next==null) //*避免n为奇数,这步有些小问题,稍后修改*
p->next=q;
else
q->next=p;
(3)算法的时间复杂度为O(n)。
总结
1.算法涉及
(1)拆分链表
(2)逆置链表
(3)合并链表
逆置链表的几种拓展:
1.无头结点的单链表的逆置
2.带附加头结点的单链表的逆置
eg1:有一个无头结点的单链表,结点有数据域data、指针域next,表头指针为h,通过遍历链表,将链表中所有的链接方向逆转。要求逆转后的链表的表头指针h指向原链表的最后一个结点。算法如下所示。【南京理工大学2005二、1(3分)】
void Inverse(&h){
if(h==null)
return ;
p=h->next;
pr=null;
while(p){
h->next=pr;
pr=h;
h=p;
p=p->next;
}
head->next=pr;
}
eg2:以下程序的功能就是实现带附加头结点的单链表数据结构逆序链接【西南交通大学2000一、9】
void reverse(pointer h){
/*h为附加头结点*/
pointer p,q;
p=h->next;
h->next=null;
while(p!=null){
q=p;
p=p->next;
q->next=h->next;
h->next=q;
}
}
1259





