3.双链表和循环链表
双链表是基于对单链表的优化(由于单链表没有前驱指针,所以要找到前面一个结点不好找,只能去遍历,因此有了双链表)
怎么实现往前找呢?--->加一个指针域
双向链表 就是在单链表的基础上 每个结点增加一个指针域,该指针域保存上一个结点的地址
相关概念:与单链表一样
操作:增删改查
- 初始化一个带头结点的空的双链表
- 增:
- 头插法:固定在头结点后插入一个数据(结点),新插入的结点做首元结点
- 尾插:固定在链表最后插入一个数据(节点),新插入的节点做尾结点
- 中间插:在中间指定数据后插入一个数据(得先查找)
- 改+查:查在中间插中已有;改则在查后加入一个改数据域代码(p->data=k)即可
- 删除:删除指定的数据(结点)
- 判断链表是否为空 如果头结点后面没有首元结点就为空,
- 查找k所在结点
- 删除操作
循环链表:
单链表:头结点是l 尾结点是r(循环遍历找尾结点)
循环单链表:把单链表首尾相接:r->next=l
循环单链表的结点结构和单链表的结点结构一样
循环单链表的操作和单链表的操作一样,唯一的区别就是操作中的r->next=NULL 改为r->next=l;
双链表:头结点是l 尾结点是r(l->pre)
循环双链表:把双链表相接:l->pre=r r->next=l
循环双链表的结点结构和双链表的结点结构一样
循环双链表的操作和双链表的操作一样,唯一的区别就是操作中的r->next=NULL 改为r->next=l;
l->pre=NULL 改为 l->pre=r;
线性表:顺序表+链表
///
假设对线性表有4中操作:
- 删除第一个数据 ---头指/尾指针的循环单链表
- 删除最后一个数据 ---尾指针/头指针循环双链表
- 在第一个数据之前插入一个新数据
- 在最后一个数据之后插入一个新数据
现在用链式结构实现该线性表,以下哪个链式结构比较好:C
A 只有尾指针 没有头指针的循环单链表
B 只有尾指针 没有头指针的飞循环单链表
C 只有头指针 没有尾指针的循环双链表
D 既有头指针 又有尾指针的循环单链表
一个链表最常用的操作是末尾插入和删除,哪个链表最省时间A
A带头结点的循环双链表
B循环单链表
C带尾指针的循环单链表
D单链表
#include<stdio.h>
#include<stdlib.h>
typedef struct Node{
int data;//数据域
struct Node* next;//指针:保存下一个结点的地址
struct Node* pre; //指针:保存上一个结点的地址
}Node,*linklist;
//初始化一个带头结点的空双链表
linklist initlist()
{
linklist l=(Node *)malloc(sizeof(Node)); //声明了一个头指针l并分配了空间
if(l==NULL)
{
printf("头结点初始化,空间分配失败\n");
}
else
{
l->next=NULL;
l->pre=NULL;
}
return l;
}
//头插法 插入数据k
linklist head_insert(linklist l,int k)
{
Node* s=(Node*)malloc(sizeof(Node));
if(s==NULL)
{
printf("头插时,空间分配失败");
return l;
}
s->data=k;
//顺序可以不同(但要保证不能使插入的下一个数据丢失就行)
s->next=l->next;//1
if(l->next!=NULL)//看首元结点存不存存--->第一次对链表进行插入就不存在
{
l->next->pre=s;//2
}
l->next=s;//3
s->pre=l; //4
return l;
}
//尾插法
linklist tail_insert(linklist l,int k)
{
//利用循环遍历找到最后一个结点
Node *p=l;//声明一个指针p当作链表来辅助遍历
while(p->next!=NULL)
{
p=p->next;
}
Node *s=(Node*)malloc(sizeof(Node));
if(s==NULL)
{
printf("尾插时,空间分配失败\n");
return l;
}
s->data=k;
// //
p->next=s;
s->pre=p;
s->next=NULL;
//可以这样的顺序吗 ---->可以 说明这里可以随便可以顺序不一样
// s->next=NULL;
// s->pre=p;
// p->next=s;
return l;
}
//中间插入涉及查找---->查找涉及链表整体不为空即p!=NULL 插入(尾插法)涉及结点不为空即p->next!=NULL
linklist mid_insert(linklist l,int x,int k)
{
//查找
Node* p=l->next;//先让p=首元结点 遍历链表找到其对应数据域
while(p!=NULL&&p->data!=x)
{
p=p->next;
}
if(p==NULL)
{
printf("x不存在,无法插入\n");
return l;
}
//插入
Node *s=(Node*)malloc(sizeof(Node));
if(s==NULL)
{
printf("中间插入时,空间分配失败\n");
return l;
}
s->data=k;
s->next=p->next;//1
if(p->next!=NULL)//要把结点s赋值的需要判断
{
p->next->pre=s;//2
}
p->next=s;//3
s->pre=p;//4
return l;
}
void show(linklist l)
{
Node* p = l->next;//从首元结点开始
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
linklist delet(linklist l,int k)
{
//判断链表是否为空 如果头结点后面没有首元结点就为空
if(l->next==NULL)
{
printf("空链表,无法删除\n");
return l;
}
//查找k所在结点
Node *p=l->next;
while(p!=NULL&&p->data!=k)
{
p=p->next;
}
if(p==NULL)
{
printf("%d不存在,无法删除\n",k);
return l;
}
//删除操作
Node *s=p->pre;//把前一个地址赋值给s结点
s->next=p->next;//1
//如果p后面没节点了(p是最后一个结点), p->next就不存在,所以就不用 p->next->pre去指向s
if(p->next!=NULL)
{
p->next->pre=s;//2
}
free(p);
p=NULL;
return l;
}
int main()
{
linklist l=initlist();
//头插 9 7 6
l=head_insert(l,6);
l=head_insert(l,7);
l=head_insert(l,9);
show(l);
//尾插 9 7 6 10 9 7
l=tail_insert(l,10);
l=tail_insert(l,9);
l=tail_insert(l,7);
show(l);
//中间插入 9 7 8 6 10 9 7
l=mid_insert(l,7,8);
show(l);
//删除 9 7 8 10
l=delet(l,6);
show(l);
return 0;
}