第1章 数据结构的概念

news2025/4/9 5:42:09

文章目录

    • 文档配套视频讲解链接地址
    • 第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问题

文档配套视频讲解链接地址

  1. 腾讯课堂视频链接地址 : 01_概述_数据结构概念1
  2. 腾讯课堂视频链接地址 : 02_概述_数据结构概念2
  3. 腾讯课堂视频链接地址 : 03_链表_链表的插入1
  4. 腾讯课堂视频链接地址 : 04_链表_链表的插入2
  5. 腾讯课堂视频链接地址 : 05_链表_链表的删除
  6. 腾讯课堂视频链接地址 : 06_链表_链表的改查逆序
  7. 腾讯课堂视频链接地址 : 07_顺序表_增删
  8. 腾讯课堂视频链接地址 : 08_顺序表_改查逆序排序
  9. 腾讯课堂视频链接地址 : 09_单循环表_增删
  10. 腾讯课堂视频链接地址 : 10_单循环表_改查逆序

第01章 数据结构的概念

1.1 数据结构的知识体系

image-20220927100332688

1.2 链表

1. 创建头结点的内存图

image-20220927151355443

2. 插入1节点时的内存图

image-20220927105246320

3. 插入2节点时的内存图

image-20220927105720266

4. 插入3节点的内存图

image-20220927141003052

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

image-20220927154410333

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. 链表逆序图

image-20220928101810481

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. 单循环链表的空头内存图

image-20220928160528358

2. 单循环链表的2号节点插入时的内存图

image-20220928160456230

3. 单循环链表的3号节点插入时的内存图

image-20220928160415705

实例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 实现单循环链表的改查逆序

  • 逆序的内存图

image-20220929092207283

  • 源文件
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. 双链表的插入

image-20220929102259360

2. 双链的删除

image-20220929103856775

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. 双链表的逆序

image-20220929152020867

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问题

image-20220929154041751

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 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/5449.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Allegro阻抗分析指导书

Allegro阻抗分析指导书 利用Allegro自带的功能可以快速分析信号的阻抗 操作如下 首先用172版本打开PCB 把每层厚度和介电常数填写进去 点击work flow Manager,出现右图对话框 选择需要查看的网络 点击start Analysis 点击impedance table和impedance vision就可以查看阻…

【网络篇】第九篇——多线程版的TCP网络程序

多进程与多线程对比 多进程 多线程 多线程版的TCP网络程序 多进程与多线程对比 多进程 优点 可以处理多个用户易于边写稳定&#xff0c;因为进程具有独立性 缺点 连接来了之后才创建进程&#xff0c;性能太低多进程服务器特别吃资源&#xff0c;而且同时服务的客户有上限…

(最新版2022版)剑指offer之排序题解

&#xff08;最新版2022版&#xff09;剑指offer之排序题解JZ3数组中重复的数字JZ51 数组中的逆序对JZ40 最小的K个数JZ41 数据流中的中位数JZ3数组中重复的数字 思路&#xff1a; 既然数组长度为nnn只包含了0到n−1n-1n−1的数字&#xff0c;那么如果数字没有重复&#xff0c…

qt C++中指针自动释放内存及程序中的内存操作、管理

程序加载到内存后代码存储到代码区&#xff0c;并将全局变量、静态变量初始化到全局/静态内存区&#xff0c;然后会分配2M左右的栈内存区用于存储局部变量&#xff0c;并在运行时根据需要可以在堆内存区(空闲内存区及硬盘的虚拟内存区)申请空间。 程序可使用的内存分区↓ 各基…

C++之Hello World

概览 编程语言历史 机器语言:00110101…最初始的计算机内部语言,不同机器使用的语言甚至不同 汇编语言:利用简单符号(a DB 7H…)对机器语言进行了一定的抽象,增加了可读性,更加人性化.在一定程度上仍然依赖硬件,属于低级的语言 高级语言:使用文字通过编译器被翻译为机器语言…

Vue中 引入使用 element-resize-detector 监听 Dom 元素 宽度、高度 变化

1. 前言 很多做pc端平台的小伙伴都遇到过这样一个问题&#xff1a;在做侧边栏菜单时会有一个收缩和展开的一个功能&#xff0c;在伸缩的过程中右边的页面的宽度就会随之改变。我上网查了查 &#xff0c;也动手试了试 window.onresize ()>{}。却不尽人意&#xff0c;因为它…

SVM 超平面计算例题

SVM Summary Example Suppose the dataset contains two positive samples x(1)[1,1]Tx^{(1)}[1,1]^Tx(1)[1,1]T andx(2)[2,2]Tx^{(2)}[2,2]^Tx(2)[2,2]T, and two negative samples x(3)[0,0]Tx^{(3)}[0,0]^Tx(3)[0,0]T and x(4)[−1,0]Tx^{(4)}[-1,0]^Tx(4)[−1,0]T. Please…

MySQL纯代码复习

前言 本文章是用于总结尚硅谷MySQL教学视频的记录文章&#xff0c;主要用于复习&#xff0c;非商用 原视频连接&#xff1a;https://www.bilibili.com/video/BV1iq4y1u7vj/?p21&spm_id_frompageDriver&vd_sourcec4ecde834521bad789baa9ee29af1f6c https://www.bilib…

C#重启 --- 枚举

第一部分 --- 枚举 枚举类型的本质其实就是在给整型数据加标签&#xff0c;当编译器遇到枚举类型标签的时候&#xff0c;编译器会自动获取标签对应的整型数据&#xff08;默认从0开始由上往下递增&#xff09; 枚举类型的使用方法&#xff1a; 1.枚举类型的类型名是由我们自己…

四.STM32F030C8T6 MCU开发之利用 TIM1+ADC1+DMA1 实现5路(3路外部电压模拟信号+内部2路信号)采集

四.STM32F030C8T6 MCU开发之利用 TIM1ADC1DMA1 实现5路&#xff08;3路外部电压模拟信号内部2路信号&#xff09;采集 文章目录四.STM32F030C8T6 MCU开发之利用 TIM1ADC1DMA1 实现5路&#xff08;3路外部电压模拟信号内部2路信号&#xff09;采集0.总体功能概述ADC 简介1.ADC硬…

数据结构《LinkeList 双向链表》

LinkeList LinkeList 的低层是由双向链表结构组成的&#xff0c;所有元素都是存放到单独的节点当中&#xff0c;通过地址引用将节点串联起来 因此在任意位置插入或删除元素时&#xff0c;都不在需要移动元素&#xff0c;效率较高 下面是双向链表的结构图&#xff1a; 在集合框…

【从零开始游戏开发】静态资源优化 | 全面总结 |建议收藏

你知道的越多&#xff0c;你不知道的越多 &#x1f1e8;&#x1f1f3;&#x1f1e8;&#x1f1f3;&#x1f1e8;&#x1f1f3; 点赞再看&#xff0c;养成习惯&#xff0c;别忘了一键三连哦 &#x1f44d;&#x1f44d;&#x1f44d; 文章持续更新中 &#x1f4dd;&#x1f4dd;…

C++智能指针

文章目录一、智能指针的目的和基本原理二、不带引用计数的智能指针2.1 auto_ptr2.2 scoped_ptr2.3 unique_ptr三、带引用计数的智能指针3.1 shared_ptr3.2 weak_ptr一、智能指针的目的和基本原理 一般new出来的对象会用普通指针引用&#xff0c;此时申请的堆上的资源需要我们手…

乐趣国学—品读《弟子规》中的“泛爱众”之道(上篇)

前言 “泛爱众”就是以广泛的爱心对待社会大众。人类生活是以爱心为纽带&#xff0c;没有爱心&#xff0c;人类生活就太痛苦不堪了。这个爱心从哪里来的&#xff1f;这个爱心就是孝心。孝道&#xff0c;正是培养爱心的第一步&#xff0c;一个连父母都不爱的人决不会真心爱他人&…

19.Feign 的工程化实例:eureka,ribbon,feign,hystrix(springcloud)

项目模型 项目结构 本实例创建model都是通过maven手动创建&#xff0c;依赖进行手动导入&#xff0c;好处是比使用springboot模板创建更加灵活&#xff0c;更方便的进行父子模块的管理。 1.创建父项目feign-project 2.对父项目feign-project的pom.xml&#xff0c;进行手动导入依…

Linux基础内容(10)—— 进程概念

目录 1.冯诺依曼体系结构 ​编辑1.冯诺依曼体系特点 2.cpu运算原理 3.数据传输 2.操作系统 1.操作系统管理的真相 2.操作系统与硬件的交互方式 3.操作系统与用户的交互方式 1.系统调用接口 2.用户对系统调用的使用 3.进程 1.进程的概念 2.Linux中的进程 3.与进程…

基于DJYOS的SPI驱动编写指导手册

1.贡献者列表 深圳市秦简计算机系统有限公司DJYOS驱动开发团队。 2.概述 DJYOS的DjyBus总线模型为IIC、SPI之类的器件提供统一的访问接口&#xff0c;SPIBUS模块是DjyBus模块的一个子模块&#xff0c;为SPI器件提供统一的编程接口&#xff0c;实现通信协议层与器件层的分离。…

Python 考试练习题 2

一、选择题 1、下列是 python 合法标识符的是&#xff08; B&#xff09;。 A. 2variable B. variable2 C. $anothervar D. if 2、在 python 中字符串的表示方式是&#xff08;D &#xff09;。 A.采用单引号包裹 B.采用双引号包裹 C.采用三重单引号包裹 D.ABC 都是 3、设有…

【浅学Linux】动态库与静态库的封装与使用

朋友们好&#xff0c;这里简单介绍一下LINUX学习中关于动态库与静态库的理解&#xff0c;以及站在封装和使用的角度去介绍是如何封装的&#xff1f;如何使用的&#xff1f; 文章目录一&#xff1a;动态库与静态库的理解二&#xff1a;静态库2.1&#xff1a;静态库的使用2.2&…

Sprite Editor

1、SpriteEditor SpriteEditor是精灵图片编辑器 它主要用于编辑2D游戏开发中使用的Sprite精灵图片 它可以用于编辑 图集中提取元素&#xff0c;设置精灵边框&#xff0c;设置九宫格&#xff0c;设置轴心&#xff08;中心&#xff09;点等等功能 2、Single图片编辑 Sprite Ed…