文章目录
- 文档配套视频讲解链接地址
- 第01章 数据结构的概念
- 1.1 数据结构的知识体系
- 1.2 链表
- 1. 创建头结点的内存图
- 2. 插入1节点时的内存图
- 3. 插入2节点时的内存图
- 4. 插入3节点的内存图
- 5. 实例1 链表节点的插入
- 6. 链表删除节点3
- 7. 实例2 链表的删除节点
- 8. 实例3 链表的改查逆序
- 9. 链表逆序图
- 1.3 顺序表
- 1. 实例4 顺序表的增删
- 2. 实例5 顺序表的改查逆序排序
- 1.4 单循环链表
- 1. 单循环链表的空头内存图
- 2. 单循环链表的2号节点插入时的内存图
- 3. 单循环链表的3号节点插入时的内存图
- 实例6 实现单循环链表的增删
- 实例7 实现单循环链表的改查逆序
- 1.5 双链表
- 1. 双链表的插入
- 2. 双链的删除
- 3. 实例8 双连表的增删
- 4. 双链表的逆序
- 5. 实例9 双链表的改查逆序
- 1.6 链表的应用
- 1. Joseph问题
- 2. 实例10 joseph问题
文档配套视频讲解链接地址
- 腾讯课堂视频链接地址 : 01_概述_数据结构概念1
- 腾讯课堂视频链接地址 : 02_概述_数据结构概念2
- 腾讯课堂视频链接地址 : 03_链表_链表的插入1
- 腾讯课堂视频链接地址 : 04_链表_链表的插入2
- 腾讯课堂视频链接地址 : 05_链表_链表的删除
- 腾讯课堂视频链接地址 : 06_链表_链表的改查逆序
- 腾讯课堂视频链接地址 : 07_顺序表_增删
- 腾讯课堂视频链接地址 : 08_顺序表_改查逆序排序
- 腾讯课堂视频链接地址 : 09_单循环表_增删
- 腾讯课堂视频链接地址 : 10_单循环表_改查逆序
第01章 数据结构的概念
1.1 数据结构的知识体系
1.2 链表
1. 创建头结点的内存图
2. 插入1节点时的内存图
3. 插入2节点时的内存图
4. 插入3节点的内存图
5. 实例1 链表节点的插入
-
内存的流程,请参考1,2,3,4
-
源文件
04-ds/01-linklist.c
- 源代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node
{
int data;
struct node * next;
}linklist_t;
linklist_t * create_empty_linklist_head(void)
{
linklist_t *h = (linklist_t *)malloc(sizeof(linklist_t)) ;
h->data = 0 ;
h->next = NULL ;
return h ;
}
int linklist_insert(linklist_t * h, int value)
{
linklist_t *p = (linklist_t *)malloc(sizeof(linklist_t)) ;
p->data = value ; // value = 2
p->next = h->next ; // 链表头部插入法的第1步
h->next = p ; // 链表头部插入法的第2步
return 0;
}
int linklist_show(linklist_t *h)
{
linklist_t * p = h->next ; // p 指向 3号节点
printf("链表内容为:");
while(p !=NULL)
{
printf("%d ",p->data); // 3 , 2 , 1
p = p->next ;
}
printf("\n");
return 0;
}
int main(int argc, char const *argv[])
{
linklist_t * H = create_empty_linklist_head(); // H = h ;
linklist_insert(H,1); // h = H , value = 1
linklist_insert(H,2); // h = H , value = 2
linklist_insert(H,3); // h = H , value = 3
linklist_show(H);
return 0;
}
- 运行结果
链表内容为:3 2 1
6. 链表删除节点3
7. 实例2 链表的删除节点
- 源文件
04-ds/02-linklist.c
- 源代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node
{
int data ; // 结构体中的一个成员
struct node * next; // 结构体指针, 和本结构体类型一样的结构体指针
}linklist_t; // linklist_t 等价于 struct node
// 这个函数的功能是在内存中申请了一块结构体大小的内存
// 并把申请后的地址返回
linklist_t * create_empty_linklist_head(void)
{
// malloc(sizeof(linklist_t)) 在堆区申请一个结构体大小的内存 16字节
// (linklist_t*)malloc() , 把malloc返回的地址强转成结构体指针类型的
// 就可以用一个结构体指针变量保存这个地址 , 指针运算时, 要求类型必须一直
// *h 就是一个结构体了 ,
linklist_t * h = (linklist_t*)malloc(sizeof(linklist_t)) ; //
h->data = 0 ; // (*h).data = 0 这两种用法等价
h->next = NULL;
return h; // 在堆区申请的内存, 是静态生存期, 内存不会释放, 用户使用free释放, 操作系统不会去释放
}
// h 是一个结构体指针, 保存结构体的地址
// value 要写入的值
int linklist_insert(linklist_t *h, int value)
{
linklist_t *p = (linklist_t *)malloc(sizeof(linklist_t)); // 申请一段内存
p->data = value;
p->next = h->next ; // (1)
h->next = p ; // (2)
return 0;
}
int linklist_show(linklist_t * h)
{
linklist_t * p = h->next ;
printf("链表的内容:");
while(p != NULL )
{
printf("%d ",p->data) ; // 访问节点的内容 3 2 1
p = p->next ; // 节点p 往后移动
}
printf("\n");
return 0;
}
// 正常删除链表中的1个节点, 需要把要删除链表的前一个节点和后一个节点连接
// 需要找到要删除节点的前一个节点才可以删除
int linklist_delete(linklist_t *h,int value)
{
// 链表中第一个元素的前1个节点就是链表的head
linklist_t * p = h; // p指向要删除节点的前一个节点,
linklist_t *q;
//p->next 指向链表的第1个节点 5
while(p->next != NULL) //
{
// p->next 是链表的第1个节点
if(p->next->data == value) // value=3 条件成立, 找到要删除节点的前一个节点
{
// 需要1个新的变量保存要删除的链表节点, 使用q
q = p->next ; // p->next 是要删除的节点, q去保存要删除的节点
// 连接链表
p->next = q->next ; // 链表的前1个节点 与 后1个节点连接
free(q); // free内存
// 释放完内存节点后, 要立即退出循环
break;
}
p = p->next ;
}
return 0;
}
int linklist_modify(linklist_t *h,int old, int new)
{
return 0;
}
// 找到返回真 1
// 没找到返回假 0
int linklist_search(linklist_t *h,int value)
{
}
int linklist_reverse(linklist_t *h)
{
return 0;
}
int main(int argc, char const *argv[])
{
// H 保存申请内存的起始地址
linklist_t * H = create_empty_linklist_head();
for(int i=0;i<5;i++)
{
linklist_insert(H,i+1);
}
linklist_show(H);
linklist_delete(H,3);
linklist_delete(H,5);
linklist_delete(H,1);
linklist_show(H);
return 0;
}
- 运行结果
链表的内容:5 4 3 2 1
链表的内容:4 2
8. 实例3 链表的改查逆序
- 源文件
04-ds/03-linklist.c
- 源代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node
{
int data; // 结构体中的一个成员
struct node *next; // 结构体指针, 和本结构体类型一样的结构体指针
} linklist_t; // linklist_t 等价于 struct node
// 这个函数的功能是在内存中申请了一块结构体大小的内存
// 并把申请后的地址返回
linklist_t *create_empty_linklist_head(void)
{
// malloc(sizeof(linklist_t)) 在堆区申请一个结构体大小的内存 16字节
// (linklist_t*)malloc() , 把malloc返回的地址强转成结构体指针类型的
// 就可以用一个结构体指针变量保存这个地址 , 指针运算时, 要求类型必须一直
// *h 就是一个结构体了 ,
linklist_t *h = (linklist_t *)malloc(sizeof(linklist_t)); //
h->data = 0; // (*h).data = 0 这两种用法等价
h->next = NULL;
return h; // 在堆区申请的内存, 是静态生存期, 内存不会释放, 用户使用free释放, 操作系统不会去释放
}
// h 是一个结构体指针, 保存结构体的地址
// value 要写入的值
int linklist_insert(linklist_t *h, int value)
{
linklist_t *p = (linklist_t *)malloc(sizeof(linklist_t)); // 申请一段内存
p->data = value;
p->next = h->next; // (1)
h->next = p; // (2)
return 0;
}
int linklist_show(linklist_t *h)
{
linklist_t *p = h->next;
printf("链表的内容:");
while (p != NULL)
{
printf("%d ", p->data); // 访问节点的内容 3 2 1
p = p->next; // 节点p 往后移动
}
printf("\n");
return 0;
}
// 正常删除链表中的1个节点, 需要把要删除链表的前一个节点和后一个节点连接
// 需要找到要删除节点的前一个节点才可以删除
int linklist_delete(linklist_t *h, int value)
{
// 链表中第一个元素的前1个节点就是链表的head
linklist_t *p = h; // p指向要删除节点的前一个节点,
linklist_t *q;
// p->next 指向链表的第1个节点 5
while (p->next != NULL) //
{
// p->next 是链表的第1个节点
if (p->next->data == value) // value=3 条件成立, 找到要删除节点的前一个节点
{
// 需要1个新的变量保存要删除的链表节点, 使用q
q = p->next; // p->next 是要删除的节点, q去保存要删除的节点
// 连接链表
p->next = q->next; // 链表的前1个节点 与 后1个节点连接
free(q); // free内存
// 释放完内存节点后, 要立即退出循环或者是返回
// break;
return 0;
}
p = p->next;
}
return 0;
}
int linklist_modify(linklist_t *h, int old, int new)
{
linklist_t *p = h->next; // p指向头节点的下一个节点 , 就是第一个节点
while (p != NULL)
{
if (p->data == old)
{
p->data = new;
return 0; // 修改后函数返回
}
p = p->next; // 移动节点
}
return 0;
}
// 找到返回真 1
// 没找到返回假 0
int linklist_search(linklist_t *h, int value)
{
linklist_t *p = h->next; // p指向头节点的下一个节点
while (p != NULL)
{
if (p->data == value) // 找到了
{
return 1; // 返回为真
}
p = p->next; // 移动节点
}
return 0; // 没有找到 返回假
}
// 用指针p 指向第一个节点, 然后把头设计成空头, 然后把p节点插入头的后面, 以此类推
// 即可实现链表的逆序
int linklist_reverse(linklist_t *h)
{
linklist_t * p = h->next ; // p 指向第一个节点
linklist_t * q;
h->next = NULL ; // 把表头置位空头
while(p != NULL) // p 指向的是链表的节点 , 不为空表示还有节点
{
// 把p节点插入到h节点的后面, 需要一个q节点保存p的下一个节点
q = p->next ; // q保存p的下一个节点
p->next = h->next ; // 插入节点 第1步
h->next = p ; // 插入节点的第2步
p = q; // 让p和q同时指向q
}
return 0;
}
int main(int argc, char const *argv[])
{
// H 保存申请内存的起始地址
linklist_t *H = create_empty_linklist_head();
for (int i = 0; i < 10; i++)
{
linklist_insert(H, i + 1);
}
linklist_show(H);
linklist_delete(H, 5);
linklist_delete(H, 10);
linklist_delete(H, 1);
linklist_show(H);
linklist_modify(H, 9, 99);
linklist_modify(H, 4, 44);
linklist_modify(H, 2, 22);
linklist_show(H);
if (linklist_search(H, 99))
{
printf("99 found\n");
}
else
{
printf("99 not found\n");
}
if (linklist_search(H, 10))
{
printf("10 found\n");
}
else
{
printf("10 not found\n");
}
linklist_reverse(H);
linklist_show(H);
linklist_reverse(H);
linklist_show(H);
return 0;
}
- 运行结果
链表的内容:10 9 8 7 6 5 4 3 2 1
链表的内容:9 8 7 6 4 3 2
链表的内容:99 8 7 6 44 3 22
99 found
10 not found
链表的内容:22 3 44 6 7 8 99
链表的内容:99 8 7 6 44 3 22
9. 链表逆序图
1.3 顺序表
1. 实例4 顺序表的增删
- 源文件
04-ds/04-seqlist.c
- 源代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 32
typedef struct node
{
int data[N]; // 用来存放数据的数组
int index; //数组下标的索引
} seqlist_t;
seqlist_t *create_empty_seqlist_head()
{
seqlist_t *h = (seqlist_t *)malloc(sizeof(seqlist_t));
h->index = -1; // 数组的下标从-1 开始 , 表示数组内没有元素
memset(h->data, 0, sizeof(h->data));
return h;
}
// 为空 为真
// 不为空 为假
int seqlist_is_empty(seqlist_t *h)
{
return (h->index == -1);
}
// 为空 为真
// 不为空 为假
int seqlist_is_full(seqlist_t *h)
{
return (h->index >= N - 1);
}
int seqlist_insert(seqlist_t *h, int value)
{
if (seqlist_is_full(h))
{
printf("顺序表已满\n");
return -1;
}
h->index++;
h->data[h->index] = value;
return 0;
}
int seqlist_delete(seqlist_t *h, int value)
{
if (seqlist_is_empty(h))
{
printf("顺序表已空\n");
return -1;
}
for (int i = 0; i <= h->index; i++)
{
if (h->data[i] == value) // 找到要删除的元素
{
// 后面的所有元素往前移动一个位置
for (int j = i; j < h->index; j++)
{
h->data[j] = h->data[j + 1];
}
h->index--; // 顺序表中的元素个数减1
return 0; // 直接返回
}
}
return 0;
}
int seqlist_show(seqlist_t *h)
{
printf("顺序表的内容:");
for (int i = 0; i <= h->index; i++)
{
printf("%d ", h->data[i]);
}
printf("\n");
}
int seqlist_modify(seqlist_t *h, int old, int new)
{
return 0;
}
// 找到返回真 , 真是1
// 没找到返回假, 假是0
int seqlist_search(seqlist_t *h, int value)
{
return 0; // 没找到
}
int seqlist_reverse(seqlist_t *h)
{
return 0;
}
int seqlist_sort(seqlist_t *h)
{
return 0;
}
int main(int argc, char const *argv[])
{
seqlist_t *H = create_empty_seqlist_head();
printf("顺序表的插入:");
for (int i = 0; i < 33; i++)
{
printf("%d ", i + 1);
seqlist_insert(H, i + 1);
}
seqlist_show(H);
seqlist_delete(H, 1);
seqlist_delete(H, 15);
seqlist_delete(H, 32);
seqlist_show(H);
return 0;
}
- 运行结果
顺序表的插入:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 顺序表已满
顺序表的内容:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
顺序表的内容:2 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
2. 实例5 顺序表的改查逆序排序
- 源文件
04-ds/05-seqlist.c
- 源代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 32
typedef struct node
{
int data[N]; // 用来存放数据的数组
int index; //数组下标的索引
} seqlist_t;
seqlist_t *create_empty_seqlist_head()
{
seqlist_t *h = (seqlist_t *)malloc(sizeof(seqlist_t));
h->index = -1; // 数组的下标从-1 开始 , 表示数组内没有元素
memset(h->data, 0, sizeof(h->data));
return h;
}
// 为空 为真
// 不为空 为假
int seqlist_is_empty(seqlist_t *h)
{
return (h->index == -1);
}
// 为空 为真
// 不为空 为假
int seqlist_is_full(seqlist_t *h)
{
return (h->index >= N - 1);
}
int seqlist_insert(seqlist_t *h, int value)
{
if (seqlist_is_full(h))
{
printf("顺序表已满\n");
return -1;
}
h->index++;
h->data[h->index] = value;
return 0;
}
int seqlist_delete(seqlist_t *h, int value)
{
if (seqlist_is_empty(h))
{
printf("顺序表已空\n");
return -1;
}
for (int i = 0; i <= h->index; i++)
{
if (h->data[i] == value) // 找到要删除的元素
{
// 后面的所有元素往前移动一个位置
for (int j = i; j < h->index; j++)
{
h->data[j] = h->data[j + 1];
}
h->index--; // 顺序表中的元素个数减1
return 0; // 直接返回
}
}
return 0;
}
int seqlist_show(seqlist_t *h)
{
printf("顺序表的内容:");
for (int i = 0; i <= h->index; i++)
{
printf("%d ", h->data[i]);
}
printf("\n");
}
int seqlist_modify(seqlist_t *h, int old, int new)
{
for (int i = 0; i <= h->index; i++)
{
if (h->data[i] == old) // 找到要修改的数据
{
h->data[i] = new;
return 0;
}
}
return 0;
}
// 找到返回真 , 真是1
// 没找到返回假, 假是0
int seqlist_search(seqlist_t *h, int value)
{
for (int i = 0; i <= h->index; i++)
{
if (h->data[i] == value) // 找到要修改的数据
{
return 1; // 找到
}
}
return 0; // 没找到
}
int seqlist_reverse(seqlist_t *h)
{
for (int i = 0, t = 0; i < (h->index + 1) / 2; i++)
{
t = h->data[i];
h->data[i] = h->data[h->index - i];
h->data[h->index - i] = t;
}
return 0;
}
int seqlist_sort(seqlist_t *h)
{
for (int i = 0,t=0; i < h->index; i++)
{
for (int j = 0; j < h->index - i; j++)
{
// 交换
if( h->data[j] > h->data[j+1])
{
t = h->data[j];
h->data[j] = h->data[j+1];
h->data[j+1] = t;
}
}
}
return 0;
}
int main(int argc, char const *argv[])
{
seqlist_t *H = create_empty_seqlist_head();
printf("顺序表的插入:");
for (int i = 0; i < 33; i++)
{
printf("%d ", i + 1);
seqlist_insert(H, i + 1);
}
seqlist_show(H);
seqlist_delete(H, 1);
seqlist_delete(H, 15);
seqlist_delete(H, 32);
seqlist_show(H);
seqlist_modify(H, 2, 102);
seqlist_modify(H, 31, 131);
seqlist_modify(H, 5, 55);
seqlist_modify(H, 10, 100);
seqlist_show(H);
if (seqlist_search(H, 55))
{
printf("55 found\n");
}
else
{
printf("55 not found\n");
}
if (seqlist_search(H, 31))
{
printf("31 found\n");
}
else
{
printf("31 not found\n");
}
seqlist_reverse(H);
seqlist_show(H);
seqlist_reverse(H);
seqlist_show(H);
seqlist_sort(H);
seqlist_show(H);
return 0;
}
- 运行结果
顺序表的插入:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 顺序表已满
顺序表的内容:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
顺序表的内容:2 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
顺序表的内容:102 3 4 55 6 7 8 9 100 11 12 13 14 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 131
55 found
31 not found
顺序表的内容:131 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 14 13 12 11 100 9 8 7 6 55 4 3 102
顺序表的内容:102 3 4 55 6 7 8 9 100 11 12 13 14 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 131
顺序表的内容:3 4 6 7 8 9 11 12 13 14 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 55 100 102 131
1.4 单循环链表
1. 单循环链表的空头内存图
2. 单循环链表的2号节点插入时的内存图
3. 单循环链表的3号节点插入时的内存图
实例6 实现单循环链表的增删
- 源文件
04-ds/06-cyclist.c
- 源代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node
{
int data;
struct node *next;
} linklist_t;
linklist_t *create_empty_cyclist_head(void)
{
linklist_t *h = (linklist_t *)malloc(sizeof(linklist_t));
h->data = 1; // 循环链表的特点是, 头结点也要参与运算
h->next = h; // 让头节点的下一个节点指向本省
return h;
}
int cyclist_insert(linklist_t *h, int value)
{
linklist_t *p = (linklist_t *)malloc(sizeof(linklist_t));
p->data = value;
p->next = h->next; // 接链表第1步
h->next = p; //接链表的第2步
return 0;
}
int cyclist_show(linklist_t *h)
{
linklist_t *p = h->next; // 让p指向头节点下一个节点, 让头节点作为最后一个节点使用
printf("单循环链表的内容:");
while (p != h) // p->next == h 是表示循环一圈
{
printf("%d ", p->data); // 输出p指向节点的内容
p = p->next; // 移动节点
}
// p == h
if (p == h) // p指向的h了, 作为循环表的最后一个元素
{
printf("%d ", p->data); // 输出p指向节点的内容
}
printf("\n");
return 0;
}
// 循环表删除节点时, 要定位到要删除节点的前一个节点
// 如果要删除的节点是头结点 , 必须要更新一个新的头结点
linklist_t * cyclist_delete(linklist_t *h, int value)
{
linklist_t *p = h; // P 指向h节点
linklist_t *q;
while (p->next != h) // 循环一圈少一个
{
if (p->next->data == value)
{
q = p->next; // 要删除的节点
p->next = q->next; // 连接链表
free(q);
return h; // 函数要返回, 不返回在最后一个节点时, 会报错,
}
p = p->next; // p往后移动
}
if (p->next == h) // 表示要删除的是头结点
{
q = p->next; // 要删除的节点
p->next = q->next; // 连接链表
free(q);
return p; // 此时删除的是头结点, 要返回一个新的头 p
}
}
int main(int argc, char const *argv[])
{
linklist_t *H = create_empty_cyclist_head();
cyclist_insert(H, 2);
cyclist_insert(H, 3);
cyclist_insert(H, 4);
cyclist_insert(H, 5);
cyclist_insert(H, 6);
cyclist_insert(H, 7);
cyclist_insert(H, 8);
cyclist_insert(H, 9);
cyclist_insert(H, 10);
cyclist_show(H);
H = cyclist_delete(H,10);
H = cyclist_delete(H,1);
H = cyclist_delete(H,5);
cyclist_show(H);
return 0;
}
- 运行结果
单循环链表的内容:10 9 8 7 6 5 4 3 2 1
单循环链表的内容:9 8 7 6 4 3 2
实例7 实现单循环链表的改查逆序
- 逆序的内存图
- 源文件
04-ds/07-cyclist.c
- 源代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node
{
int data;
struct node *next;
} linklist_t;
linklist_t *create_empty_cyclist_head(void)
{
linklist_t *h = (linklist_t *)malloc(sizeof(linklist_t));
h->data = 1; // 循环链表的特点是, 头结点也要参与运算
h->next = h; // 让头节点的下一个节点指向头部
return h;
}
int cyclist_insert(linklist_t *h, int value)
{
linklist_t *p = (linklist_t *)malloc(sizeof(linklist_t));
p->data = value;
p->next = h->next; // 接链表第1步
h->next = p; //接链表的第2步
return 0;
}
int cyclist_show(linklist_t *h)
{
linklist_t *p = h->next; // 让p指向头节点下一个节点, 让头节点作为最后一个节点使用
printf("单循环链表的内容:");
while (p != h) // p->next == h 是表示循环一圈
{
printf("%d ", p->data); // 输出p指向节点的内容
p = p->next; // 移动节点
}
// p == h
if (p == h) // p指向的h了, 作为循环表的最后一个元素
{
printf("%d ", p->data); // 输出p指向节点的内容
}
printf("\n");
return 0;
}
// 循环表删除节点时, 要定位到要删除节点的前一个节点
// 如果要删除的节点是头结点 , 必须要更新一个新的头结点
linklist_t *cyclist_delete(linklist_t *h, int value)
{
linklist_t *p = h; // P 指向h节点
linklist_t *q;
while (p->next != h) // 循环一圈少一个
{
if (p->next->data == value)
{
q = p->next; // 要删除的节点
p->next = q->next; // 连接链表
free(q);
return h; // 函数要返回, 不返回在最后一个节点时, 会报错,
}
p = p->next; // p往后移动
}
if (p->next == h) // 表示要删除的是头结点
{
q = p->next; // 要删除的节点
p->next = q->next; // 连接链表
free(q);
return p; // 此时删除的是头结点, 要返回一个新的头 p
}
}
// 头节点时链表的最后一个节点
int cyclist_modify(linklist_t *h, int old, int new)
{
linklist_t *p = h->next; // 指向链表的第一个节点
while (p != h)
{
if (p->data == old)
{
p->data = new;
return 0;
}
p = p->next; // 移动节点
}
if (p == h)
{
if (p->data == old)
{
p->data = new;
return 0;
}
}
return 0;
}
// 找到返回为真
// 没找到返回为假
int cyclist_search(linklist_t *h, int value)
{
linklist_t *p = h->next; // 指向链表的第一个节点
while (p != h)
{
if (p->data == value)
{
return 1;
}
p = p->next; // 移动节点
}
if (p == h)
{
if (p->data == value)
{
return 1;
}
}
return 0;
}
// 因为链表的头节点, 我们把他处理为最后一个节点, 要做逆序, 必须把头结点处理为
// 第一个节点, 最先输出, 只需要把头结点往前移动一个位置即可, 头节点变为第一个节点
linklist_t * cyclist_reverse(linklist_t * h)
{
linklist_t * p = h->next ; // 让p 指向第一个节点
h->next = h ; // 把头设置位一个空节点
linklist_t * q;
// 把p指向的节点循环插入到h的后面
while(p != h)
{
q = p->next ; // 保存下一个要插入的节点
p->next = h->next ; // 接链表的第1步
h->next = p ; // 接链表的第2步
p = q ; // 把p和q 保持一致
}
// 处理最后一个节点, 把头节点往后移动一个位置
// 要找到头节点的前一个位置
p = h ; // 让p 指向h
while(p->next != h)
{
p = p->next ;
}
if(p->next == h)
{
return p ;
}
return h ;
}
int main(int argc, char const *argv[])
{
linklist_t *H = create_empty_cyclist_head();
cyclist_insert(H, 2);
cyclist_insert(H, 3);
cyclist_insert(H, 4);
cyclist_insert(H, 5);
cyclist_insert(H, 6);
cyclist_insert(H, 7);
cyclist_insert(H, 8);
cyclist_insert(H, 9);
cyclist_insert(H, 10);
cyclist_show(H);
H = cyclist_delete(H, 10);
H = cyclist_delete(H, 1);
H = cyclist_delete(H, 2);
H = cyclist_delete(H, 3);
H = cyclist_delete(H, 4);
H = cyclist_delete(H, 5);
cyclist_show(H);
cyclist_modify(H, 9, 99);
cyclist_modify(H, 7, 77);
cyclist_modify(H, 6, 66);
cyclist_show(H);
if (cyclist_search(H, 99))
{
printf("99 found\n");
}
else
{
printf("99 not found\n");
}
if (cyclist_search(H, 88))
{
printf("88 found\n");
}
else
{
printf("88 not found\n");
}
H = cyclist_reverse(H);
cyclist_show(H);
H = cyclist_reverse(H);
cyclist_show(H);
return 0;
}
- 运行结果
单循环链表的内容:10 9 8 7 6 5 4 3 2 1
单循环链表的内容:9 8 7 6
单循环链表的内容:99 8 77 66
99 found
88 not found
单循环链表的内容:66 77 8 99
单循环链表的内容:99 8 77 66
1.5 双链表
1. 双链表的插入
2. 双链的删除
3. 实例8 双连表的增删
- 双连表的增删
- 源文件
04-ds/08-double_linklist.c
- 源代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node
{
int data;
struct node *prior;
struct node *next;
} linklist_t;
linklist_t *create_empty_double_linklist_head()
{
linklist_t *h = (linklist_t *)malloc(sizeof(linklist_t));
h->data = 0;
h->next = NULL;
h->prior = NULL;
return h;
}
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; // 双链插入第1步
h->next = p; // 双链插入第2步
if (p->next != NULL)
p->next->prior = p; // 双链插入第3步
p->prior = h; // 双链插入第4步
return 0;
}
int double_linklist_show(linklist_t *h)
{
linklist_t *p = h->next;
printf("链表的内容为:");
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
return 0;
}
int double_linklist_delete(linklist_t *h, int value)
{
linklist_t *p = h->next;
while (p != 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 0;
}
p = p->next;
}
return 0;
}
int main(int argc, char const *argv[])
{
linklist_t *H = create_empty_double_linklist_head();
for (int i = 0; i < 10; i++)
{
double_linklist_insert(H, i + 1);
}
double_linklist_show(H);
double_linklist_delete(H, 10);
double_linklist_delete(H, 1);
double_linklist_delete(H, 5);
double_linklist_show(H);
return 0;
}
- 运行结果
链表的内容为:10 9 8 7 6 5 4 3 2 1
链表的内容为:9 8 7 6 4 3 2
4. 双链表的逆序
5. 实例9 双链表的改查逆序
-
双链表的改查逆序
-
源文件
04-ds/08-double_linklist.c
- 源代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node
{
int data;
struct node *prior;
struct node *next;
} linklist_t;
linklist_t *create_empty_double_linklist_head()
{
linklist_t *h = (linklist_t *)malloc(sizeof(linklist_t));
h->data = 0;
h->next = NULL;
h->prior = NULL;
return h;
}
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; // 双链插入第1步
h->next = p; // 双链插入第2步
if (p->next != NULL)
p->next->prior = p; // 双链插入第3步
p->prior = h; // 双链插入第4步
return 0;
}
int double_linklist_show(linklist_t *h)
{
linklist_t *p = h->next;
printf("链表的内容为:");
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
return 0;
}
int double_linklist_delete(linklist_t *h, int value)
{
linklist_t *p = h->next;
while (p != 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 0;
}
p = p->next;
}
return 0;
}
int double_linklist_modify(linklist_t *h, int old, int new)
{
linklist_t *p = h->next; // p 指向头结点的下一个节点
while (p != NULL)
{
if (p->data == old)
{
p->data = new;
return 0;
}
p = p->next; // 移动节点
}
return 0;
}
int double_linklist_search(linklist_t *h, int value)
{
linklist_t *p = h->next; // p 指向头结点的下一个节点
while (p != NULL)
{
if (p->data == value)
{
return 1;
}
p = p->next; // 移动节点
}
return 0;
}
int double_linklist_reverse(linklist_t *h)
{
linklist_t *p = h->next; // p 指向头结点的下一个节点
linklist_t *q; //中间变量
// 设置一个空头
h->next = NULL;
h->prior = NULL;
while (p != NULL)
{
q = p->next;
p->next = h->next;
h->next = p;
if (p->next != NULL)
p->next->prior = p;
p->prior = h;
p = q; // 让p往后移动一位
}
return 0 ;
}
int main(int argc, char const *argv[])
{
linklist_t *H = create_empty_double_linklist_head();
for (int i = 0; i < 10; i++)
{
double_linklist_insert(H, i + 1);
}
double_linklist_show(H);
double_linklist_delete(H, 10);
double_linklist_delete(H, 1);
double_linklist_delete(H, 5);
double_linklist_show(H);
double_linklist_modify(H, 9, 99);
double_linklist_modify(H, 2, 22);
double_linklist_modify(H, 6, 66);
double_linklist_show(H);
if (double_linklist_search(H, 99))
{
printf("99 found\n");
}
else
{
printf("99 not found\n");
}
if (double_linklist_search(H, 1))
{
printf("1 found\n");
}
else
{
printf("1 not found\n");
}
double_linklist_reverse(H);
double_linklist_show(H);
double_linklist_reverse(H);
double_linklist_show(H);
return 0;
}
- 运行结果
链表的内容为:10 9 8 7 6 5 4 3 2 1
链表的内容为:9 8 7 6 4 3 2
链表的内容为:99 8 7 66 4 3 22
99 found
1 not found
链表的内容为:22 3 4 66 7 8 99
链表的内容为:99 8 7 66 4 3 22
1.6 链表的应用
1. Joseph问题
2. 实例10 joseph问题
- joseph问题
- 源文件
04-ds/10-joseph.c
- 源代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node
{
int data;
struct node *next;
} linklist_t;
linklist_t *create_empty_cyclist_head(void)
{
linklist_t *h = (linklist_t *)malloc(sizeof(linklist_t));
h->data = 1; // 循环链表的特点是, 头结点也要参与运算
h->next = h; // 让头节点的下一个节点指向本省
return h;
}
int cyclist_insert(linklist_t *h, int value)
{
linklist_t *p = (linklist_t *)malloc(sizeof(linklist_t));
p->data = value;
p->next = h->next; // 接链表第1步
h->next = p; //接链表的第2步
return 0;
}
int cyclist_show(linklist_t *h)
{
linklist_t *p = h; // 让p指向头节点下一个节点, 让头节点作为最后一个节点使用
printf("单循环链表的内容:");
while (p->next != h) // p->next == h 是表示循环一圈
{
printf("%d ", p->data); // 输出p指向节点的内容
p = p->next; // 移动节点
}
// p == h
if (p->next == h) // p指向的h了, 作为循环表的最后一个元素
{
printf("%d ", p->data); // 输出p指向节点的内容
}
printf("\n");
return 0;
}
int joseph(int n, int k, int m)
{
linklist_t *H = create_empty_cyclist_head();
linklist_t *q ;
for (int i = n-1; i > 0; i--)
{
cyclist_insert(H, i + 1);
}
cyclist_show(H);
linklist_t *p = H;
for (int i = 0; i < k - 1; i++) // 定位到m 的位置上
{
p = p->next;
}
printf("出列顺序为:");
//p == p->next 表示链表中只剩下一个节点了
while (p != p->next )
{
// 此时p移动到6节点的前一个位置, 5号的位置
for (int i = 0; i < m - 2; i++)
{
p = p->next;
}
// 删除6
q = p->next ; // q 是要删除的节点
p->next = q->next; // 接上5 和 7
printf("%d ",q->data);
free(q);
p = p->next ; // 往后移动一个节点
}
printf("%d ",p->data); // 最后一个节点输出
free(p); // 最后一个节点释放掉
printf("\n");
return 0;
}
int main(int argc, char const *argv[])
{
joseph(8, 3, 4);
return 0;
}
- 运行结果
单循环链表的内容:1 2 3 4 5 6 7 8
出列顺序为:6 2 7 4 3 5 1 8