目录

一、单链表

1、存储方式

2、插入

3、删除

总代码:

二、静态链表

1、存储方式

2、插入

3、删除

4、遍历

总代码:

三、循环链表

总代码:

四、双向循环链表

1、存储方式:

2、插入和删除

3、正向遍历与反向遍历

总代码:


一、单链表

每个数据存储在结点中,单链表负责把这些结点串起来。(主要利用*next指针)

1、存储方式

从根结点开始(根结点可以不存储元素),用*next指针指向后面元素的地址,后面元素的*next继续指向更后面的元素,以此类推。(添加或插入元素要用malloc函数动态分配内存空间)头指针指向根,尾指针指向最后结点,便于插入删除。

//结点结构体
typedef struct Node
{int data;                      //数据域struct Node* next;     //指针域
}Node;
Node* head;
Node* tail;

2、插入

前面元素的*next断开,指向插入元素,插入元素的*next又指向后面的元素,完成“挤入”式插入。

//插入元素
void Insert(int index, int num)
{int i = 0;if (index < getLength() && index >= 0)       //插入下标有效{Node* s = (Node*)malloc(sizeof(Node)), * p = head;for (i = 0; i < index; i++)p = p->next;s->data = num;                //插入的元素值s->next = p->next;           //s->后面p->next = s;                      //前面->s}elseprintf("不在插值范围内!");
}

3、删除

待删除元素前面元素的*next断开,指向待删除元素后面的元素,释放掉待删除元素的空间。(前面malloc分配的内存需要释放)

//删除结点   (必须有外变量暂时保存要删除的结点)
//如果删除的是head结点,直接把head指向后面的结点即可
void Delete_Node(int index)
{int i = 0;Node* p = head, * s = NULL;if (index < getLength() && index >= 0)      //删除下标有效{for (i = 0; i < index; i++)p = p->next;                      //移动到前一位s = p->next;                                //要删除的结点p->next = p->next->next;      //前结点next->后结点(跳过要删除的结点)free(s);}elseprintf("没有该下标的结点!\n");
}

总代码:

//单链表
//动态内存分配malloc函数:            (指定的指针类型)malloc(要分配的字节数)
//头结点保存链表的首,可以在尾结点进行一系列操作(如添加),p表示动态的指针
#include <stdio.h>
#include <malloc.h>int i = 0;//结点结构体
typedef struct Node
{int data;                      //数据域struct Node* next;     //指针域
}Node;
Node* head;
Node* tail;//初始化链表
void Init_List()
{//初始化链表head = (Node*)malloc(sizeof(Node));tail = (Node*)malloc(sizeof(Node));head = tail;
}//创建结点
void Add_Node(int Data)
{Node* p = (Node*)malloc(sizeof(Node));p->data = Data;p->next = NULL;//指针后移tail->next = p;tail = p;
}//获取链表长度
int getLength()
{int num = 0;Node* p = head->next;                 //第一个数据存放在head->next里面while (p){num++;p = p->next;}return num;
}//遍历链表
void Print()
{Node* p = head->next;                  //第一个数据存放在head->next里面while (p){printf("%d\n", p->data);p = p->next;}
}//插入元素
void Insert(int index, int num)
{int i = 0;if (index < getLength() && index >= 0)       //插入下标有效{Node* s = (Node*)malloc(sizeof(Node)), * p = head;for (i = 0; i < index; i++)p = p->next;s->data = num;                //插入的元素值s->next = p->next;           //s->后面p->next = s;                      //前面->s}elseprintf("不在插值范围内!");
}//删除结点 (必须有外变量暂时保存要删除的结点)
//如果删除的是head结点,直接把head指向后面的结点即可
void Delete_Node(int index)
{int i = 0;Node* p = head, * s = NULL;if (index < getLength() && index >= 0)      //删除下标有效{for (i = 0; i < index; i++)p = p->next;                      //移动到前一位s = p->next;                                //要删除的结点p->next = p->next->next;      //前结点next->后结点(跳过要删除的结点)free(s);}elseprintf("没有该下标的结点!\n");
}//清空链表
void Clear_List()
{Node* p = head->next, * q;while (p){q = p->next;free(p);p = q;}head->next = NULL;
}int main()
{//初始化链表Init_List();//创建链表for (i = 0; i < 5; i++){Add_Node(i);}//插入结点Insert(0, 5);//删除结点(第二次删除注意:位置改变)Delete_Node(0);//清空链表Clear_List();//遍历Print();return 0;
}

二、静态链表

以数组的方式存储,虽然需要分配较大的内存空间,但是插入/删除比较容易,不需要移动数组元素,只用改变游标(cur)指向即可即可。

1、存储方式

创建一个数组(index是下标,cur是游标)

cur(游标):存放前一个元素的index,通过它实现连接。

整个链表被分成两部分:1、有值链表(存放有元素)2、备用链表(不存放元素)

首元素(index=0)的cur:                           存放备用链表的首元素。

尾元素(index=MAXSIZE-1)的cur:        存放有值链表的首元素。

链表构成:1、以首元素为首构成的备用链表;        2、以尾元素为首构成的有值链表。

//静态链表结构体
typedef struct
{int data;          //数据int cur;                //游标
}StaticList;
StaticList L[MAXSIZE];

2、插入

只移动游标,不移动元素。

//插入链表
void Insert(int index, int num)
{int pre = MAXSIZE - 1;if (index <= 0 || index > L[0].cur)          //插入范围无效(第1个和最后一个元素不存放数据){printf("%d Insert Error!\n", num);return;}//移动到前一个元素(有效元素)for (i = 1; i < index; i++)pre = L[pre].cur;                         L[0].cur = L[index].cur;               //链表首元素游标指向备用链表首元素L[index].data = num;
}

3、删除

被删除的元素作为备用链表首元素,其游标连接原备用链表首元素,原备用链表首元素变成第二个元素(向后移)。

//删除链表(把需要删除的元素降级,变为备用链表的首元素)
void Delete(int index)
{int pre = MAXSIZE - 1, j;if (index < 1 || index >= L[0].cur){printf("要删除的下标无效!\n");return;}for (i = 1; i <= index - 1; i++)pre = L[pre].cur;                      //移动到需要删除的前一个位置(有元素位)L[index].cur = L[0].cur;                    //1、游标指向原备用元素(被删除的元素作为备用元素的首)L[0].cur = index;                           //2、更新备用链表首元素(被删除的元素)L[pre].cur = index;                     //3、连接前一个元素和后一个元素(游标)
}

4、遍历

从index = MAXSIEZE-1处开始遍历(从尾元素开始遍历),因为尾元素的cur存有有效链表的首元素。

//遍历链表
void Traverse()
{int cur;cur = L[MAXSIZE - 1].cur;         //指向有效链表的首元素for (i = 1; i < L[0].cur; i++)            //从第一个开始,到最后一个结束(备用链表的首元素就是最后一个元素的下一个元素){printf("%d\n", L[cur].data);cur = L[cur].cur;                      //有效链表中继续后移}
}

总代码:

//静态链表
//插入/删除时只修改游标,不移动结点
//游标存放下标,前一元素游标存放后一元素的下标
//第一个元素游标:   存放备用链表第一个元素的下标
//最后一个元素游标:存放有元素链表第一个元素的下标
//把有效元素作为一个循环,用pre=MAXSIZE-1标志开始,因为L[MAXSIZE-1].cur指向有效链表首元素,相当于在有效链表内循环
#include <stdio.h>#define MAXSIZE 20
int i = 0;typedef struct
{int data;int cur;
}StaticList;
StaticList L[MAXSIZE];//链表初始化
void List_Init()
{//初始化游标for (i = 0; i < MAXSIZE; i++){L[i].cur = i + 1;                         //游标指向下一个}L[MAXSIZE - 1].cur = 1;              //最后一个元素的游标(有效元素)
}//根据游标获取下标(这里没用到)
int getIndex(int Cur)
{for (i = 0; i < MAXSIZE; i++){if (L[i].cur == Cur)     //找到对应游标的元素return i;                    //返回下标}return -1;                           //没有找到
}//获取长度
int getLength()
{int num = 0;i = L[MAXSIZE - 1].cur;if (L[0].cur == L[MAXSIZE - 1].cur)         //没有元素return 0;while (i < L[0].cur){i = L[i].cur;num++;}return num;
}//插入链表
void Insert(int index, int num)
{int pre = MAXSIZE - 1;if (index <= 0 || index > L[0].cur)          //插入范围无效(第1个和最后一个元素不存放数据){printf("%d Insert Error!\n", num);return;}//移动到前一个元素(有效元素)for (i = 1; i < index; i++)pre = L[pre].cur;                         L[0].cur = L[index].cur;               //链表首元素游标指向备用链表首元素L[index].data = num;L[0].cur = L[index].cur;                //更新备用链表
}//删除链表(把需要删除的元素降级,变为备用链表的首元素)
void Delete(int index)
{int pre = MAXSIZE - 1, j;if (index < 1 || index >= L[0].cur){printf("要删除的下标无效!\n");return;}for (i = 1; i <= index - 1; i++)pre = L[pre].cur;                      //移动到需要删除的前一个位置(有元素位)L[index].cur = L[0].cur;                    //1、游标指向原备用元素(被删除的元素作为备用元素的首)L[0].cur = index;                           //2、更新备用链表首元素(被删除的元素)L[pre].cur = index;                     //3、连接前一个元素和后一个元素(游标)
}//遍历链表
void Traverse()
{int cur;cur = L[MAXSIZE - 1].cur;         //指向有效链表的首元素for (i = 1; i < L[0].cur; i++)            //从第一个开始,到最后一个结束(备用链表的首元素就是最后一个元素的下一个元素){printf("%d\n", L[cur].data);cur = L[cur].cur;                      //有效链表中继续后移}
}int main()
{List_Init();                       //链表初始化printf("插入前元素总数:%d\n", getLength());Insert(1, 6);                   //插入链表Insert(2, 8);Insert(3, 10);printf("删除前元素总数:%d\n", getLength());Delete(3);Delete(2);                      //删除链表Insert(2, 8);printf("删除后元素总数:%d\n", getLength());Traverse();return 0;
}

三、循环链表

循环链表顾名思义,尾连接头,实现循环。

存储结构和链表类似,就多一个头尾相接,完成循环。

总代码:

//循环链表
//用了两个指针,一个指向头,一个指向尾,尾连接上头,完成循环链表
#include <stdio.h>
#include <malloc.h>int i = 0;//结构体
typedef struct Node
{int data;struct Node* next;
}Node;
Node* head;         //头结点
Node* tail;             //尾结点
Node* t;                    //遍历结点//链表初始化
void List_Init()
{head = (Node*)malloc(sizeof(Node));           //分配地址tail = (Node*)malloc(sizeof(Node));          //分配地址head->next = tail;tail->next = head;
}//获取链表长度
int getLength() {int num = 0;Node* L = head;while (L->next != tail){L = L->next;num++;}return num;
}//插入链表
void Insert(int index, int num)
{if (index<0 || index>getLength()){printf("%d 序号不在范围内\n", num);return;}Node* p = (Node*)malloc(sizeof(Node)), * L;p->next = NULL;L = head;                            //指向头while (L->next != tail)        //移动到尾结点前面(最后一个有效结点)L = L->next;L->next = p;p->data = num;tail = p->next;                 //更新尾结点
}//删除链表
void Delete(int index)
{if (index < 0 || index >= getLength()){printf("无该序号\n");return;}Node* L = head, * t;for (i = 0; i < index; i++)L = L->next;t = L->next;L->next = L->next->next;free(t);
}//遍历链表
void Traverse()
{Node* L = head;while (L->next != tail)                //头尾循环{L = L->next;printf("%d\n", L->data);}
}int main()
{List_Init();                   //链表初始化Insert(0, 0);                //插入链表Insert(1, 1);Insert(2, 2);Delete(2);                  //删除链表Delete(1);                    //第二次删除位置改变Traverse();                      //遍历链表return 0;
}

四、双向循环链表

双向循环链表优势:可以完成双向的遍历。

1、存储方式:

双向:*prior指针和*next指针分别指向前后两个元素。

循环:头结点的*prior指向尾结点,尾结点的*next指向头结点。

//双向循环链表
typedef struct Node
{int data;struct Node* prior;struct Node* next;
}Node;

2、插入和删除

插入:一个遍历链表,另一个进行插入(一开始不要进入链表,散置,不然会一直循环)。
删除:也要同时考虑next和prior指针。

3、正向遍历与反向遍历

正向遍历:从头结点head出发,向尾部遍历,直到遇到tail结束遍历。

反向遍历:从尾结点tail出发,向头部遍历,直到遇到head结束遍历。

//正向遍历链表
void Traverse()
{Node* p = head;printf("正向遍历:\n");while (p->next != tail){p = p->next;printf("%d\n", p->data);}
}//反向遍历链表
void AntiTraverse()
{Node* p = tail;printf("反向遍历:\n");while (p->prior != head){p = p->prior;printf("%d\n", p->data);}
}

总代码:

//双向循环链表
//前指针和后指针分别连接前面和后面的元素
//插入:一个遍历链表,另一个进行插入(一开始不要进入链表,散置,不然会一直循环)
//删除:也要同时考虑next和prior指针
#include <stdio.h>
#include <malloc.h>//双向循环链表
typedef struct Node
{int data;struct Node* prior;struct Node* next;
}Node;Node* head;           //头
Node* tail;             //尾
int i = 0;//链表初始化
void Init_List()
{head = (Node*)malloc(sizeof(Node));tail = (Node*)malloc(sizeof(Node));head->prior = tail;head->next = tail;tail->next = head;tail->prior = head;//赋一个值,便于调试tail->data = -2;head->data = -1;
}//获取元素数量
int getLength()
{int num = 0;Node* p = head;while (p->next != tail){p = p->next;num++;}return num;
}//链表插入
void Insert(int index, int num)
{if (index<0 || index>getLength()){printf("插入下标不正确!\n");return;}Node* t = (Node*)malloc(sizeof(Node)), * p;p = head;for (i = 0; i < index; i++){p = p->next;                 //移动到前一个元素}t->prior = p;t->next = p->next;p->next->prior = t;             //这一步要在前面p->next = t;                           //这一步要在后面,负责p->next已经改变,前面的没有意义了t->data = num;
}//删除链表
void Delete(int index)
{if (index < 0 || index >= getLength()){printf("删除下标不正确!\n");return;}Node* p = head, * s;for (i = 0; i < index; i++)p = p->next;s = p->next;p->next->next->prior = p;p->next = p->next->next;free(s);
}//正向遍历链表
void Traverse()
{Node* p = head;printf("正向遍历:\n");while (p->next != tail){p = p->next;printf("%d\n", p->data);}
}//反向遍历链表
void AntiTraverse()
{Node* p = tail;printf("反向遍历:\n");while (p->prior != head){p = p->prior;printf("%d\n", p->data);}
}int main()
{Init_List();                       //链表初始化Insert(0, 0);                    //插入元素Insert(1, 1);Insert(2, 2);Delete(2);                  //删除元素Traverse();                       //正向遍历AntiTraverse();                   //反向遍历return 0;
}

数据结构与算法(2-2)线性表之链式存储(单链表、静态链表、循环链表、双向循环链表)相关推荐

  1. 数据结构——线性表之链式存储结构

    单链表: 概念: 1.由于线性表的顺序存储在插入与删除时需要移动大量元素,适用于不经常改变元素的情况,那么当我们需要经常操作元素时该怎么办,这就有了接下来的线性表的链式存储结构 2.单链表在内存的存储 ...

  2. 线性表之链式存储结构

    线性表的顺序存储结构,插入和删除时需要移动大量元素,耗费时间,可解决这些问题. 转载于:https://www.cnblogs.com/cailingsunny/p/4562287.html

  3. 线性表之链式存储结构_单链表相关算法

    在存储结构上,不需要连续的存储空间,需要上一个结点的指针域 指向下一个结点即可,找到一个结点就可以找到下一个结点. 学习教材是大话数据结构,加上自己的一些个人理解.这个算法 有点绕,需要对指针 相关内 ...

  4. 数据结构-- 线性表之链式存储

    https://www.cnblogs.com/ZWOLF/p/10604252.html

  5. 【数据结构与算法】之线性表的应用和操作

    数据结构概念 数据结构:是相互之间存在一种或多种特定关系的数据元素的集合. 数据结构的逻辑结构:数据对象中数据元素之间的相互关系,分为线性结构.树形结构.图形结构以及集合结构. 数据结构的物理结构:数 ...

  6. 数据结构与算法2:线性表的顺序存储与链式存储

    文章目录 线性表 定义 线性表的抽象数据类型(Abstract Data Type) 线性表的顺序存储结构 线性表的链式存储结构 单链表(single linked list) 静态链表 (stati ...

  7. 数据结构之线性表-链式存储之单链表(一)

    本人文笔较差,语文从来不及格,基础不好,写此类文章仅供自己学习,理解队列及其他知识,高手大神请略过.参考书籍 <数据结构与算法分析-Java语言描述> 1.1 单链表简介 线性表的最大的缺 ...

  8. 线性表-链式存储结构

    3.6 线性表的链式存储结构 3.6.1 顺序存储结构不足的解决办法 前面我们讲的线性表的顺序存储结构.它是有缺点的,最大的缺点就是插入和删除时需要移动大量元素,这显然就需要耗费时间.能不能想办法解决 ...

  9. 【数据结构与算法基础】线性表

    写在前面 挺早之前看的数据结构和算法了,但是最近刷LeetCode上题目的时候发现还不是很熟练(都忘光了....),于是狠心再来一遍.边整理边刷题,用的教材是北大裘宗燕的<数据结构与算法pyth ...

最新文章

  1. c专家编程/c陷阱_如何避免常见的初学者陷阱并像专家一样开始编码
  2. 【JavaSE01】初识Java-思维导图
  3. Java线程 Thread 的6种状态以及转变过程
  4. linux查看编译器的大小端,Linux系统大小端判断
  5. Oracle删除重复数据并且只留其中一条数据
  6. C++学习系列笔记(二)
  7. 3个概念,入门 Vue 组件开发
  8. 【前端 · 面试 】HTTP 总结(四)—— HTTP 状态码
  9. praat 语音识别教程
  10. 把合数分解成若干个质因数相成
  11. 惠普linux打印驱动怎么安装驱动程序,hp打印机驱动怎么安装 惠普打印机驱动程序安装方法【详解】...
  12. 全球气象数据的网站集合数据包含(大气数据、海洋数据等各种数据)
  13. 预售┃让苹果CEO库克折服的程序员仅10岁!?
  14. 汤姆熊游艺厅抓娃娃技巧汇总
  15. DSP F2803x中CLA模块介绍
  16. Mac上Unity打ab包报错 Moving file failed. … No Such file or director
  17. 如何批量重命名多张图片
  18. TrendMicro Officescan 7卸载方法
  19. php 天干地支,php实现天干地支计算器示例
  20. 防范电话诈骗的八种方法

热门文章

  1. Android 使用 ADB 命令录制屏幕上的视频
  2. 需求分析阶段项目经验
  3. k8s系列----一个简单的例子
  4. 计算机基础知识第十讲,计算机文化基础(第十讲)学习笔记
  5. C++ 笔记(02)— 程序结构(头文件说明、命名空间、函数返回值、函数参数、注释、语句结束符、cin/cout)
  6. Servlet运行原理以及生命周期
  7. Java fork join ForkJoinPool 用法例子
  8. Linux下JDK环境的配置
  9. hadoop上的pageRank算法
  10. C++标准库简介(转)