线性表
线性表的定义
线性表是一种基本的数据结构,它是由n个数据元素a[1]、a[2]、…、a[n]组成的有限序列。线性表中的数据元素之间呈现出一对一的顺序关系,每个元素都有一个前驱和后继元素,除了第一个元素没有前驱,最后一个元素没有后继,它可以用顺序存储结构和链式存储结构来实现。
举一个例子,一个班级的学生名单可以看作是一个线性表,其中每一个学生都是一个数据元素,学生名单中的数据元素之间存在一定的顺序关系,每个学生都有一个前一个和后一个学生,除了第一个学生没有前一个学生,最后一个学生没有后一个学生。
如果使用顺常存情结构来实现学生名单,那么每个学生的信息将被存储在一个数组中,如里使用链式存诸结构,可以将每个学生的信息存储在一个链表的节点中。无论是使用数组还是链表,都可以对学生名单进行插入、删除、查找和遍历等常见的操作,这些操作都是线性表的基本操作。
线性表作为一种基本的数据结构类型,在计算机存储器中的表示一般有两种形式,一种是顺序存储,一种是链式存储。
顺序表
1、顺序表的定义
线性表的顺序存储又叫顺序表。
线性表的顺序存储是指将数据元素按照其逻辑顺序依次存储在一组连续的物理位置上,通过数组来实现。顺序存储结构以连续的存储单元顺序存放线性表的元素,可以直接访问元素,查找和遍历较快,但插入和删除操作需要移动大量元素,效率较低
线性表的顺序存储可以用一维数组来实现。数组的下标从0开始,表示元素在数组中的位置。具体实现步骤如下:
1.定义一个数组,用来存储线性表中的元素,数组大小为线性表的长度;
2.将线性表中的元素按照逻辑顺序存储在数组中,即将第i个元素存储在数组中下标为i-1的位置上:
3,可以通过下标访问具体的元素,数组下标从0开始,第i个元素的下标为i-1;
4.可以通过数组下标修改或者查询元素的值;
5.插入元素时,需要将插入点之后的元素依次后移,然后在插入点处插入新元素;
6.删除元素时,需要将删除点之后的元素依次前移,然后删除最后一个元素,
顺序表具有随机存取的优点,可以直接按照下标访问任意位置的元素,但是因为删除或插入操作时需要称动大量元素,所以其插入和删除操作效率较低。
2、顺序表的创建
定义一个顺序表代码如下:
#define MAX_SIZE 100 定义顺序表的最大长度
typedef struct{
int data[MAX_SIZE];存储顺序表的数组
int length; 顺序表当前的长度
}sqlist_t 顺序表名字
创建一个顺序表如下:
sqlist_t *sqlist_init(){
sqlist_t *list=malloc(sizeof(sqlist_t));链表的大小
list->length=0; 初始化长度为0
return list; 返回顺序表的地址
}
3、顺序表的插入
插入元素时,需要将插入点之后的元素依次后移,然后在插入点处插入新元素例如,在{1,2,3,4,5} 的第3个位置上插入元素 6,实现过程如下:
1.遍历至顺序表存储第 3个数据元素的位置,如图所示
2.将元素 3 以及后续元素 4 和 5 整体向后移动一个位置,如图所示
3、将新元素 6 放入腾出的位置,如图所示:
代码实现如下
循环方向:i 从 length-1 递减到 index(从后往前)。
数据移动方向:data[i] → data[i+1](向后移动)。
最终效果:index 位置空出,可以安全插入新元素。
index表示要在顺序表插入的位置
int sqlist_insert(sqlist_t*list,int index,int value){
if(list==NULL||index<0||index>list->length){
return -1;
}
for(int i=list->length-1;i>=index;i--){
list->data[i+1]= list->data[i];
}
list->data[index]=value;
list->length++; 真实长度加1
return 0
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_SIZE 50
typedef struct
{
int data[MAX_SIZE];
int length; // 当前元素个数
} sqlist_t;
sqlist_t *list_init(void);
int list_insert(sqlist_t *list, int index, int value);
int main()
{
sqlist_t *L = list_init();
// 通过 list_insert 插入数据(自动更新 length)
for (int i = 0; i < 5; i++) {
list_insert(L, i, i + 1); // 在位置 i 插入值 i+1
}
printf("插入前:");
for (int i = 0; i < L->length; i++) { // 用 length 控制遍历
printf("%d ", L->data[i]);
}
printf("\n");
// 在位置 2 插入 10
list_insert(L, 2, 10);
printf("插入后:");
for (int i = 0; i < L->length; i++) {
printf("%d ", L->data[i]);
}
printf("\n");
return 0;
}
sqlist_t *list_init()
{
sqlist_t *list = (sqlist_t *)malloc(sizeof(sqlist_t));
list->length = 0;
memset(list->data, 0, sizeof(list->data)); // 精确初始化数据部分
return list;
}
// 顺序表的插入
int list_insert(sqlist_t *list, int index, int value)
{
if (index < 0 || index > list->length || list->length >= MAX_SIZE)
{
return -1;
}
for (int i = list->length - 1; i >= index; i--)
{
list->data[i + 1] = list->data[i];
}
list->data[index] = value;
list->length++;
return 1;
}
运行结果
4、顺序表的删除
删除元素时,需要将删除点之后的元素依次前移,然后删除最后一个元素。例如,从 {1,2,3,4,5} 中删除元素 3 的过程如图所示:
循环方向:i 从 index递增到 list->length-1(从后往前)。
数据移动方向:data[i+1]--->data[i](向前移动)。
最终效果:index 位置覆盖,可以安全删除元素。
int sqlist_delete(sqlist_t*list,int index,int*value){
if (list == NULL || index < 0 || index >= list->length) {
return -1; // 无效参数(索引越界或空指针)
}
if (value != NULL) {
*value = list->data[index];
}
// 将 index 之后的元素前移一位
for (int i = index; i < list->length - 1; i++) {
list->data[i] = list->data[i + 1];
}
// 更新顺序表长度
list->length--;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_SIZE 50
typedef struct
{
int data[MAX_SIZE];
int length; // 当前元素个数
} sqlist_t;
sqlist_t *list_init(void);
int list_insert(sqlist_t *list, int index, int value);
int list_delete(sqlist_t *list, int index, int *value);
int main()
{
sqlist_t *L = list_init();
// 通过 list_insert 插入数据(自动更新 length)
for (int i = 0; i < 5; i++)
{
list_insert(L, i, i + 1); // 在位置 i 插入值 i+1
}
printf("删除前:");
for (int i = 0; i < L->length; i++)
{ // 用 length 控制遍历
printf("%d ", L->data[i]);
}
printf("\n");
// 在位置 2 插入 10
//list_insert(L, 2, 10);
int b;
list_delete(L,2,&b);
printf("删除后:");
for (int i = 0; i < L->length; i++)
{
printf("%d ", L->data[i]);
}
printf("\n");
return 0;
}
sqlist_t *list_init()
{
sqlist_t *list = (sqlist_t *)malloc(sizeof(sqlist_t));
list->length = 0;
memset(list->data, 0, sizeof(list->data)); // 精确初始化数据部分
return list;
}
// 顺序表的插入
int list_insert(sqlist_t *list, int index, int value)
{
if (index < 0 || index > list->length || list->length >= MAX_SIZE)
{
return -1;
}
for (int i = list->length - 1; i >= index; i--)
{
list->data[i + 1] = list->data[i];
}
list->data[index] = value;
list->length++;
return 1;
}
int list_delete(sqlist_t *list, int index, int *value)
{
if (index < 0 || index > list->length || list->length >= MAX_SIZE)
{
return -1;
}
if (value != NULL)
{
*value = list->data[index];
}
for (int i = index; i < list->length - 1; i++)
{
list->data[i] = list->data[i + 1];
}
list->length--;
}
5、顺序表的逆序
顺序表的逆序操作可以通过交换表头和表尾、表第二个元素和倒数第二个元素、第三个元素和倒数第三个元素……以此类推,直到两个指针相遇为止。
int sqlist_reverse(sqlist_t*list){
int temp;
for(int i=0;i<list->length/2;i++){
temp=list->data[i];
list->data[i]=list->data[list->length-1-i];
list->data[list->length-1-i]=temp;
}
return 0;
}
6、顺序表的修改
int sqlist_modify(sqlist_t*list,int index,int value){
if (list == NULL || index < 0 || index >= list->length) {
return -1; // 无效参数(索引越界或空指针)
}
list->data[index] =value;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_SIZE 50
typedef struct
{
int data[MAX_SIZE];
int length; // 当前元素个数
} sqlist_t;
sqlist_t *list_init(void);
int list_insert(sqlist_t *list, int index, int value);
int sqlist_modify(sqlist_t*list,int index,int value);
int main()
{
sqlist_t *L = list_init();
// 通过 list_insert 插入数据(自动更新 length)
for (int i = 0; i < 5; i++)
{
list_insert(L, i, i + 1); // 在位置 i 插入值 i+1
}
printf("修改前:");
for (int i = 0; i < L->length; i++)
{ // 用 length 控制遍历
printf("%d ", L->data[i]);
}
printf("\n");
sqlist_modify(L,2,10);
printf("修改后:");
for (int i = 0; i < L->length; i++)
{
printf("%d ", L->data[i]);
}
printf("\n");
return 0;
}
sqlist_t *list_init()
{
sqlist_t *list = (sqlist_t *)malloc(sizeof(sqlist_t));
list->length = 0;
memset(list->data, 0, sizeof(list->data)); // 精确初始化数据部分
return list;
}
// 顺序表的插入
int list_insert(sqlist_t *list, int index, int value)
{
if (index < 0 || index > list->length || list->length >= MAX_SIZE)
{
return -1;
}
for (int i = list->length - 1; i >= index; i--)
{
list->data[i + 1] = list->data[i];
}
list->data[index] = value;
list->length++;
return 1;
}
int sqlist_modify(sqlist_t*list,int index,int value){
if (list == NULL || index < 0 || index >= list->length) {
return -1; // 无效参数(索引越界或空指针)
}
list->data[index] =value;
}
7、顺序表的查找
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_SIZE 50
typedef struct
{
int data[MAX_SIZE];
int length; // 当前元素个数
} sqlist_t;
sqlist_t *list_init(void);
int sqlist_find(sqlist_t *list, int value);
int main()
{
sqlist_t *L = list_init();
// 通过 list_insert 插入数据(自动更新 length)
for (int i = 0; i < 5; i++)
{
list_insert(L, i, i + 1); // 在位置 i 插入值 i+1
}
printf("查找前:");
for (int i = 0; i < L->length; i++)
{ // 用 length 控制遍历
printf("%d ", L->data[i]);
}
printf("\n");
sqlist_find(L,4);
printf("\n");
return 0;
}
sqlist_t *list_init()
{
sqlist_t *list = (sqlist_t *)malloc(sizeof(sqlist_t));
list->length = 0;
memset(list->data, 0, sizeof(list->data)); // 精确初始化数据部分
return list;
}
int sqlist_find(sqlist_t *list, int value)
{
if (list == NULL)
{
return -1; // 无效参数(索引越界或空指针)
}
for (int i = 0; i < list->length-1; i++)
{
if (list->data[i] == value)
{
printf("该元素的索引为%d", i);
}
}
}
单链表(有头)
h->next = p; 这行代码的核心作用是 将新节点p正式 "接入" 到链表中,让它成为链表的一部分。
p->next=NULL不会报错,NULL 在C语言中是一个合法的指针值(通常是 (void *)0),表示“空指针”。
对 NULL 赋值是完全安全的,不会导致程序崩溃。
只有对 NULL 解引用(如 *p 或 p->data)才会导致段错误
node是一个完整的节点,包含:
数据域:data = 10
指针域:next = NULL
我们可以用一个生活中的例子来理解:
假设原来的链表是这样的:
h(头节点) → a(第一个节点) → b(第二个节点) → ...
当我们创建了新节点p后,p->next = h->next;(相当于p节点接入h结点下一个节点,但是p的头结点没有接上)(接上了尾巴头没有接)
h->next 原本指向 a。执行 p->next = h->next; 后,p 的 next 指针指向 a此时关系是:
h → a → b → ...
p → a → b → ...
但此时p还没有和h建立联系,它只是单独 "抓着"a,并没有真正加入链表。
而h->next = p; 这一步,就是让头节点h的 "下一个指向" 从原来的a改成p,此时关系变成:
h → p → a → b → ...
这样p才真正成为了h的下一个节点,正式加入了链表。
如果不写h->next = p;
h的下一个节点仍然是a
新节点p虽然抓着a,但它和h没有任何联系
从h开始遍历链表时,永远找不到p(相当于p游离在链表外)
最终会导致内存泄漏(p占用的内存无法被访问和释放)
1、单链表的定义
线性表的链式存储结构,即链表!
单链表可以形象比喻为一列火车车厢,每个车厢代表一个节点,车厢里的货物代表节点的值。
每个车厢都有一个挂钩,代表指向下一个车厢的指针。
在单链表中,头节点是车头,尾节点是最后一个车厢。要访问某个节点,需要从头节点开始一辆一辆地往下找,直到找到目标节点。
要插入或删除某个节点,需要将它的前一个节点的指针指向它之后的节点,或将它的前一个节点的指针指向它之前的节点。
像火车一样,单链表的元素是有序的,每个节点只能访问它前一个和后一个节点,因此操作的自由度和效率相对较低。
单链表是一种基本的数据结构,它由多个节点组成,每个节点包含一个值和一个指向下一个节点的指针。单链表的头节点是链表的起点
每个节点通过指针连接在一起。
单链表的优点是插入和删除元素的效率很高,因为只需要改变指针的指向即可,不需要像数组一样移动元素。但是,访问单链表中的元素效率较低,因为需要遍历整个链表。
以下是单链表的基本操作:
1.创建链表:创建一个空链表,即创建头节点并将头节点的指针指向NULL
2.插入元素:在链表中插入一个元素,可以在链表的任意位置插入一个节点。
3.删除元素:从链表中删除一个元素,需要找到要删除元素的前一个节点。
4.遍历链表:遍历整个链表,可以依次访问链表中的所有节点。
2、单链表的创建
单链表节点定义
typedef struct node{
int data; 结构体中一个成员
struct node*next; 结构体指针,和本结构体类型一样的结构体指针
}linklist_t; linklist_t 等价于struct node
单链表初始化
linklist_t*linklist_init(void){
linklist_t *link=(linklist_t*)malloc(sizeof(linklist_t));
link->data=0;
link->next=NULL;
return link;
}
3、单链表的插入
步骤1和步骤2执行后头结点不再指向节点1,而是指向节点p。
int linklist_insert(linklist_t*h,int value){
linklist_t *p=(linklist_t*)malloc(sizeof(linklist_t));
p->data=value;
p->next=h->next;
h->next=p;
return 0;
}
h->next = p
将头节点 h 的 next 指针改为指向 新节点 p。
此时 p 正式被插入到链表中,成为新的第一个节点(头节点的后继)。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct node{
int data;
struct node *next;
}linklist_t ;
linklist_t *link_init();
int linklist_insert(linklist_t*h,int value);
void linklist_print(linklist_t*h);
int main(int argc, char const *argv[])
{
linklist_t*link=link_init();
for (int i = 0; i <5; i++)
{
linklist_insert(link,i+1);
}
linklist_print(link);
return 0;
}
linklist_t *link_init(){
linklist_t*p=(linklist_t*)malloc(sizeof(linklist_t));
p->data=0;
p->next=NULL;
return p;
}
//这里的形参是原来的链表
int linklist_insert(linklist_t*h,int value){
linklist_t*p=(linklist_t*)malloc(sizeof(linklist_t));
p->next=h->next;
p->data=value;
h->next=p;//更新原来链表的头节点p此时是新的节点
return 0;
}
void linklist_print(linklist_t*h){
linklist_t*p=h->next;
while (p!=NULL)
{
printf("%d ",p->data);
p=p->next;//元素地址的移动
}
printf("\n");
}
4、单链表的删除
正常删除链表中的1个节点,需要把要删除链表的前一个节点和后一个节点连接。
需要找到要删除节点的前一个节点才可以删除。
// 链表的删除
int link_delete(link_t *h, int value)
{
link_t *p = h;
link_t *q;
while (p->next != NULL)
{
if (p->next->data == value)
{
q = p->next;
p->next = q->next;
free(q);
return 0;
}
p = p->next;
}
return -1;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义链表
typedef struct node
{
int data;
struct node *next;
} link_t;
link_t *link_init();
int link_delete(link_t *h, int value);
int link_insert(link_t *h, int value);
int main()
{
link_t *L = link_init();
for (int i = 0; i < 5; i++)
{
link_insert(L, i + 1);
}
link_delete(L, 4);
while (L->next != NULL)
{
L = L->next;
printf("%d ", L->data);
}
return 0;
}
// 链表的初始化
link_t *link_init()
{
link_t *link = (link_t *)malloc(sizeof(link_t));
link->data = 0;
link->next = NULL;
return link;
}
// 链表的插入
int link_insert(link_t *h, int value)
{
link_t *p = (link_t *)malloc(sizeof(link_t));
p->data = value;
p->next = h->next;
h->next = p;
return 0;
}
// 链表的删除
int link_delete(link_t *h, int value)
{
link_t *p = h;
link_t *q;
while (p->next != NULL)
{
if (p->next->data == value)
{
q = p->next;
p->next = q->next;
free(q);
return 0;
}
p = p->next;
}
return -1;
}
5、单链表的逆序
1.p 指向头节点的下一个节点。
2.把表头设置为空头
3.把p指向的链表再重新进行头部插入一次,这样就可以实现逆序
int linklist_reverse(linklist_t *h)
{
linklist_t *p = h->next;
linklist_t = *q;
h->next = NULL;
while (p != NULL)
{
q = p->next;
p->next = h->next;
h->next = p;
p = p->next;
}
retrun 0;
}
6、单链表的修改
// 链表的修改
int link_modify(link_t *h, int old,int new)
{
link_t *p = h;//头结点也就是第一个节点
while (p->next != NULL)
{
if (p->data==old)
{
p->data=new;
return 0;
}
p = p->next;
}
return -1;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义链表
typedef struct node
{
int data;
struct node *next;
} link_t;
link_t *link_init();
int link_insert(link_t *h, int value);
int link_modify(link_t *h, int old,int new);
int main()
{
link_t *L = link_init();
for (int i = 0; i < 5; i++)
{
link_insert(L, i + 1);
}
link_modify(L,4,40);
while (L->next != NULL)
{
L = L->next;
printf("%d ", L->data);
}
return 0;
}
// 链表的初始化
link_t *link_init()
{
link_t *link = (link_t *)malloc(sizeof(link_t));
link->data = 0;
link->next = NULL;
return link;
}
// 链表的插入
int link_insert(link_t *h, int value)
{
link_t *p = (link_t *)malloc(sizeof(link_t));
p->data = value;
p->next = h->next;
h->next = p;
return 0;
}
// 链表的修改
int link_modify(link_t *h, int old,int new)
{
link_t *p = h;//头结点也就是第一个节点
while (p->next != NULL)
{
if (p->data==old)
{
p->data=new;
return 0;
}
p = p->next;
}
return -1;
}
运行结果
7、单链表的查找
// 链表的查找
// 链表的查找
int link_find(link_t *h, int value)
{
link_t *p = h;//头结点也就是第一个节点
while (p->next != NULL)
{
if (p->data==value)
{
return 1;
}
p = p->next;
}
return -1;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义链表
typedef struct node
{
int data;
struct node *next;
} link_t;
link_t *link_init();
int link_insert(link_t *h, int value);
int link_find(link_t *h, int value);
int main()
{
link_t *L = link_init();
for (int i = 0; i < 5; i++)
{
link_insert(L, i + 1);
}
int res= link_find(L,4);
printf("找到了返回为%d",res);
return 0;
}
// 链表的初始化
link_t *link_init()
{
link_t *link = (link_t *)malloc(sizeof(link_t));
link->data = 0;
link->next = NULL;
return link;
}
// 链表的插入
int link_insert(link_t *h, int value)
{
link_t *p = (link_t *)malloc(sizeof(link_t));
p->data = value;
p->next = h->next;
h->next = p;
return 0;
}
// 链表的查找
int link_find(link_t *h, int value)
{
link_t *p = h;//头结点也就是第一个节点
while (p->next != NULL)
{
if (p->data==value)
{
return 1;
}
p = p->next;
}
return -1;
}
单链表与顺序表优缺点
单链表结构和顺序存储结构都是常见的数据结构,它们各有优缺点。
1.单链表结构的优点
1.在插入和删除节点时,只需要改变指针的指向,时间复杂度为0(1)。
2.单链表可以动态地分配内存空间,不受固定大小的限制,能够灵活应用。
3.单链表可以比较容易地实现链表的翻转、查找中间节点等操作。
2.单链表结构的缺点
1.单链表不支持随机访问,查找节点需要遍历整个链表,时间复杂度为0(n)。
2.单链表的每个节点需要额外的指针域来存储下一个节点的地址,占用额外的存储空间。
3.单链表在访问某个节点之前,必须先访问它的前一个节点,不便于一些操作的实现。
3.顺序存储结构的优点
1.顺序存储结构支持随机访问,可以通过下标直接访问某个元素,时间复杂度为O(1)。
2.顺序存储结构不需要额外的指针域存储节点关系,占用空间较小。
3.顺序存储结构对于一些简单的线性表操作,如插入、删除、排序等,实现比较简单。
4.顺序存储结构的缺点
1.在插入和删除元素时,需要移动其他元素,时间复杂度为O(n)。
2.顺序存储结构的大小是固定的,不能动态调整,不适用于动态变化的应用。
3.当需要改变存储空间时,需要进行大量的数据搬移,效率较低。
因此,在实际应用中,需要根据具体的需求选择不同的数据结构进行使用.
单链表(无头)
1、单链表的创建
单链表节点定义
typedef struct node
{
int data;
struct node *next;
} link_list;
单链表初始化
创建节点
link_list *create_node(int data)
{
创建一个新节点
link_list *L = (link_list *)malloc(sizeof(link_list));
L->data = data;
L->next = NULL;
return L;
}
// 打印链表
void print_list(link_list *h)
{
link_list *p = h;
while (p != NULL)
{
printf("%d -> ", p->data);
p = p->next;
}
printf("NULL\n");
}
int main()
{
link_list*L=create_node(10);
print_list(L);
return 0;
}
2、单链表的插入
头插入法
头插法(成功返回1,失败返回-1)
int create_insert(link_list **h, int data)
{
创建一个新节点
link_list *newNode = (link_list *)malloc(sizeof(link_list));
if (!newNode)
{
printf("内存分配失败");
return -1;
}
newNode->data = data;
newNode->next = *h; 新节点指向第一个节点,也就是头结点
*h = newNode; 更新链表节点
return 1;
}
// 打印链表
void print_list(link_list *h)
{
link_list *p = h;
while (p != NULL)
{
printf("%d -> ", p->data);
p = p->next;
}
printf("NULL\n");
}
int main()
{
link_list*L=create_node(10);
create_insert(&L,20);
// create_insert(&L,30);
// create_Tail_insert(&L,40);
print_list(L);
return 0;
}
尾插入法
// 尾插法(成功返回1,失败返回-1)
int create_Tail_insert(link_list **h, int data)
{
link_list *newNode = (link_list *)malloc(sizeof(link_list));
if (!newNode)
{
printf("内存分配失败");
return -1;
}
newNode->data=data;
link_list *p = *h;
// 原来链表为空的
if (!p)
{
p = newNode; // 更新链表
return 1;
}
// 定义临时变量用于接受最后的元素
link_list *current = *h;
while (current->next != NULL)
{
current = current->next;
}
// 尾插入法
current->next = newNode;
}
// 打印链表
void print_list(link_list *h)
{
link_list *p = h;
while (p != NULL)
{
printf("%d -> ", p->data);
p = p->next;
}
printf("NULL\n");
}
int main()
{
link_list *L = create_node(10);
create_insert(&L, 20);
create_Tail_insert(&L, 30);
print_list(L);
return 0;
}
任意位置插入法
// 任意位置插入法(修正版)
int create_Rinsert(link_list **h, int target_value, int new_data)
{
if (!h) return -1;
link_list *newNode = (link_list *)malloc(sizeof(link_list));
if (!newNode) {
printf("内存分配失败");
return -1;
}
newNode->data = new_data;
newNode->next = NULL;
// 处理空链表
if (*h == NULL) {
*h = newNode;
return 1;
}
// 检查头节点
if ((*h)->data == target_value) {
newNode->next = *h;
*h = newNode;
return 1;
}
// 遍历链表
link_list *p = *h;
link_list *q = NULL;
while (p != NULL) {
if (p->data == target_value) {
newNode->next = p;
if (q) q->next = newNode;
return 1;
}
q = p; 指针循环尾随,p->next,一直循环q=p q一直是p的值,但是p的值一直在往后面跑
p = p->next;
}
// 未找到目标值,插入末尾
if (q) q->next = newNode;
return 1;
}
// 打印链表
void print_list(link_list *h)
{
link_list *p = h;
while (p != NULL)
{
printf("%d -> ", p->data);
p = p->next;
}
printf("NULL\n");
}
int main()
{
link_list *L = create_node(10);
create_insert(&L, 20);
create_Tail_insert(&L, 30);
create_Rinsert(&L,30,45);
print_list(L);
return 0;
}
3、单链表的删除
int link_delete(link_list **h, int data)
{
if (*h == NULL)
return -1; // 空链表直接返回
link_list *p = *h, *q = NULL;
// 删除头节点
if (p->data == data)
{
*h = p->next;//更新头指针,下一个节点变成新的头指针
free(p);
return 1;
}
// 删除非头节点(指针尾随法)
while (p != NULL)
{
if (p->data == data)
{
if (q != NULL)
{ // 确保q有效
q->next = p->next;
free(p); // 释放目标节点
return 1;
}
}
q = p; // q记录前驱节点
p = p->next; // p继续遍历
}
return -1; // 未找到目标数据
}
int main()
{
link_list *L = create_node(10);
create_insert(&L, 20);
create_Tail_insert(&L, 30);
create_Rinsert(&L, 30, 45);
printf("打印之前的数据为");
print_list(L);
link_delete(&L, 45);
printf("打印之后的数据为");
print_list(L);
return 0;
}
4、单链表的修改
// 链表的修改
int link_modify(link_list *h, int oldData,int newData)
{
link_list *p = h;
if (!p)
{
printf("链表内容为空");
return -1;
}
while (p!=NULL)
{
//从链表中找到旧数据
if (p->data==oldData)
{
p->data=newData;
return 1;
}
p= p->next;
}
}
// 打印链表
void print_list(link_list *h)
{
link_list *p = h;
while (p != NULL)
{
printf("%d -> ", p->data);
p = p->next;
}
printf("NULL\n");
}
int main()
{
link_list *L = create_node(10);
create_insert(&L, 20);
create_Tail_insert(&L, 30);
create_Rinsert(&L, 30, 45);
printf("打印之前的数据为");
print_list(L);
//link_delete(&L, 45);
link_modify(L,45,450);
printf("打印之后的数据为");
print_list(L);
return 0;
}

单循环链表(有头)
1、单循环链表的定义
单循环链表是一种数据结构,它类似于单向链表,不同之处在于它的最后一个节点连接回第一个节点,形成了一个循环。这意味着在单循环链表中,每个节点都有一个指针指向下一个节点,并且最后一个节点指向第一个节点。
这样,这个链表就可以无限制地循环下去。
2、单循环链表的创建
单循环链表节点的定义
typedef struct node{
int data;
struct node*next;
}linklist_t
linklist_t*cyclist_init(void){
linklist_t *h=(linklist_t*)malloc(sizeof(linklist_t));
h->data=0;
h->next=h;
return h;
}
3、单循环链表的插入
单循环链表的头部插入是指在链表头部插入新节点的操作。插入新节点后,将其设置为链表的头节点。具体实现如下:
1.分配新节点内存。
2.如果链表为空,将新节点的 next 指向自己,
3.如果链表不为空,使用头插法插入节点即可。
int cyclist_insert(linklist_t*h,int value){
linklist_t *p=(linklist_t*)malloc(sizeof(linklist_t));
p->data=value;
p->next=h->next;
h->next=p;
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义循环链表
typedef struct node
{
int data;
struct node *next;
} cyclist_t;
cyclist_t *cyc_init();
int cyc_insert(cyclist_t *h, int value);
int cyc_print(cyclist_t *h);
int main(int argc, char const *argv[])
{
cyclist_t *L = cyc_init();
for (int i = 0; i <5; i++)
{
cyc_insert(L,i+1);
}
cyc_print(L);
return 0;
}
// 单循环链表的初始化
cyclist_t *cyc_init()
{
cyclist_t *h = malloc(sizeof(cyclist_t));
h->data = 0;
h->next = h;
return h;
}
int cyc_insert(cyclist_t *h, int value)
{
cyclist_t *p = malloc(sizeof(cyclist_t));
p->data = value;
p->next = h->next;
h->next = p;
return 1;
}
int cyc_print(cyclist_t *h)
{
cyclist_t *p = h->next;
printf("链表的内容为:");
//p==h 说明链表循环一周
//p!=h 可以遍历循环链表一圈少一次
while (p != h)
{
printf("%d ", p->data);
p = p->next;
}
if (p == h)
{
printf("%d ", p->data);
}
printf("\n");
return 1;
}
4、单循环链表的删除
循环链表删除节点时,要定位到要删除节点的前一个节点。
如果要删除的节点是头结点,必须要更新一个新的头结点,
linklist_t *cyclist_delete(linklist_t *h, int value) {
linklist_t *p = h; // p 指向头节点
linklist_t *q; // q 用于指向要删除的节点
while (p->next != h) { // 遍历链表(除了头节点)
if (p->next->data == value) { // 找到要删除的节点
q = p->next;
p->next = q->next; // 绕过 q
free(q);
return h; // 返回原头节点(未删除头节点)
}
p = p->next;
}
// 如果循环结束仍未删除,检查头节点是否是要删除的节点
if (h->data == value) { // 头节点是要删除的节点
q = h;
p->next = h->next; // 让最后一个节点指向新的头节点
free(q);
return p->next; // 返回新的头节点
}
return h; // 未找到要删除的节点,返回原头节点
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义循环链表
typedef struct node
{
int data;
struct node *next;
} cyclist_t;
cyclist_t *cyc_init();
int cyc_insert(cyclist_t *h, int value);
int cyc_print(cyclist_t *h);
int cyc_delete(cyclist_t *h, int value);
int main(int argc, char const *argv[])
{
cyclist_t *L = cyc_init();
for (int i = 0; i < 5; i++)
{
cyc_insert(L, i + 1);
}
cyc_delete(L,3);
cyc_print(L);
return 0;
}
// 单循环链表的初始化
cyclist_t *cyc_init()
{
cyclist_t *h = malloc(sizeof(cyclist_t));
h->data = 0;
h->next = h;
return h;
}
int cyc_insert(cyclist_t *h, int value)
{
cyclist_t *p = malloc(sizeof(cyclist_t));
p->data = value;
p->next = h->next;
h->next = p;
return 1;
}
int cyc_print(cyclist_t *h)
{
cyclist_t *p = h->next;
printf("链表的内容为:");
// p==h 说明链表循环一周
// p!=h 可以遍历循环链表一圈少一次
while (p != h)
{
printf("%d ", p->data);
p = p->next;
}
if (p == h)
{
printf("%d ", p->data);
}
printf("\n");
return 1;
}
int cyc_delete(cyclist_t *h, int value)
{
cyclist_t *p = h; // p 指向头节点
cyclist_t *q; // q 用于指向要删除的节点
while (p->next != h)
{ // 遍历链表(除了头节点)
if (p->next->data == value)
{ // 找到要删除的节点
q = p->next;
p->next = q->next; // 绕过 q
free(q);
return h; // 返回原头节点(未删除头节点)
}
p = p->next;
}
// 如果循环结束仍未删除,检查头节点是否是要删除的节点
if (h->data == value)
{ // 头节点是要删除的节点
q = h;
p->next = h->next; // 让最后一个节点指向新的头节点
free(q);
return p->next; // 返回新的头节点
}
return h; // 未找到要删除的节点,返回原头节点
}
5、单循环链表的逆序
cyclist_t *cyc_reverse(cyclist_t *h)
{
cyclist_t *p = h->next; // 让p 指向第一个节点
h->next = h; // 把头设置为一个空节点
cyclist_t *q;
// 把p指向的节点循环插入到h的后面
while (p != h)
{
q = p->next; // 保存下一个要插入的节点
p->next = h->next; // 接链表的第一步
h->next = p; // 接链表的第二步
p = q; // 把p和q保持一致
}
/*
处理最后一个节点,把头结点往后移动一个位置
要找到头节点的前一个位置
*/
p = h; // 让p指向h
while (p->next != h)
{
p = p->next;
}
if (p->next == h)//定位到头结点的前一个节点
{
return p;
}
return h;
}
6、单循环链表的修改
int cyc_modify(cyclist_t *h, int old, int new)
{
cyclist_t *p = h;
// 修改的不是头节点
while (p->next != h)
{
if (p->data == old)
{
p->data = new;
return 1;
}
p = p->next;
}
// 修改的是头节点
if (p == h)
{
if (p->data == old)
{
p->data = new;
return 1;
}
}
return -1;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义循环链表
typedef struct node
{
int data;
struct node *next;
} cyclist_t;
cyclist_t *cyc_init();
int cyc_insert(cyclist_t *h, int value);
int cyc_print(cyclist_t *h);
int cyc_modify(cyclist_t *h, int old, int new);
int main(int argc, char const *argv[])
{
cyclist_t *L = cyc_init();
for (int i = 0; i < 5; i++)
{
cyc_insert(L, i + 1);
}
cyc_modify(L,5,500);
cyc_print(L);
return 0;
}
// 单循环链表的初始化
cyclist_t *cyc_init()
{
cyclist_t *h = malloc(sizeof(cyclist_t));
h->data = 0;
h->next = h;
return h;
}
int cyc_insert(cyclist_t *h, int value)
{
cyclist_t *p = malloc(sizeof(cyclist_t));
p->data = value;
p->next = h->next;
h->next = p;
return 1;
}
int cyc_print(cyclist_t *h)
{
cyclist_t *p = h->next;
printf("链表的内容为:");
// p==h 说明链表循环一周
// p!=h 可以遍历循环链表一圈少一次
while (p != h)
{
printf("%d ", p->data);
p = p->next;
}
if (p == h)
{
printf("%d ", p->data);
}
printf("\n");
return 1;
}
int cyc_delete(cyclist_t *h, int value)
{
cyclist_t *p = h; // p 指向头节点
cyclist_t *q; // q 用于指向要删除的节点
while (p->next != h)
{ // 遍历链表(除了头节点)
if (p->next->data == value)
{ // 找到要删除的节点
q = p->next;
p->next = q->next; // 绕过 q
free(q);
return h; // 返回原头节点(未删除头节点)
}
p = p->next;
}
// 如果循环结束仍未删除,检查头节点是否是要删除的节点
if (h->data == value)
{ // 头节点是要删除的节点
q = h;
p->next = h->next; // 让最后一个节点指向新的头节点
free(q);
return p->next; // 返回新的头节点
}
return h; // 未找到要删除的节点,返回原头节点
}
int cyc_modify(cyclist_t *h, int old, int new)
{
cyclist_t *p = h;
// 修改的不是头节点
while (p->next != h)
{
if (p->data == old)
{
p->data = new;
return 1;
}
p = p->next;
}
// 修改的是头节点
if (p == h)
{
if (p->data == old)
{
p->data = new;
return 1;
}
}
return -1;
}
7、单循环链表的查找
int cyc_find(cyclist_t *h, int value)
{
cyclist_t *p = h;
// 查找的不是头节点
while (p->next != h)
{
if (p->data == value)
{
printf("找到了");
return 1;
}
p = p->next;
}
// 修改的是头节点
if (p == h)
{
if (p->data == value)
{
printf("找到了");
return 1;
}
}
return -1;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义循环链表
typedef struct node
{
int data;
struct node *next;
} cyclist_t;
cyclist_t *cyc_init();
int cyc_insert(cyclist_t *h, int value);
int cyc_print(cyclist_t *h);
int cyc_modify(cyclist_t *h, int old, int new);
int cyc_find(cyclist_t *h, int value);
int main(int argc, char const *argv[])
{
cyclist_t *L = cyc_init();
for (int i = 0; i < 5; i++)
{
cyc_insert(L, i + 1);
}
cyc_modify(L,5,500);
cyc_print(L);
cyc_find(L,500);
return 0;
}
// 单循环链表的初始化
cyclist_t *cyc_init()
{
cyclist_t *h = malloc(sizeof(cyclist_t));
h->data = 0;
h->next = h;
return h;
}
int cyc_insert(cyclist_t *h, int value)
{
cyclist_t *p = malloc(sizeof(cyclist_t));
p->data = value;
p->next = h->next;
h->next = p;
return 1;
}
int cyc_print(cyclist_t *h)
{
cyclist_t *p = h->next;
printf("链表的内容为:");
// p==h 说明链表循环一周
// p!=h 可以遍历循环链表一圈少一次
while (p != h)
{
printf("%d ", p->data);
p = p->next;
}
if (p == h)
{
printf("%d ", p->data);
}
printf("\n");
return 1;
}
int cyc_delete(cyclist_t *h, int value)
{
cyclist_t *p = h; // p 指向头节点
cyclist_t *q; // q 用于指向要删除的节点
while (p->next != h)
{ // 遍历链表(除了头节点)
if (p->next->data == value)
{ // 找到要删除的节点
q = p->next;
p->next = q->next; // 绕过 q
free(q);
return h; // 返回原头节点(未删除头节点)
}
p = p->next;
}
// 如果循环结束仍未删除,检查头节点是否是要删除的节点
if (h->data == value)
{ // 头节点是要删除的节点
q = h;
p->next = h->next; // 让最后一个节点指向新的头节点
free(q);
return p->next; // 返回新的头节点
}
return h; // 未找到要删除的节点,返回原头节点
}
int cyc_modify(cyclist_t *h, int old, int new)
{
cyclist_t *p = h;
// 修改的不是头节点
while (p->next != h)
{
if (p->data == old)
{
p->data = new;
return 1;
}
p = p->next;
}
// 修改的是头节点
if (p == h)
{
if (p->data == old)
{
p->data = new;
return 1;
}
}
return -1;
}
int cyc_find(cyclist_t *h, int value)
{
cyclist_t *p = h;
// 查找的不是头节点
while (p->next != h)
{
if (p->data == value)
{
printf("找到了");
return 1;
}
p = p->next;
}
// 修改的是头节点
if (p == h)
{
if (p->data == value)
{
printf("找到了");
return 1;
}
}
return -1;
}
双链表(有头)
1、双链表的定义
双向链表是一种数据结构,它由一系列节点组成,每个节点都包含了一个指向前继节点和后继节点的指针,这种链表可以从前往后遍历,也可以从后往前遍历。
双向链表的优点是可以方便地在链表中任意位置插入或删除节点,并且可以有效地支持双向遍历。相对于单向链表,双向链表的一个缺点是它使用了更多的指针空间,因此需要更多的内存,双向链表可以被形象地比喻成一列双向连接的车厢,每个车厢都可以向前或向后连接其他车厢,而且每个车厢都知道它前面和后面的车厢是哪些。
2、双链表的创建
双链表的节点的定义
typedef struct node{
int data;
struct node*prior;//先前的
struct node*next;
}linklist_t;
双链表的创建
linklist_t*double_linklist_init(){
linklist_t*h=( linklist_t*)malloc(sizeof(linklist_t));
h->data=0;
h->next=NULL;
h->prior=NULL;
return h;
}
3、双链表的插入
双向链表的插入操作包括在链表的头部、尾部和中间插入元素。以下是双向链表的插入操作的步骤
1.p节点指向h的下一个节点。
2.h的next保存p节点。
3.节点p的next的prior保存p节点。
4. p的prior保存h节点。
next是先前往后
prior是从后往前
int double_linklist_insert(linklist_t*h,int value){
linklist_t*p=(linklist_t*)malloc(sizeof(linklist_t))
p->data=value;
p->next=h->next;
h->next=p;
if(p->next!=NULL){
p->next->prior=p;
}
p->prior=h;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// 定义一个双向链表
typedef struct node
{
int data;
struct node *prior;
struct node *next;
} link_list;
link_list *double_linklist();
int double_linklist_insert(link_list *h, int value);
int double_linklist_print(link_list *h);
int main(int argc, char const *argv[])
{
link_list*L=double_linklist();
for (int i = 0; i <5; i++)
{
double_linklist_insert(L,i+1);
}
double_linklist_print(L);
return 0;
}
// 双向链表的初始化
link_list *double_linklist()
{
link_list *p = (link_list *)malloc(sizeof(link_list));
p->data = 0;
p->next = NULL;
p->prior = NULL;
}
// 双向链表的插入
int double_linklist_insert(link_list *h, int value)
{
link_list *p = (link_list *)malloc(sizeof(link_list));
p->data = value;
p->next = h->next;
h->next = p;
if (p->next != NULL)
{
p->next->prior = p;
}
p->prior = h;
return 1;
}
// 双向链表的遍历
int double_linklist_print(link_list *h)
{
link_list *p = h->next; // 跳过头节点
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
return 0;
}
4、双链表的删除
双向链表的删除操作包括对接前链和后链。以下是双向链表的删除操作的步骤
1.找到需要删除的节点,使用p定位到要删除的节点。
2.接后链,把p的前链和p的后链对接起来。
3.接前链,把p的后链和p的前链对接起来。
4.释放需要删除的节点。
int double_linklist_delete(link_list *h, int value)
{
link_list *p = h->next; // 头节点不参与运算
while (p->next != NULL)
{
if (p->data = value)
{
if (p->prior != NULL)
{
p->prior->next = p->next;
}
if (p->next != NULL)
{
p->next->prior = p->prior;
}
free(p);
return 1;
}
p = p->next;
}
return -1;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// 定义一个双向链表
typedef struct node
{
int data;
struct node *prior;
struct node *next;
} link_list;
link_list *double_linklist();
int double_linklist_insert(link_list *h, int value);
int double_linklist_print(link_list *h);
int double_linklist_delete(link_list *h, int value);
int main(int argc, char const *argv[])
{
link_list *L = double_linklist();
for (int i = 0; i < 5; i++)
{
double_linklist_insert(L, i + 1);
}
double_linklist_delete(L,4);
double_linklist_print(L);
return 0;
}
// 双向链表的初始化
link_list *double_linklist()
{
link_list *p = (link_list *)malloc(sizeof(link_list));
p->data = 0;
p->next = NULL;
p->prior = NULL;
}
// 双向链表的插入
int double_linklist_insert(link_list *h, int value)
{
link_list *p = (link_list *)malloc(sizeof(link_list));
p->data = value;
p->next = h->next;
h->next = p;
if (p->next != NULL)
{
p->next->prior = p;
}
p->prior = h;
return 1;
}
// 双向链表的遍历
int double_linklist_print(link_list *h)
{
link_list *p = h->next; // 跳过头节点
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
return 0;
}
// 双向链表的删除
int double_linklist_delete(link_list *h, int value)
{
link_list *p = h->next; // 头节点不参与运算
while (p->next != NULL)
{
if (p->data = value)
{
if (p->prior != NULL)
{
p->prior->next = p->next;
}
if (p->next != NULL)
{
p->next->prior = p->prior;
}
free(p);
return 1;
}
p = p->next;
}
return -1;
}
双链表无头(无头)
1、双链表的定义
typedef struct node
{
int data;
struct node *next;
struct node *prev;
} double_link;
2、双链表的创建
双向链表的初始化
double_link *double_Link(int data)
{
创建一个新的节点
double_link *L = (double_link *)malloc(sizeof(double_link));
L->data = data;
L->next = NULL;
L->prev = NULL;
return L;
}
3、双链表的插入
头插法
// 双向链表的插入-1表示插入失败,1表示插入成功 头插法
int double_link_head_insert(double_link **h, int data)
{
// 创建一个新的节点
double_link *newNode = (double_link *)malloc(sizeof(double_link));
if (!newNode)
{
printf("链表的内容为空");
return -1;
}
double_link *p = *h;
newNode->data=data;
if (!p)
{
printf("链表的内容为空");
return -1;
}
newNode->next = p; // 新节点指向第一个节点也就是头
p->prev = newNode; // 前驱指向新节点
*h = newNode; // 更新头指针
return 1;
}
// 双向链表的遍历 成功返回1,失败返回-1
int double_link_print(double_link *h)
{
double_link *L = h;
if (!L)
{
printf("链表的内容为空");
return -1;
}
printf("next遍历的结果:");
while (L != NULL)
{
printf("%d ", L->data);
L = L->next;
}
printf("prev遍历的结果:");
// while (L!=NULL)
// {
// printf("%d",L->data);
// L=L->prev;
// }
}
int main(int argc, char const *argv[])
{
double_link *L = NULL;
double_Link(&L, 10);
double_link_head_insert(&L, 20);
double_link_print(L);
return 0;
}
尾插法
// 双向链表的插入-1表示插入失败,1表示插入成功 尾插法
int double_link_tail_insert(double_link **h, int data)
{
// 创建一个新的节点
double_link *newNode = (double_link *)malloc(sizeof(double_link));
if (!newNode)
{
printf("链表的内容为空");
return -1;
}
double_link *p = *h;
newNode->data=data;
//如果是空链表
if (!p)
{
*h=newNode;
}
//如果为非空链表插入到尾节点
while (p->next!=NULL)
{
p=p->next;
}
if (p!=NULL)
{
p->next=newNode;
newNode->prev=p;
}
return 1;
}
int main(int argc, char const *argv[])
{
double_link *L = NULL;
double_Link(&L, 10);
double_link_head_insert(&L, 20);
double_link_tail_insert(&L,40);
double_link_print(L);
return 0;
}
任意位置插入法
//双向链表的任意位置插入
/**
* @brief 向链表指定位置pos插入数据(中间插法)
* @param head 待操作的链表
* @param pos 待插入数据的目标节点
* @param data 待插入的节点数据
*
* @return 成功返回0,失败返回-1
*/
int dlist_insert(double_link **head, int position, int data)
{
//创建一个新节点
double_link*newNode=__node_create(head,data);
if (!newNode)
{
printf("内存分配失败");
return -1;
}
double_link*p=*head;
//如果链表为空链表
if (!p)
{
//直接更新它的节点
*head=newNode;
return 1;
}
//如果链表不为空
while (p)
{
//找到要插入数据的位置
if (p->data==position)
{
//要插入的节点是头结点
if (p->prev==NULL)
{
newNode->next=p;新节点指向第一个节点
p->prev=newNode;第一个节点的前驱指向新节点
*head=newNode; 更新新节点
return 1;
}
要插入的节点是非头节点
else{
newNode->prev=p->prev; 连接头节点的前驱
newNode->next=p; 新节点的后继指向第一个节点
p->prev->next=newNode; 在p节点前插入newNode在newNode之前插入的数据元素的前一个节点的后继指针指向新节点
p->prev=newNode; 第一个节点的前驱指向新节点
return 1;
}
}
p=p->next;
}
return -1;
}
int main()
{
double_link *L = NULL;
double_Link(&L, 10);
double_link_head_insert(&L, 20);
double_link_tail_insert(&L, 40);// 20->10->40
dlist_insert(&L,40,50);
double_link_print(L);
return 0;
}
4、双链表的删除
// 双向链表的删除,成功返回1,失败返回-1
int double_link_delete(double_link **h, int data)
{
double_link *p = *h;
double_link *temp = NULL;
while (p)
{
if (p->data == data)
{
// 保存要删除节点的下一个节点
temp = p->next;
// 头节点
if (p->prev == NULL)
{
*h = p->next; // 更新头指针
if (*h != NULL)
{
(*h)->prev = NULL; // 新头节点的prev置空
}
}
// 中间或尾部节点
else
{
p->prev->next = p->next; 跳过中间要删除的元素。
if (p->next != NULL)
{
p->next->prev = p->prev; 跳过中间要删除的元素。
}
}
free(p); // 释放节点
return 1; // 成功删除,立即返回
}
p = p->next;
}
return -1; // 遍历完都没找到,返回-1
}