注意我写的代码和课本的差不多,和PPT的风格还是有些区别的,但本质没啥区别其实,你会哪个都成
#define LIST_INIT_SIZE 100 //线性表存储空间的初始分配量
#define LISTINCREMENT 10 //线性表存储空间的分配增量
typedef struct
{
ElemType* elem; //存储空间基地址
int length; //表中元素的个数(表长)
int listsize; //表容量大小(以sizeof(ElemType)为单位)
} SqList; //顺序表类型定义
Status InitList_Sq(SqList &L){
//初始化L为一个空的有序顺序表
L.elem = (ElemType *)malloc(LIST_INIT_SIZE*sizeof(ElemType));
if(!L.elem)
exit(OVERFLOW);//存储分配失败
L.listsize = LIST_INIT_SIZE;//初始存储容量
L.length = 0;//空表长度为0
return OK;
}
int ListLocate_Sq(SqList L, ElemType e)
{
int a = -1;//给a赋初值,无论查找的数据在第几个,都不可能是第-1个,所以赋值-1
for(int i = 0;i<=L.length-1;i++)
{
if(L.elem[i] == e)
{
a = i;
break;
}
}
if(a>=0&&a<=L.length-1)
return a+1;
else
return ERROR;
}
Status ListInsert_Sq(SqList &L, int pos, ElemType e)
{
ElemType *newbase;
if(pos>=1&&pos<=L.length+1)//注意这个范围
{
if(L.length>=L.listsize)//此时要扩容
{
newbase = (ElemType*)realloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(ElemType));
if(!newbase)
return ERROR;
L.elem = newbase;//新分配空间的基地址
L.listsize+=LISTINCREMENT;
}
//即将进行插入操作
ElemType *p,*q;
p = &(L.elem[pos-1]);//将原来pos位置元素的地址分配给指针p,即p为插入位置
for(q = &(L.elem[L.length-1]);q>=p;--q)
*(q+1) = *q;//将原来顺序表最后一个位置数据的地址分配给q,然后从后往前依次将数据向后移动一位
*p = e;
++L.length;
return OK;
}
else
return OVERFLOW;
}
Status ListDelete_Sq(SqList &L, int pos, ElemType &e)
{
if(pos>=1&&pos<=L.length)//注意范围
{
ElemType *p;
e = L.elem[pos-1];//先将e赋值,也就是返回删掉了哪个数,因为要返回引用
for(p = &(L.elem[pos-1]);p<=&(L.elem[L.length-1]);p++)
*p = *(p+1);
--L.length;
return OK;
}
else
return OVERFLOW;
}
void ListPrint_Sq(SqList L)
{
for(int i = 0;i < L.length;i++)
{
if(i==0)
printf("%d",L.elem[i]);
else
printf(" %d",L.elem[i]);
}
}
void ListPrint_Sq(SqList L)
{
ElemType *p = L.elem;//遍历元素用的指针
for(int i = 0; i < L.length; ++i){
if(i == L.length - 1){
printf("%d", *(p+i));
}
else{
printf("%d ", *(p+i));
}
}
}
Status ListCreate_Sq(SqList &L)
{
//初始化
L.elem = (ElemType*)malloc(LIST_INIT_SIZE*sizeof(ElemType));
if(!L.elem)
return OVERFLOW;
L.length = 0;
L.listsize = LIST_INIT_SIZE;
//输入信息创建顺序表
int n;
scanf("%d",&n);
for(int i = 0;i < n;i++)
{
scanf("%d",&L.elem[i]);
L.length++;
}
return OK;
}
void ListReverse_Sq(SqList &L)
{
int tmp;
//int n;
for(int i = 0;i< n/2;i++)
{
tmp = L.elem[i];
L.elem[i]=L.elem[n-i-1];
L.elem[n-i-1] = tmp;
}
}
<1>顺序表的无序合并
SqList bingji(SqList &S1,SqList &S2)
{
SqList S;
SqListInit(S);
int i,j;
for(i = 0;i
<2>顺序表的有序合并(升序)
void MergeList_sq(SqList La,SqList Lb,SqList &Lc)
{
//实现代码
pa = La.elem;pb = Lb.elem;
Lc.listsize = Lc.length = La.length+Lb.length;
pc = Lc.elem = (ElemType*)malloc(Lc.listsize*sizeof(ElemType));
if(!Lc.elem)
exit(OVERFLOW);
pa_last = La.elem + La.length - 1;
pb_last = Lb.elem + Lb.length - 1;
while(pa<=pa_last&&pb<=pb_last)
{
if(*pa<=*pb)
*pc++ = *pa++;
else
*pc++ = *pb++;
}
while(pa<=pa_last)
*pc++ = *pa++;
while(pb<=pb_last)
*pc++ = *pb++;
/*
//算法大致代码
InitList(Lc);
i = j =1;k = 0;
La_len = ListLength(La);
Lb_Len = ListLength(Lb);
while(i<=La_len&&j<=Lb_len)
{
GetElem(La,i,ai);GetElem(Lb,j,bj);
if(ai<=bj)
{
ListInsert(Lc,++k,ai);
i++;
}
else
{
ListInsert(Lc,++k,bj);
j++;
}
}
while(i<=La_len)
{
GetElem(La,i,ai);
ListInsert(Lc,++k,ai);
}
while(j<=Lb_len)
{
GetElem(Lb,j,bj);
ListInsert(Lc,++k,bj);
}
*/
}
10.顺序表的插入并排序且考虑扩容
Status ListInsert_SortedSq(SqList &L, ElemType e)
{
ElemType *newbase;
if(L.length >= L.listsize)//扩容
{
newbase = (ElemType*)realloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(ElemType));
if(!newbase)
exit(OVERFLOW);
L.elem = newbase;
L.listsize += LISTINCREMENT;
}
int i,j;
for(i=0; e>*(L.elem+i)&ⅈ j--)
{
*(L.elem+j)=*(L.elem+j-1);//元素往后移一个位置
}
*(L.elem+i)=e;
L.length++;//增加链表长度
return OK;
}
这里要补充一个知识:
单链表插入和删除的时间复杂度都是O(N)。如果我们不知道第i个结点的指针位置,此时单链表和顺序表在插入删除操作方面几乎无差别。但如果,我们希望从第i个位置,插入10个结点,对于顺序存储结构意味着,每一次插入都要移动n-i个结点,每次都是O(n)。而单链表,我们只需要在第一次时,找到第i个位置的指针,此时为O(N),接下来只是简单地通过指针赋值移动指针而已,时间复杂度都是O(1)!!!
显然,插入或删除操作频繁时,单链表的效率优势越明显!!!
1.单链表的链式结构定义
typedef struct LNode
{
ElemType data; //数据域
struct LNode *next; //指针域
}LNode,*LinkList; //循环单链表类型定义与单链表定义相同,区别在尾节点next取值
2.单链表的读取操作(获取第i个元素位置)
Status GetElem(LinkList &L,int i,ElemType &e)
{
LinkList p = L->next;//p指向第一个结点
int j = 1;//计数器j
while(p&&jnext;//就是遍历,往后挪
j++;
}
if(j>i||!p)
return 0;
e = p->data;
return OK;
}
3.单链表的插入操作
Status LinkListInsert(LinkList &L,int pos,ElemType e)
{
LinkList p = L->next,s;
int j = 1;
while(p&&jnext;
j++;
}
if(!p&&j>pos-1)
return 0;
s = (LinkList)malloc(sizeof(LNode));
s->data = e;
s->next =p->next;//这两部是插入操作的核心
p->next =s;
return 1;
}
4.单链表的删除操作
Status ListDelete(LinkList &L,int i,ElemType &e)
{
LinkList p = L->next,q;
int j = 1;
while(p&&jnext;
j++;
}
if(!p||j>i-1)
return 0;
q = p->next;//这两步是删除操作的核心
p->next = q->next;
e = q->data;
free(q);//别忘了free
return 1;
}
补充一个(循环)单链表的区间删除函数
原理一画图就明白了
void ListDelete_CL(LinkList &CL, ElemType min, ElemType max)
{
LinkList p = CL->next,q;
while(p->next!=CL)
{
q = p->next;
if(q->data>min&&q->datanext = q->next;//删除操作
free(q);
}
else
p = p->next;
}
}
5.单链表的求表长操作
Status ListLength(LinkList &L)
{
LinkList p = L->next;
int j = 0;
while(p)
{
p = p->next;
j++;
}
return j;
}
以下是常考的地方
6.单链表常用打印操作
void ListPrint(LinkList &L)
{
LinkList p = L->next;//p指向第一个元素结点
int flag = 1;
while(p!=NULL)
{
if(flag)
{
cout<data;
flag = 0;
}
else
{
cout<<" "<data;
}
p = p->next;
}
cout<
7.单链表的判空操作
Status JudgeEmpty(LinkList &L)
{
LinkList p = L->next;
if(p)
return false;
else
return true;
}
8.单链表的元素定位操作
Status ListLocate(LinkList &L,ElemType e)
{
LinkList p = L->next;
int j = 1;
while(p&&e!=p->data)
{
p = p->next;
j++;
}
if(!p)
return 0;
else
return j;
}
8.单链表常用free函数
void ListFree(LinkList &L)//最好自己写free函数
{
LinkList p = L->next;
//LinkList rear;
//rear = L;
while(p)
{
free(p);
//rear = p;
p = p->next;
}
free(p);
}
9.单链表的整表创建
a.头插法
Status ListCreate_CL(LinkList &CL,int n)
{
LinkList p;
int i;
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
for(int i = 0;idata);
p->next = L->next;
L->next = p;//插入到表头
}
}
b.尾插法(正常思路,最常见)(两种)
Status ListCreate_L(LinkList &L,int n)
{
LinkList rear,p; //一个尾指针,一个指向新节点的指针
L=(LinkList)malloc(sizeof (LNode));
if(!L)exit(OVERFLOW);
L->next=NULL; //先建立一个带头结点的单链表
rear=L; //初始时头结点为尾节点,rear指向尾巴节点
for (int i=1;i<=n;i++){ //每次循环都开辟一个新节点,并把新节点拼到尾节点后
p=(LinkList)malloc(sizeof(LNode));//生成新结点
if(!p)exit(OVERFLOW);
scanf("%d",&p->data);//输入元素值
p->next=NULL; //最后一个节点的next赋空
rear->next=p;
rear=p;
}
return OK;
}
//或者
Status ListCreate_CL(LinkList &CL)
{
CL = (LinkList)malloc(sizeof(LNode));
//LinkList head = CL;
LinkList rear = CL;
rear->next = NULL;
int t;
while(cin>>t&&t!=-1)
{
LinkList p = (LinkList)malloc(sizeof(LNode));
p->data = t;
rear->next = p;//把p插入尾部,指针移到最后
rear = p;//数值移到最后
}
rear->next = NULL;//让最后的尾指针指向NULL,否则链表创建无法停止
return 0;
}
此外,补充一个循环单链表的整表创建(这里用尾插法实现,头插法我觉得是实现不了的)
//其实只有两步不同
Status ListCreate_CL(LinkList &CL)//尾插法创建循环单链表
{
CL = (LinkList)malloc(sizeof(LNode));
LinkList rear = CL;//第一个不同,尾指针指向头结点
rear->next = NULL;
int n;
scanf("%d",&n);
for(int i = 0;i < n;i++)
{
LinkList p = (LinkList)malloc(sizeof(LNode));
int t;
scanf("%d",&t);
p->data = t;
rear->next = p;
rear = p;
}
rear->next = CL;//第二个不同最后再次指向头结点
return OK;
}
10.单链表的整表删除
算法思路:
声明两个结点p和q
将第一个结点赋值给p
循环:
将下一个结点赋值给q
释放p
将q赋值给p
Status ClearList(LinkList &L)
{
LinkList p = L->next,q;
while(p)
{
q = p->next;
free(p);
q = p;
}
L->next = NULL;
return OK;
}
11.单链表的就地逆置(较难)
算法思想:逆置链表初始为空,表中节点从原链表中依次“删除”,再逐个插入逆置链表的表头(即“头插”到逆置链表中),使它成为逆置链表的“新”的第一个结点,如此循环,直至原链表为空。
void ListReverse_L(LinkList &L)//L为头结点
{
LinkList p = L->next,q;
L->next = NULL;
while(p)
{
//向后挪
q = p;//
p = p->next;
//头插
q->next = L->next;//非常重要,相当于p和q之间没有了指针连接
L->next = q;//把q接到头的后面
}
}
详细图解参考一位大佬的文章:https://blog.csdn.net/v_xchen_v/article/details/53067448
12.单链表的合并
void merge(LinkList La,LinkList Lb,LinkList &Lc){
LinkList pa,pb,pc;
pa=La->next;pb=Lb->next;
Lc=pc=La;
while(pa&&pb){
if(pa->data<=pb->data){
pc->next=pa;
pc=pa;
pa=pa->next;
}
else{
pc->next=pb;
pc=pb;
pb=pb->next;
}
}
pc->next=pa?pa:pb;
free(Lb);
}
13.单链表的递归创建
Status ListCreate_L_Rec(LinkList &L,int n)
{
//递归边界:创建空表时只需将L赋空即可;
//递归关系:创建非空表时,将链表看做两部分:首元素组成的子表La, 第二个元素及其后元素构成的子表Lb。
//子表La容易创建(只需开辟一个节点)
//子表Lb由于规模小可以递归创建完成(类似数学归纳法的假设,只要小的都可以建设能完成)
//最后将两个子表拼接即可。
LNode *La, *Lb;
if(n==0) L=NULL;
else{
La=(LNode *)malloc(sizeof(LNode)); //开辟子表La
if(!La) exit(OVERFLOW);
scanf("%d",&La->data);
ListCreate_L_Rec(Lb,n-1 ); //递归创建子表Lb
La->next = Lb; //两个子表拼结
L=La; //第一个子表的地址赋给L。请思考为什这样处理不会使得主函数中的L取错值?
}
return OK;
}
14.单链表的递归输出
方法1:递归法
void ListPrint_L_Rec(LinkList L)
{
if(!L)
return;
printf(" %d",L->data);
ListPrint_L_Rec(L->next);
return;
}
方法2:直接输出法
void ListPrint_L_Rec(LinkList L)
{
LinkList p = L;
//int flag = 1;
while(p)
{
//int flag = 1;
printf(" %d",p->data);
p = p->next;
}
printf("\n");
}
循环链表是另一种形式的链式存贮结构。它的特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环。
循环链表
(1)单循环链表——在单链表中,将终端结点的指针域NULL改为指向表头结点或开始结点即可。
(2)多重链的循环链表——将表中结点链在多个环上 。
判断空链表的条件是
head==head->next;
rear==rear->next;
用尾指针rear表示的单循环链表对开始结点a1和终端结点an查找时间都是O(1)。而表的操作常常是在表的首尾位置上进行,因此,实用中多采用尾指针表示单循环链表。带尾指针的单循环链表可见下图。
注意:判断空链表的条件为rear==rear->next;
循环链表的特点是无须增加存储量,仅对表的链接方式稍作改变,即可使得表处理更加方便灵活。
【例】在链表上实现将两个线性表(a1,a2,…,an)和(b1,b2,…,bm)连接成一个线性表(a1,…,an,b1,…bm)的运算。
分析:若在单链表或头指针表示的单循环表上做这种链接操作,都需要遍历第一个链表,找到结点an,然后将结点b1链到an的后面,其执行时间是O(n)。若在尾指针表示的单循环链表上实现,则只需修改指针,无须遍历,其执行时间是O(1)。
相应的算法如下:
LinkListConnect(LinkListA,LinkListB)
{//假设A,B为非空循环链表的尾指针
LinkListp=A->next;//①保存A表的头结点位置
A->next=B->next->next;//②B表的开始结点链接到A表尾
free(B->next);//③释放B表的头结点
B->next=p;//④
returnB;//返回新循环链表的尾指针
}
注意:
①循环链表中没有NULL指针。涉及遍历操作时,其终止条件就不再是像非循环链表那样判别p或p->next是否为空,而是判别它们是否等于某一指定指针,如头指针或尾指针等。
②在单链表中,从一已知结点出发,只能访问到该结点及其后续结点,无法找到该结点之前的其它结点。而在单循环链表中,从任一结点出发都可访问到表中所有结点,这一优点使某些运算在单循环链表上易于实现。
四.双向链表
参考博文:https://www.cnblogs.com/Braveliu/p/6408578.html
总结:
循环链表可以让我们从一个结点出发遍历所有节点
循环链表的判空条件p->next = head
此外,有时候不设立头指针而设立尾指针可以简化算法,例如两个表的合并
而双向链表的好处则在于对某一结点的前后结点操作带来了便利
但是他耗费了时间
带表头附加结点的双向循环链表为空的判断条件是头指针L满足条件(
)。
此外一个重点是它一般都有头指针而没有尾指针
一个做题技巧:
之后还会更新静态链表。
如果哪里代码有问题欢迎指正(下面的是之前写的,就不用看啦)。
一、顺序表的表示与实现
1.线性表的顺序结构定义
#define LIST_INIT_SIZE 100 //线性表存储空间的初始分配量
#define LISTINCREMENT 10 //线性表存储空间的分配增量
typedef struct
{
ElemType* elem; //存储空间基地址
int length; //表中元素的个数(表长)
int listsize; //表容量大小(以sizeof(ElemType)为单位)
} SqList; //顺序表类型定义
2.顺序表的初始化(构建空的线性表)
Status InitList_Sq(SqList &L){
//初始化L为一个空的有序顺序表
L.elem = (ElemType *)malloc(LIST_INIT_SIZE*sizeof(ElemType));
if(!L.elem)
exit(OVERFLOW);//存储分配失败
L.listsize = LIST_INIT_SIZE;//初始存储容量
L.length = 0;//空表长度为0
return OK;
}
3.顺序表的插入操作
Status ListInsert_Sq(SqList &L, int pos, ElemType e)
{
ElemType *newbase;
if(pos>=1&&pos<=L.length+1)//注意这个范围
{
if(L.length>=L.listsize)//此时要扩容
{
newbase = (ElemType*)realloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(ElemType));
if(!newbase)
return ERROR;
L.elem = newbase;//新分配空间的基地址
L.listsize+=LISTINCREMENT;
}
//即将进行插入操作
ElemType *p,*q;
p = &(L.elem[pos-1]);//将原来pos位置元素的地址分配给指针p,即p为插入位置
for(q = &(L.elem[L.length-1]);q>=p;--q)
*(q+1) = *q;//将原来顺序表最后一个位置数据的地址分配给q,然后从后往前依次将数据向后移动一位
*p = e;
++L.length;
return OK;
}
else
return OVERFLOW;
}
4.顺序表的删除操作
Status ListDelete_Sq(SqList &L, int pos, ElemType &e)
{
if(pos>=1&&pos<=L.length)//注意范围
{
ElemType *p;
e = L.elem[pos-1];//先将e赋值,也就是返回删掉了哪个数,因为要返回引用
for(p = &(L.elem[pos-1]);p<=&(L.elem[L.length-1]);p++)
*p = *(p+1);
--L.length;
return OK;
}
else
return OVERFLOW;
}
5.顺序表的查找操作
int ListLocate_Sq(SqList L, ElemType e)
{
int a = -1;//给a赋初值,无论查找的数据在第几个,都不可能是第-1个,所以赋值-1
for(int i = 0;i<=L.length-1;i++)
{
if(L.elem[i] == e)
{
a = i;
break;
}
}
if(a>=0&&a<=L.length-1)
return a+1;
else
return ERROR;
}
6.顺序表常见的输出函数
void ListPrint_Sq(SqList L)
{
ElemType *p = L.elem;//遍历元素用的指针
for(int i = 0; i < L.length; ++i){
if(i == L.length - 1){
printf("%d", *(p+i));
}
else{
printf("%d ", *(p+i));
}
}
}
7.顺序表的就地逆置
void ListReverse_Sq(SqList &L)
{
int tmp;
//int n;
for(int i = 0;i< n/2;i++)
{
tmp = L.elem[i];
L.elem[i]=L.elem[n-i-1];
L.elem[n-i-1] = tmp;
}
}
8.顺序表的合并
<1>顺序表的无序合并
SqList bingji(SqList &S1,SqList &S2)
{
SqList S;
SqListInit(S);
int i,j;
for(i = 0;i
<2>顺序表的有序合并(升序)
void MergeList_sq(SqList La,SqList Lb,SqList &Lc)
{
//实现代码
pa = La.elem;pb = Lb.elem;
Lc.listsize = Lc.length = La.length+Lb.length;
pc = Lc.elem = (ElemType*)malloc(Lc.listsize*sizeof(ElemType));
if(!Lc.elem)
exit(OVERFLOW);
pa_last = La.elem + La.length - 1;
pb_last = Lb.elem + Lb.length - 1;
while(pa<=pa_last&&pb<=pb_last)
{
if(*pa<=*pb)
*pc++ = *pa++;
else
*pc++ = *pb++;
}
while(pa<=pa_last)
*pc++ = *pa++;
while(pb<=pb_last)
*pc++ = *pb++;
/*
//算法大致代码
InitList(Lc);
i = j =1;k = 0;
La_len = ListLength(La);
Lb_Len = ListLength(Lb);
while(i<=La_len&&j<=Lb_len)
{
GetElem(La,i,ai);GetElem(Lb,j,bj);
if(ai<=bj)
{
ListInsert(Lc,++k,ai);
i++;
}
else
{
ListInsert(Lc,++k,bj);
j++;
}
}
while(i<=La_len)
{
GetElem(La,i,ai);
ListInsert(Lc,++k,ai);
}
while(j<=Lb_len)
{
GetElem(Lb,j,bj);
ListInsert(Lc,++k,bj);
}
*/
}
<2>(单)链表的表示与实现
1.单链表的链式结构定义
typedef struct LNode
{
ElemType data; //数据域
struct LNode *next; //指针域
}LNode,*LinkList; //循环单链表类型定义与单链表定义相同,区别在尾节点next取值
2.单链表的读取操作(获取第i个元素位置)
Status GetElem(LinkList &L,int i,ElemType &e)
{
LinkList p = L->next;//p指向第一个结点
int j = 1;//计数器j
while(p&&jnext;//就是遍历,往后挪
j++;
}
if(j>i||!p)
return 0;
e = p->data;
return OK;
}
3.单链表的插入操作
Status LinkListInsert(LinkList &L,int pos,ElemType e)
{
LinkList p = L->next,s;
int j = 1;
while(p&&jnext;
j++;
}
if(!p&&j>pos-1)
return 0;
s = (LinkList)malloc(sizeof(LNode));
s->data = e;
s->next =p->next;//这两部是插入操作的核心
p->next =s;
return 1;
}
4.单链表的删除操作
Status ListDelete(LinkList &L,int i,ElemType &e)
{
LinkList p = L->next,q;
int j = 1;
while(p&&jnext;
j++;
}
if(!p||j>i-1)
return 0;
q = p->next;//这两步是删除操作的核心
p->next = q->next;
e = q->data;
free(q);//别忘了free
return 1;
}
5.单链表的求表长操作
Status ListLength(LinkList &L)
{
LinkList p = L->next;
int j = 0;
while(p)
{
p = p->next;
j++;
}
return j;
}
6.单链表常用打印操作
void ListPrint(LinkList &L)
{
LinkList p = L->next;//p指向第一个元素结点
int flag = 1;
while(p!=NULL)
{
if(flag)
{
cout<data;
flag = 0;
}
else
{
cout<<" "<data;
}
p = p->next;
}
cout<
7.单链表的判空操作
Status JudgeEmpty(LinkList &L)
{
LinkList p = L->next;
if(p)
return false;
else
return true;
}
8.单链表的元素定位操作
Status ListLocate(LinkList &L,ElemType e)
{
LinkList p = L->next;
int j = 1;
while(p&&e!=p->data)
{
p = p->next;
j++;
}
if(!p)
return 0;
else
return j;
}
8.单链表常用free函数
void ListFree(LinkList &L)//最好自己写free函数
{
LinkList p = L->next;
//LinkList rear;
//rear = L;
while(p)
{
free(p);
//rear = p;
p = p->next;
}
free(p);
}
9.单链表的整表创建
a.头插法
Status ListCreate_CL(LinkList &CL,int n)
{
LinkList p;
int i;
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
for(int i = 0;idata);
p->next = L->next;
L->next = p;//插入到表头
}
}
b.尾插法(正常思路,最常见)(两种)
Status ListCreate_L(LinkList &L,int n)
{
LinkList rear,p; //一个尾指针,一个指向新节点的指针
L=(LinkList)malloc(sizeof (LNode));
if(!L)exit(OVERFLOW);
L->next=NULL; //先建立一个带头结点的单链表
rear=L; //初始时头结点为尾节点,rear指向尾巴节点
for (int i=1;i<=n;i++){ //每次循环都开辟一个新节点,并把新节点拼到尾节点后
p=(LinkList)malloc(sizeof(LNode));//生成新结点
if(!p)exit(OVERFLOW);
scanf("%d",&p->data);//输入元素值
p->next=NULL; //最后一个节点的next赋空
rear->next=p;
rear=p;
}
return OK;
}
//或者
Status ListCreate_CL(LinkList &CL)
{
CL = (LinkList)malloc(sizeof(LNode));
//LinkList head = CL;
LinkList rear = CL;
rear->next = NULL;
int t;
while(cin>>t&&t!=-1)
{
LinkList p = (LinkList)malloc(sizeof(LNode));
p->data = t;
rear->next = p;//把p插入尾部,指针移到最后
rear = p;//数值移到最后
}
rear->next = NULL;//让最后的尾指针指向NULL,否则链表创建无法停止
return 0;
}
10.单链表的整表删除
算法思路:
声明两个结点p和q
将第一个结点赋值给p
循环:
将下一个结点赋值给q
释放p
将q赋值给p
Status ClearList(LinkList &L)
{
LinkList p = L->next,q;
while(p)
{
q = p->next;
free(p);
q = p;
}
L->next = NULL;
return OK;
}
11.单链表的就地逆置(较难)
算法思想:逆置链表初始为空,表中节点从原链表中依次“删除”,再逐个插入逆置链表的表头(即“头插”到逆置链表中),使它成为逆置链表的“新”的第一个结点,如此循环,直至原链表为空。
void ListReverse_L(LinkList &L)//L为头结点
{
LinkList p,q;
p = L->next;
L->next = NULL;
while(p)
{
//向后挪
q = p;//
p = p->next;
//头插
q->next = L->next;//非常重要,相当于p和q之间没有了指针连接
L->next = q;//把q接到头的后面
}
}
详细图解参考一位大佬的文章:https://blog.csdn.net/v_xchen_v/article/details/53067448
12.单链表的合并
void merge(LinkList La,LinkList Lb,LinkList &Lc){
LinkList pa,pb,pc;
pa=La->next;pb=Lb->next;
Lc=pc=La;
while(pa&&pb){
if(pa->data<=pb->data){
pc->next=pa;
pc=pa;
pa=pa->next;
}
else{
pc->next=pb;
pc=pb;
pb=pb->next;
}
}
pc->next=pa?pa:pb;
free(Lb);
}
13.单链表的递归创建
Status ListCreate_L_Rec(LinkList &L,int n)
{
//递归边界:创建空表时只需将L赋空即可;
//递归关系:创建非空表时,将链表看做两部分:首元素组成的子表La, 第二个元素及其后元素构成的子表Lb。
//子表La容易创建(只需开辟一个节点)
//子表Lb由于规模小可以递归创建完成(类似数学归纳法的假设,只要小的都可以建设能完成)
//最后将两个子表拼接即可。
LNode *La, *Lb;
if(n==0) L=NULL;
else{
La=(LNode *)malloc(sizeof(LNode)); //开辟子表La
if(!La) exit(OVERFLOW);
scanf("%d",&La->data);
ListCreate_L_Rec(Lb,n-1 ); //递归创建子表Lb
La->next = Lb; //两个子表拼结
L=La; //第一个子表的地址赋给L。请思考为什这样处理不会使得主函数中的L取错值?
}
return OK;
}
14.单链表的递归输出
方法1:递归法
void ListPrint_L_Rec(LinkList L)
{
if(!L)
return;
printf(" %d",L->data);
ListPrint_L_Rec(L->next);
return;
}
方法2:直接输出法
void ListPrint_L_Rec(LinkList L)
{
LinkList p = L;
//int flag = 1;
while(p)
{
//int flag = 1;
printf(" %d",p->data);
p = p->next;
}
printf("\n");
}
之后还会更新静态链表,循环链表,双向链表的笔记。
如果哪里代码有问题欢迎指正