数据结构----双向链表

news2025/1/20 3:34:34

一丶双向链表

1.特点

        逻辑结构:线性结构

        存储结构:链式存储
        操作:增删改查

2.函数的操作

创空

双链表从中间插入

双向链表尾插

删除中间节点

删除尾节点

#include <stdio.h>
#include <stdlib.h>
typedef int datatype; // 重定义数据类型
typedef struct linklist
{
    datatype data;           // 数据域
    struct linklist *next;   // 指针域,指向下一个地址
    struct linklist *perior; // 指针域,指向前一个地址
} node_t, *node_p;
typedef struct
{
    node_p head; // 指向头结点
    node_p tail; // 指向尾节点
    int len;     // 记录链的长度
} node;
node *CreateEmpty() // 创建一个空的双向链表
{
    node *p = (node *)malloc(sizeof(node)); // 开辟一个存放头尾指针的空间
    if (NULL == p)
    {
        printf("Cteate err");
        return 0;
    }
    p->len = 0;                                         // 初始化链表长度
    p->head = p->tail = (node_p)malloc(sizeof(node_t)); // 开辟一个链表头结点
    if (NULL == p->head)
    {
        printf("p->head err");
        return NULL;
    }
    p->head->next = p->head->perior = NULL; // 初始化
    return p;
}
int Insert(node *p, int post, int data) // 插入数据,post为要插入的位置,data为要插入的数据
{
    node_p temp = NULL;            // 定义一个指针,用来存放头/尾指针
    if (post < 0 || post > p->len) // 判断插入位置是否超出范围
    {
        printf("Post err");
        return -1;
    }
    node_p p_new = (node_p)malloc(sizeof(node_t)); // 对插入的数据开辟空间
    if (NULL == p_new)                             // 判断是否开辟成功
    {
        printf("P_new err");
        return -1;
    }
    p_new->data = data; // 初始化新节点
    p_new->next = NULL;
    p_new->perior = NULL;
    if (post == p->len) // 如果插入位置在最后,使用尾插法
    {
        p->tail->next = p_new;
        p_new->perior = p->tail;
        p->tail = p_new;
    }
    else
    {
        if (post < p->len / 2) // 当插入位置在前半段时,从头开始向后遍历
        {
            temp = p->head;
            for (int i = 0; i <= post; i++)
                temp = temp->next;
        }
        else // 当插入位置在后半段时,从尾向前遍历
        {
            temp = p->tail;
            for (int i = p->len - 1; i > post; i--)
                temp = temp->perior;
        }
        temp->perior->next = p_new; // 将新节点连接到链表中
        p_new->perior = temp->perior;
        temp->perior = p_new;
        p_new->next = temp;
    }
    p->len++;
    return 0;
}
int IsEmpty(node *p) // 判空
{
    return p->len == 0;
}
int show(node *p) // 输出数据
{
    if (IsEmpty(p)) // 判空
    {
        printf("show err");
        return -1;
    }
    // 顺序遍历,从头节点向后遍历
    printf("正序遍历\n");
    node_p temp = p->head->next;
    while (temp)
    {
        printf("%d ", temp->data);
        temp = temp->next;
    }
    printf("\n");
    // 逆序遍历,从尾节点向前遍历
    printf("逆序遍历\n");
    temp = p->tail;
    while (temp != p->head)
    {
        printf("%d ", temp->data);
        temp = temp->perior;
    }
    printf("\n");
}
int Delete(node *p, int post) // 根据位置删除数据。post为要删除的位置
{
    node_p temp = NULL;
    if (IsEmpty(p) || post < 0 || post >= p->len) // 判断删除数据位置是否合理
    {
        printf("del err");
        return -1;
    }
    if (post == p->len - 1) // 如果要删除尾节点,使用以下方法
    {
        node_p p_del = p->tail;
        p->tail = p->tail->perior;
        p->tail->next = NULL;
        free(p_del);
        p_del = NULL;
    }
    else // 删除中间节点
    {
        if (post < p->len / 2) // 当删除位置在前半段时,从头开始向后遍历
        {
            temp = p->head->next;
            for (int i = 0; i < post; i++)
                temp = temp->next;
        }
        else // 当删除位置在后半段时,从后向前遍历
        {
            temp = p->tail;
            for (int i = p->len - 1; i > post; i--)
                temp = temp->perior;
        }
        temp->perior->next = temp->next; // 删除后将删除节点的前后节点相连
        temp->next->perior = temp->perior;
        free(temp); // 将被删除节点释放
        temp = NULL;
    }
    p->len--;
    return 0;
}
int DeleteData(node *p, int data)//删除所有出现的该数据的节点,data为要删除节点的数据
{
    if (IsEmpty(p))//判空
    {
        printf("del data err");
        return -1;
    }
    node_p temp = NULL;
    temp = p->head->next;//跳过头节点
    node_p p_del = NULL;
    while (temp)//循环遍历链表
    {
        if (temp->data == data)//数据域等于data时进入判断
        {
            if (temp == p->tail)//如果为尾节点时
            {
                p_del = temp;
                temp = temp->perior;
                free(p_del);
                p_del = NULL;
            }
            else//如果是中间节点
            {
                p_del = temp;
                p_del->perior->next = p_del->next;
                p_del->next->perior = p_del->perior;
                temp = temp->next;
                free(p_del);
                p_del = NULL;
            }
        }
        else
            temp = temp->next;//更新temp
    }
    return 0;
}
int Modify(node *p, int post, int data) // 修改数据,post为修改位置,data为要修改的数据
{
    if (IsEmpty(p) || post < 0 || post >= p->len) // 判断修改位置是否合理
    {
        printf("Modify err");
        return -1;
    }
    node_p temp = NULL;
    if (post < p->len / 2) // 当修改位置在前半段时,从头开始向后遍历
    {
        temp = p->head->next;
        for (int i = 0; i < post; i++)
            temp = temp->next;
    }
    else // 当修改位置在后半段时,从后向前遍历
    {
        temp = p->tail;
        for (int i = p->len - 1; i > post; i--)
            temp = temp->perior;
    }
    temp->data = data; // 修改数据
    return 0;
}
int Search(node *p, int data) // 根据数据查找数据第一次出现的下标
{
    node_p temp = p->head->next;
    int post = 0;//记录下标
    while (temp)//循环遍历链表
    {
        if (temp->data == data)//返回第一次出现data的下标
            return post;
        temp = temp->next;
        post++;
    }
    return -1;
}
int main(int argc, char const *argv[])
{
    node *p = CreateEmpty();
    Insert(p, 0, 1);
    Insert(p, 1, 2);
    Insert(p, 2, 3);
    Insert(p, 3, 4);
    Insert(p, 4, 5);
    Insert(p, 5, 6);
    printf("最初的链表\n");
    show(p);
    printf("删除指定位置后的链表\n");
    Delete(p, 1);
    show(p);
    printf("修改指定位置后的链表\n");
    Modify(p, 1, 4);
    show(p);
    printf("删除指定数据后的链表\n");
    DeleteData(p, 4);
    show(p);
    return 0;
}

二丶双向循环链表

约瑟夫问题:

#include <stdio.h>
#include <stdlib.h>

typedef int datatype;
typedef struct node_t
{
    datatype data;
    struct node_t *prior;
    struct node_t *next;
} link_node_t, *link_list_t;

typedef struct doublelinklist
{
    link_list_t head;
    link_list_t tail;
} double_node_t, *double_list_t;

int main(int argc, const char *argv[])
{
    int i;
    int all_num = 8;   // 猴子总数
    int start_num = 3; // 从3号猴子开始数
    int kill_num = 3;  // 数到几杀死猴子
    link_list_t h = NULL;
    link_list_t pdel = NULL; // 用来指向被杀死猴子的节点
    printf("请您输入猴子的总数,开始号码,出局号码:\n");
    scanf("%d%d%d", &all_num, &start_num, &kill_num);
    // 1.创建一个双向的循环链表
    double_list_t p = (double_list_t)malloc(sizeof(double_node_t)); // 申请头指针和尾指针
    if (NULL == p)
    {
        perror("malloc failed");
        return -1;
    }
    p->head = p->tail = (link_list_t)malloc(sizeof(link_node_t));
    if (NULL == p->tail)
    {
        perror("p->tail malloc failed");
        return -1;
    }
    p->head->data = 1;
    p->head->prior = NULL;
    p->head->next = NULL;
    // 将创建n个新的节点,链接到链表的尾
    for (i = 2; i <= all_num; i++)
    {
        link_list_t pnew = (link_list_t)malloc(sizeof(link_node_t));
        if (NULL == pnew)
        {
            perror("pnew malloc failed");
            return -1;
        }
        pnew->data = i;
        pnew->prior = NULL;
        pnew->next = NULL;
        //(1)将新的节点链接到链表的尾
        p->tail->next = pnew;
        pnew->prior = p->tail;
        //(2)尾指针向后移动,指向当前链表的尾
        p->tail = pnew;
    }
    //(3)形成双向循环链表
    p->tail->next = p->head;
    p->head->prior = p->tail;
    // 调试程序
#if 0
	while(1)
	{
		printf("%d\n",p->head->data);
		p->head = p->head->next;
		sleep(1);
	}
#endif
    // 2.循环进行杀死猴子
    h = p->head;
    //(1)先将h移动到start_num处,也就是开始数数的猴子号码处
    for (i = 0; i < start_num - 1; i++)
        h = h->next;
    while (h->next != h) // 当h->next == h 就剩一个节点了,循环结束
    {
        //(2)将h移动到即将杀死猴子号码的位置
        for (i = 0; i < kill_num - 1; i++)
            h = h->next;
        //(3)进行杀死猴子,经过上面的循环后,此时的h指向即将杀死的猴子
        h->prior->next = h->next;
        h->next->prior = h->prior;
        pdel = h; // pdel指向被杀死猴子的位置
        printf("kill is -------%d\n", pdel->data);
        h = h->next; // 需要移动,从杀死猴子后的下一个位置开始数
        free(pdel);
    }
    printf("猴王是%d\n", h->data);
    return 0;
}

单向链表与双向(循环)链表的区别:

      在存储空间方面:单链表需要的存储空间比双向链表的要少,因为双向链表不仅保存有指向下一个节点的指针,还保存有指向上一个节点的指针,需要较大的空间来存储双向链表的指针域。

      在处理时间方面:双向链表的插入与删除操作比单链表的效率高,因为如果在后半段删除或者插入可以从后往前遍历到插入或删除位置然后进行操作。

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

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

相关文章

全自动商用油炸锅介绍:

全自动商用油炸锅‌是一种专门为商业用途设计的厨房设备&#xff0c;旨在高效、节能、卫生地完成大量食品的油炸加工。这种设备通常采用油水混合技术&#xff0c;能够自动过滤残渣&#xff0c;延长换油周期&#xff0c;从而大大降低用油成本。全自动商用油炸锅适合中、小型油炸…

来聊一聊JVM

为什么需要JVM&#xff0c;不要JVM可以吗&#xff1f; 1.JVM可以帮助我们屏蔽底层的操作系统 一次编译&#xff0c;到处运行 2.JVM可以运行Class文件 我们的编译器到底干了什么事&#xff1f; 仅仅是将我们的 .java 文件转换成了 .class 文件&#xff0c;实际上就是文件格式…

C的温故而知新:结构和其他数据形式(C Primer Plus第十四章)

第十四章&#xff1a;结构和其他数据形式 在我们使用语言进行程序编程设计的时候&#xff0c;经常会出现很多复杂的数据&#xff0c;&#xff0c;每到这种时候&#xff0c;就需要创建很多的变量去存储各种各类的数据。还有一种情况&#xff0c;不同类型的数据是相互关联的&…

通过 MATLAB 的 cylinder 函数生成圆柱体的表面坐标,生成表示一个具有非标准形状的圆柱体(在本例中是杯子)

MATLAB的机器人系统工具箱&#xff08;RST&#xff09;的官方例程Plan a Reaching Trajectory with Multiple Kinematic Constraints规划具有多个运动学约束的到达轨迹 % 创建用于视觉化杯子的点 [X,Y,Z] cylinder(cupRadius*linspace(0,1,50).^0.125); % 调整 Z 坐标的比例…

element-plus form 表单嵌套表格树单行校验问题

需要实现这样的效果&#xff0c;单行校验表格树 公共方法可以直接用 const findPosi (tree, targetId, path "") > {for (let i 0; i < tree.length; i) {const node tree[i];if (node.id targetId) {return path i;}if (node.sub_parameters &&a…

网站服务包含哪些

网站服务是指一系列通过互联网提供的服务&#xff0c;涵盖了多个方面&#xff0c;从基本的网站建设到更高级的在线业务解决方案。以下是网站服务可能包含的一些主要方面&#xff1a; 网站设计与开发&#xff1a; 网站设计&#xff1a; 包括整体网站结构、用户界面设计、图形设…

一、插件开发入门【Qt环境-mingw6.5.3-qmake版】-封装dll调用

1.先创建一个名为mainProject的主项目,并进行简单的ui布局 2.使用C Library库创建动态库项目作为主项目的子插件 2.1 Qt模块选择为Widgets 2.2 环境配置和主项目要一致 3.给插件添加qt界面设计类 4.给subPlugin插件添加一个名为PluginInterface.h的头文件 #ifndef PLUGININTERF…

软件设计师全套备考系列文章8 -- 查找、排序

软考-- 软件设计师&#xff08;8&#xff09;-- 查找、排序 文章目录 软考-- 软件设计师&#xff08;8&#xff09;-- 查找、排序前言一、查找二、排序三、排序的评价指标&#xff08;重点&#xff09; 前言 考试时间&#xff1a;每年5月、11月&#xff0c;软件设计师每年都会…

湖南 | 产能利用率高达80%的龙头砼企如何进行站内外高效配合

走进标杆企业 走进标杆企业&#xff0c;感受名企力量&#xff0c;探寻学习优秀企业领先之道。本期要跟砼行们推介的标杆企业是湖南省新宁县鑫旺混凝土有限公司。 鑫旺混凝土有限公司成立于2012年&#xff0c;建有三条混凝土生产线&#xff0c;自有水泥、砂石骨料、房地产等一…

vue3 中 defineProps 和 defineEmits

在 Vue 3 中&#xff0c;defineProps 和 defineEmits 是组合式 API 的核心功能&#xff0c;用于处理父子组件之间的传值和事件通信。 1. defineProps defineProps 用于定义并接收父组件传递过来的数据&#xff08;props&#xff09;。它是在子组件中使用的&#xff0c;接收的…

100000在银行存个3年利息居然12000

python存款利息问题 设计一个函数来计算存款利息。存款利息由存款金额和存款时间决定。 如果存款金额小于或等于5000元&#xff0c;则年利率为2%;5000元到10000元之间&#xff0c;则年利率为3%;大于10000元&#xff0c;则年利率为4%。返回本金和利息 def getMoney(amount,yea…

44.开发商购买土地

44.开发商购买土地 &#xff08;用到了前缀和的知识&#xff09; 题目链接 //卡码网题号44.开发商购买土地 #include<iostream> #include<vector> #include<climits> using namespace std;int main() {int n, m;int sum0;cin >> n >> m;vector…

stable-diffusion-webui容器构建教程

一、介绍 Stable Diffusion WebUI 是一个提供了易于使用的 AI 绘画工具&#xff0c;它允许用户通过一个更友好、可视化的网页界面来与 Stable Diffusion 模型互动&#xff0c;可以实现文生图、图生图等。 二、特点 易于使用的界面 &#xff1a;用户可以通过网页界面进行操作…

【数据同步】SeaTunnel初体验,5000字深入浅出带你用上Oracle-CDC

Apache SeaTunnel 是啥&#xff1f;下一代高性能、分布式、海量数据集成框架。支持上百个数据源、传输速度快、准确率高&#xff0c;丰富易扩展的连接器和插件化的连接器设计&#xff0c;能够更轻松的运行复杂的集成。是一个分布式、高性能的数据集成平台&#xff0c;用于数据迁…

USB Type-C如何取9V、12V、15V、20V电压-PD快充协议芯片ECP5701

相信大家在生活中也发现了&#xff0c;现在越来越多的设备都改用这种type-C接口的母座进行取电了。 因为欧盟决议 &#xff1a;自2024年起部分消费电子产品必须提供单一的USB-C充电接口。 那么这种type-C接口相比之前的Micro-B接口有着一个很大的优势就是可以有更高的电压&…

部署同步工具syncthing

1、下载包arm包&#xff08;根据自己的环境下载包&#xff09; #进到指定目录 cd /usr/local/ #可以根据自己的环境下载不同版本的包 wget https://github.com/syncthing/syncthing/releases/download/v1.27.10/syncthing-linux-arm64-v1.27.10.tar.gz2、进行部署 #将其解压 …

接口参数与文档||关于淘宝商品·订单数据API接口的功能达成经验分享

电商数据采集有5种方式&#xff0c;包括API、RPA、数据库连接、Excel下载和ERP等业务系统数据采集。这些方法可帮助卖家获取多平台电商数据&#xff0c;进行深度挖掘&#xff0c;实现电商运营的优化。 电商竞争白热化的今天&#xff0c;一个电商卖家往往会在多个平台铺设店铺来…

Flutter Web 正式官宣弃用 HTML renderer , Canvas 路线成为唯一

Flutter Web 团队计划在 2025 年的第一个 Flutter stable 版本中弃用 HTML renderer&#xff0c;当然在 master 和 beta 中会更早合并这一更改。 关于这个话题&#xff0c;其实在年初的我就曾发布过 《Flutter 即将放弃 Html renderer 》&#xff0c; Html renderer 从 2018 年…

狗都能看懂的Swin Transformer的讲解和代码实现

文章目录 1、Swin-Transformer介绍2、模型整体框架3、Patch Mergeing详解4、W-MSA模块详解MSA模块计算量W-MSA模块计算量 5、SW-MSA详解6、Relative Position Bias详解7、模型详细配置参数 1、Swin-Transformer介绍 自从ViT&#xff08;Vision Transformer&#xff09;出现之后…

腾讯软件测试岗二面:web 测试问题被虐哭了,直到学长给了我这些知识点.....

web 测试一直是大厂软件测试问到的一个重点&#xff0c;下面给大家展示下大厂关于web 测试经常会问到的一些问题&#xff0c;以及解析。看当面试官问到你这些问题的时候&#xff0c;你是否也能够对答如流。 web 测试面试真题及解析&#xff1a; 一&#xff0c;描述用浏览器访问…