2019年(单链表)41.(13分)设线性表L=(a1,a2,a3,...,an-2,an-1,an)采用带头结点的单链表保存,链表中的结点定义如下:
typedef struck node{
int data;
struct node *next;
}NODE;
请设计一个空间复杂度为O(1)且时间上尽可能高效的算法,重新排列L中的各结点得到线性表:
L=(a,an,a2,an-1,...)。
要求:
(1)给出算法的基本设计思想。
(2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释
(3)说明你所设计的算法的时间复杂度。
解读:首先空间复杂度是0(1)我们不能申请额外的空间,然后找到链表的中间结点,前面一半是链表L,将链表的后半部分给一个新的头结点L2,然后将链表L2进行原地逆置,然后再将L和L2链表进行合并。
代码部分:
/**
* *真题2019 41*
* 1.含头结点的单链表
* 2.空间复杂度为O(1)
* 3.将链表分为L1和L2(平均分)
* 4.L=(a1,an,a2,an-1...)
* 注意:思路勿与顺序表混在一起
*/
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
typedef struct LNode{ //构建一个单链表
ElemType data;//数据域
struct LNode *next;//指针域
}LNode,*LinkList;
/**
* ---尾插法--
* @param L
*/
void ListTailInsert(LinkList &L){
L=(LinkList)malloc(sizeof(LNode));
LinkList r;//尾指针
r=L;//初始时尾指针指向头结点
LinkList s ;
ElemType x;
scanf("%d",&x);
while(x!=00){
s=(LinkList)malloc(sizeof(LNode));
s->data=x;
r->next=s;//尾指针指向新结点
r=s;
scanf("%d",&x);
}
r->next=NULL;
}
/**
* 将L的后半部分放到L2中
* @param L
* @param L2
*/
void ListFindMiddle(LinkList L,LinkList &L2){
L2=(LinkList)malloc(sizeof(LNode));
LinkList p,q;
q=L->next; //同时指向第一个结点
p=L->next;
while (q!=NULL){
q=q->next;//指针q指向第二个结点
//如果此时指向的结点不为空则指向下一结点,若下一结点也不为空,则将p指针向后移一个结点
if(q==NULL){
break;
}
q=q->next;
if(q==NULL){
break;
}
p=p->next;
}
L2->next=p->next;//将L2的头指针指向L的中心结点的下一结点
p->next=NULL; //将L的中间结点的指针域赋值为空
}
/**
* 将L2逆转
* @param L2
*/
void ListReverse(LinkList L2){
LinkList s,r,t;//定义三个指针
s=L2->next;//s指向L2的第一个结点
if(s==NULL){ //判断头结点是否为空
return;
}
r=s->next;//r指向第二个结点
if(r==NULL){//判断第二个结点是否为空
return;
}
t=r->next;//指向第三个结点
while(t!=NULL){//当t指针指向的结点不为空时,进入循环
r->next=s;//将链表内两个结点之间的箭头逆转
s=r;//s指针向右移
r=t;//r指针向右移
t=t->next;//t指针向右移
}
r->next=s;//当t指针指向的结点为空时,链表尾部两个结点的未发生逆置,此时将两个结点之间的箭头逆转
L2->next->next=NULL;//将L2链表内第一个结点的指针域赋值为空
L2->next=r;//将L2的头指针指向r指针指向的结点
}
/**
* 将L与L2的值联结在一起
* @param L
* @param L2
*/
void ListAnastomosis(LinkList L,LinkList L2){
LinkList s,r,t;//定义三个同类型指针
s=L->next;//将指针s指向L的第一个结点
if(s==NULL){//如果L的第一个结点存在,则继续
return;
}
r=L;//指针r指向链表L
t=L2->next;//指针t指向链表L2的第一个结点
if(t==NULL){//判断L2的第一个结点是否为空
return;
}
//重新排列L
while(s!=NULL && t!=NULL){//当指针s和指针t指向的结点不为空时,进入循环
//将链表L中的一个结点的地址存到指针r的指针域,然后将指针r后移一位,再将链表L2中的一个结点的地址存到指针r的指针域
//直到指针s或者t出现空值,跳出循环
r->next=s;
s=s->next;
r=r->next;
r->next=t;
t=t->next;
r=r->next;
}
//如果(r,t)其中一个指针为空,则另一个为尾结点,尾结点的指针域必须为空
if(s==NULL){
r->next=t;
}
if(t==NULL){
r->next=s;
}
}
/**
* ----打印输出----
* @param L
*/
void ListPrint(LinkList L){
L=L->next;
while(L!=NULL){
printf("%d",L->data);//打印当前结点数据
L=L->next;//指向下一个结点
if(L!=NULL){
printf(" ");
}
}
printf("\n");
}
/**
* -----主函数------
* 函数的入口
* @return
*/
int main() {
LinkList L,L2; //定义两个头指针
ListTailInsert(L);//尾插法实现链表L
printf("------------- L2-> -------------\n");
ListFindMiddle(L,L2);//实现L2
ListPrint(L);
ListPrint(L2);
printf("------------- L2 <- -------------\n");
ListReverse(L2);//逆置L2
ListPrint(L2);
printf("------------- New L -------------\n");
ListAnastomosis(L,L2);//重排L
ListPrint(L);
free(L2);
return 0;
}