数据结构1————链表的基本操作
文章目录
一.链表的基本概念
- 链表是什么:
链表是一种逻辑顺序是连续的但物理存储单元上非连续、非顺序的存储结构。 - 链表实现方法:
链表是通过以结构体为节点,然后将所有节点通过结构体的指针域指向下一个节点连接起来(连接的实现),将数据存储到数据域中(存储的实现)。
struct node//节点
{
int data;//数据域 (数据域可以多组数据,多种类型)
struct lianbiao *next;//指针;
};
- 链表的优缺点(同数组相比):
优点:不需要预先知道数据的个数,删除添加只需要改变附近节点的指针域
缺点:创建使用麻烦 - 链表的图示:
说明:- 头指针为与链表节点类型相同的指针
- 一般情况情况下,头指针所指向的第一个节点不存数据,这样对在后面进行添加删除排序有很大的便利
- 最后一个节点指针域存NULL。方便在遍历链表时提示遍历完成
二.链表的建立
1.尾插法
- 特点:
输入顺序和存储顺序相同(同头插法相比) - 思路:
- \将新建的节点同之前已连接好的链表最后的节点连接起来,后
- 循环重复步骤1,直到符合终止条件停止循环。
- 使最后一个节点指针域为NULL
- 核心代码:
pEnd->next=pNew;//将新节点同之前已链接好的尾节点链接起来
pEnd=pNew;//尾节指针移动指向新的尾节点
pNew->next=NULL;//结束循环后使尾节点指针域为NULL
- 完整代码:
struct node *create()
{
int i;
struct node *pHead,*pNew,*pEnd;
printf("请输入正整数,输入小于0的数停止输入:\n");
pHead=pEnd=(struct node *)malloc(sizeof(struct node));//头指针所指的节点不存数据
pHead->next=NULL;
pNew=(struct node *)malloc(sizeof(struct node));//申请新节点
scanf("%d",&pNew->date);//向数据域输入数据
while(pNew->date>0)
{
pEnd->next=pNew;//连接
pNew->next=NULL;//新节点指针域变空
pEnd=pNew;
pNew=(struct node *)malloc(sizeof(struct node));
scanf("%d",&pNew->date);
}
free(pNew);//释放最后存入小于0的节点
return pHead;//返回头指针
}
- 图示:
画图示意输入 3 4 -1的过程
- 解释:
- pHead,pNew,pEnd 全部为指针变量,类型为结构体,不是结构体变量。
- pHead为头指针,pNew为新建的节点,pEnd为已连接好的节点最后一个节点
2. 头插法
- 特点:
存储顺序与输入顺序相反。 - 思路:
- 将新建节点插入到头结点(不存数据的节点)之后。
- 循环重复步骤1,直到遇到终止条件停止循环
- 核心代码:
pNew->next=pHead->next;//将新节点与第二个连接 pHead->next=pNew;//将新节点与头节点连接
- 完整代码:
struct node *create()
{
int i;
struct node *pHead,*pNew,*pEnd;
printf("请输入正整数,输入小于0的数停止输入:\n");
pHead=pEnd=(struct node *)malloc(sizeof(struct node));//头指针所指的节点不存数据
pHead->next=NULL;
pNew=(struct node *)malloc(sizeof(struct node));//申请新节点
scanf("%d",&pNew->date);//向数据域输入数据
while(pNew->date>0)
{
pNew->next=pHead->next;//将新节点与第二个连接
pHead->next=pNew;//将新节点与头节点连接
pNew=(struct node *)malloc(sizeof(struct node));
scanf("%d",&pNew->date);
}
free(pNew);//释放最后存入小于0的节点
return pHead;//返回头指针
}
- 图示:
此处应该有图,但画图太心累。大家自己动手画吧。●v●
三.链表的遍历以及增删改查
1.遍历链表
- 思路:
- 定义一个指针,初始化为头指针的指针域(不存数据时)
- 访问操作它所指向的节点的数据域
- 移动指针使它指向下一个节点。
- 循环重复1-3,直到指针指向NULL(指针移动到尾节点)
5.遍历是链表操作的基础
- 核心代码:
while(pt!=NULL)
{
pt=pt->next;
}
- 完整代码:
void output(struct node *pHead)
{
struct node *pt=pHead->next;
while(pt!=NULL)
{
printf("%d\n",pt->date);//输出
pt=pt->next;//指针移动
}
}
2.增加新节点
- 思路:
- 新建节点。
- 再利用指针遍历链表找到插入位置的前一个节点(pt),
- 操作指针,插入新建的节点
- 核心代码:
pNew->next=pt->next;
pHead->nt=pNew;
- 完整代码:
void increase(struct node *pHead)
{
struct node *pt=pHead,*pNew;
int a;
printf("你想插在数字几的后面:\n");
scanf("%d",&a);
while(pt&&pt->date!=a)
pt=pt->next;
if(pt==NULL)
printf("未找到这个数\n");
else
{
pNew=(struct node *)malloc(sizeof(struct node));
printf("输入需要插入的数");
scanf("%d",&pNew->date);
pNew->next=pt->next;
pt->next=pNew;
}
}
3. 删除节点
- 思路:
- 寻找需删除的节点和前一个节点
- 操作指针,连接删除节点的前后节点
- 释放需删除节点所占用的内存空间
- 核心代码:
pt_1->next=pt->next;
- 完整代码:
void strike_out(struct node *pHead)//删除
{
int a;
struct node *pt_1=pHead,*pt=pHead;
printf("请输入你想删除数字\n");
scanf("%d",&a);
while(pt&&pt->date!=a)
{
pt_1=pt;//该节点的前一个节点
pt=pt->next;
}
if(pt==NULL)
printf("未找到这个数!\n");
else
{
pt_1->next=pt->next;//删除
free(pt);//释放空间
printf("已删除!\n");
}
}
4.修改数据
- 思路:遍历链表,找到需改变的节点,操作数据域。
- 其他类似与遍历链表
5. 查找数据
- 思路:遍历链表,找到查询的节点,输出数据域。
- 其他类似与遍历链表。
四.升序合并,冒泡排序,插入排序
1. 升序合并
- 思路:
- 建立两个链表,a链和b链
- 定义三个链表指针变量,a,b,c
- a遍历a链,b遍历b链,c用来合并
- 比较a和b当前指的节点。
- c和两者中小的连接起来,移动c和两者中小的指针。循环重复,直到某条链表移动到尾部。将c和未遍历完连接起来。
- 核心代码:
c->next=a(b);//c连接值较小的节点
c=a(b);//c移动
a(b)=a(b)->next;//指向小的节点的指针移动
2. 冒泡排序
- 思路:
- 假设有n个节点。进行n趟排序,每趟比较0到第n-i-1个数(i为趟数)中相邻的节点(使用指针遍历)。
- 符合条件交换节点位置(也可以交换数据)。
- 然后移动指针,注意如果使用交换节点的方法进行冒泡时,有没有交换移动指针的方式不同。
- 核心代码:
交换两个相邻节点(pj和pj_h,pj_1为pj的前一个节点,pt为临时指针)
pt=pj->next;
pj->next=pj_h->next;
pj_h->next=pt;
pt=pj_1->next;
pj_1->next=pj_h->next;
pj_h->next=pt;
- 完整代码:
void sore(struct node *pHead) //排序
{
int i,j,flag;
struct node *pj_1,*pj,*pj_h,*pt;
for(i=0;i<iCound-1;i++)
for(j=0,pj=pHead,flag=0;j<iCound-i-1;j++)
{
if(flag==0)
{
pj_1=pj;
pj=pj->next;
pj_h=pj->next;
}
if(flag==1)
{
pj_1=pj_1->next;
pj_h=pj->next;
}
flag=0;
if((pj->aver)<(pj_h->aver))
{
pt=pj->next;
pj->next=pj_h->next;
pj_h->next=pt;
pt=pj_1->next;
pj_1->next=pj_h->next;
pj_h->next=pt;
flag=1;
}
}
}
3. 插入排序
- 思路:
1. 将无序节点插入到有序节点。
2. 有序表节点初始为第一个节点。
3. 将有序表的下一个节点(无序节点的第一个)按大小插入到有序表表中。
4. 插入时,先删除需插入的节点(但不释放内存)。再插入。 - 核心代码:
pi_1->next=pi->next;//删除
pi->next=pj->next;//连接
pj->next=pi;
- 完整代码:
void sore(struct node *pHead) //排序
{
struct node *pt,*pt_h,*pi;
pt=pHead->next;
pt_h=pt->next;
pHead->next=NULL;
while(pt){
for(pi=pHead;pi->next&&pi->next->expn<pt->expn;pi=pi->next);
pt_h=pt->next;
pt->next=pi->next;
pi->next=pt;
pt=pt_h;
}
}
五.简单应用
1.删除a链中与b链重复的节点
2. n个小孩报m个数的问题
- 问题:n个小孩从1到m报数,报到m的小孩出队,然后继续从0到m报数,报到m的小孩出队。当最后一个报完后,从第1个小孩继续报数,循环重复,直到只剩一名小孩,输出该小孩的序号。
- 思路:
1. 建立一个n个节点的循环链表(头结点也存数据,尾节点指针域存头结点地址)。
2. 遍历,删除报到第m个数节点。 - 完整代码:
- 想看源代码点我
- 或者点我
3.简单的学生管理习统
最后一些废话
写这个博客真很心累,特别是画图,强迫症的我为了对齐,画了好长时间,原计划要画好多图的,因为链表这一块画图真的很有助于理解。非常建议大家画图。最后,我也是初学者,有些地方难免有所疏漏,希望大家可以多多包涵。在评论中指出错误指出。
还有文中的超链接 一份是有道云笔记,一份是我的cmdn代码片。应该都可以直接运行。有什么错误也可以指出。