王道考研数据结构--3.双链表

news2024/11/15 14:02:39

目录

1.前言

2.代码难点

2.1双链表的插入和删除

3.代码函数

3.1双链表结构体定义

3.2双链表初始化函数

3.3双链表插入

3.4双链表节点删除

3.5双链表的遍历

4.全部代码 


1.前言

日期:2023.6.21

书籍:2024年数据结构考研复习指导(王道考研系列)

内容:双链表的实现,结构体定义,初始化,创建新结点,头插和尾插,查询,按位序插入,删除指定节点,输出单链表


2.代码难点

2.1双链表的插入和删除

在p节点后插操作s节点,

上图操作的语句顺序不是唯一的,但也不是任意的,

①和②两步必须在④步之前

否则p的后继结点的指针就会丢掉,导致插入失败。
为了加深理解,读者可以在纸上画出示意图。

此处①②无顺序要求
若问题改成要求删除结点
q的前驱结点*p,请读者思考具体的操作步骤。


3.代码函数

3.1双链表结构体定义

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

//C语言自定义bool操作
#define bool char
#define false 0
#define true 1

/*数据元素类型*/
typedef int ElemType;

/*双链表结构体定义*/
typedef struct DNode        //定双链表结点类型
{
    ElemType data;          //每个结点存放一个数据元素
    struct DNode *next;     //指针指向下一节点
    struct DNode *prior;    //指针指向上一节点
}DNode,*DLinkList;


3.2双链表初始化函数

/*双链表初始化函数*/
DLinkList DLinkListInit()
{
    //[1]申请空间
    DLinkList L = (DLinkList)malloc(sizeof(DLinkList));
    //[2]安排指针
    L->next = NULL;
    L->prior = NULL;
    //[3]返回头节点
    return L;
}

3.3双链表插入

/*双链表的插入操作*/
//【1】双链表新节点的创建,返回新节点指针
DNode* CreateNewDNode(ElemType data){
    //[1]为新节点分配空间,并判断是否分配成功
    DNode* newNode = (DNode*)malloc(sizeof(DNode));
    if(!newNode){
        printf("分配空间失败,请检查内存!\n");
        return NULL;
    }
    //[2]初始化新节点
    newNode->data = data;
    newNode->next = NULL;
    newNode->prior = NULL;
    //[3]返回节点地址
    return newNode;
}


//【2】双链表节点定位----按位序号查询,返回需要查找的节点的指针
DNode* GetElem(DLinkList L, int i){
    //[1]判空
    if(L->next == NULL) {printf("这是空表!"); return L;}
    //[2]生成指向目标节点的指针p
    DNode* p = L->next;
    //[3]如果i = 0
    if(i < 1){
        p = L;
        return p;
    }
    //[4]开始循环定位
    while (p->next != NULL && i != 1){
        p = p->next;
        i--;
    }
    //[5]返回节点
    if(i != 1)
    printf("没有这个节点,返回链表的最后一位.\n");
    return p;
}


//【3】双链表节点定位----按内容值查询,返回需要查找的节点的指针
DNode* LocateElem(DLinkList L,ElemType e){
    //[1]判空
    if(L->next == NULL) {printf("这是空表!"); return L;}
    //[2]生成指向目标节点的指针p
    DNode* p = L->next;
    //[3]开始循环定位
    while (p->next != NULL && p->data != e){
        p = p->next;
    }
    //[4]返回节点
    if(p->data != e)//排除最后一位元素是目标元素的情况
    printf("没有这个节点,返回链表的最后一位.");
    return p;
}


//【4】双链表的插入操作
//在第i位插入新元素,如果第i位不存在,则插入在表尾
bool DLinkListInsert(DLinkList L,int i,ElemType e){
    //[0]程序健壮性增强
    if(i < 1){
        printf("输入出错!");
        return 0;
    }
    //[1]建立新元素节点
    DNode* s = CreateNewDNode(e);
    if(s == NULL) return 0;

    //[2]生成指向待插入位置的指针p
    DNode* p = GetElem(L,i-1);
    //[3]插入新节点

    s->next = p->next;
    if(p->next != NULL)
        p->next->prior = s;
    s->prior = p;
    p->next = s;
    //[4]插入成功,返回结果
    return 1;
}

3.4双链表节点删除

//按位删除
bool DLinkLisDelete(DLinkList L,int i){
    //[1]判空
    if(L->next == NULL){
        printf("表空,无删除元素\n");
        return false;
    }

    //[2]定位,找到待删除元素的前一位*p和待删除元素*q
    DNode* p = GetElem(L,i-1);
    if(p == NULL) return false;
    DNode* q = p->next;
    if(q == NULL) return false; //*p没有后继结点
    //[3]执行删除操作
    p->next = q->next;
    if(q->next != NULL)         //*q不是最后一位元素
        q->next->prior = p;
    free(q);
    return true;
    //没有q的写法
    // if(p->next == NULL) false;  //最后一位,没有后继
    // p->next = p->next->next;
    // if(p->next->next != NULL)   //待删除结点不是最后一个节点
    //     p->next->next->prior = p;
    // free(p);              //释放结点空间
    // return true;
}
//删除双链表
void DestoryList(DLinkList L){
    while (L->next != NULL){
        DLinkLisDelete(L,1);
    }
}

3.5双链表的遍历

/*双链表的遍历*/
int PrintDLinkList(DLinkList L){
    //[1]判空
    if (L == NULL){
        printf("该链表不存在\n");
        return 0;
    }
    
    if(L->next == NULL){
        printf("这是空表!没有需要打印的元素");   
        return 0;
    }
    //[2]生成当前要打印的元素位置的指针p
    DNode* p = L->next;
    //[3]遍历打印
    int i = 0;
    while (p!= NULL){
        i++;
        printf("第%d个元素的内容值为:%d\n",i,p->data);
        p = p->next;
        
    }
    printf("打印结束,共%d个链表结点.\n",i);
    return 0;
}


4.完成代码 

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

//C语言自定义bool操作
#define bool char
#define false 0
#define true 1

/*数据元素类型*/
typedef int ElemType;

/*双链表结构体定义*/
typedef struct DNode{        //定双链表结点类型
    ElemType data;          //每个结点存放一个数据元素
    struct DNode *next;     //指针指向下一节点
    struct DNode *prior;    //指针指向上一节点
}DNode,*DLinkList;

/*双链表初始化函数*/
DLinkList DLinkListInit(){
    //[1]申请空间
    DLinkList L = (DLinkList)malloc(sizeof(DLinkList));
    //[2]安排指针
    L->next = NULL;
    L->prior = NULL;
    //[3]返回头节点
    return L;
}

/*双链表的插入操作*/
//【1】双链表新节点的创建,返回新节点指针
DNode* CreateNewDNode(ElemType data){
    //[1]为新节点分配空间,并判断是否分配成功
    DNode* newNode = (DNode*)malloc(sizeof(DNode));
    if(!newNode){
        printf("分配空间失败,请检查内存!\n");
        return NULL;
    }
    //[2]初始化新节点
    newNode->data = data;
    newNode->next = NULL;
    newNode->prior = NULL;
    //[3]返回节点地址
    return newNode;
}
//【2】双链表节点定位----按位序号查询,返回需要查找的节点的指针
DNode* GetElem(DLinkList L, int i){
    //[1]判空
    if(L->next == NULL) {printf("这是空表!"); return L;}
    //[2]生成指向目标节点的指针p
    DNode* p = L->next;
    //[3]如果i = 0
    if(i < 1){
        p = L;
        return p;
    }
    //[4]开始循环定位
    while (p->next != NULL && i != 1){
        p = p->next;
        i--;
    }
    //[5]返回节点
    if(i != 1)
    printf("没有这个节点,返回链表的最后一位.\n");
    return p;
}
//【3】双链表节点定位----按内容值查询,返回需要查找的节点的指针
DNode* LocateElem(DLinkList L,ElemType e){
    //[1]判空
    if(L->next == NULL) {printf("这是空表!"); return L;}
    //[2]生成指向目标节点的指针p
    DNode* p = L->next;
    //[3]开始循环定位
    while (p->next != NULL && p->data != e){
        p = p->next;
    }
    //[4]返回节点
    if(p->data != e)//排除最后一位元素是目标元素的情况
    printf("没有这个节点,返回链表的最后一位.");
    return p;
}
//【4】双链表的插入操作
//在第i位插入新元素,如果第i位不存在,则插入在表尾
bool DLinkListInsert(DLinkList L,int i,ElemType e){
    //[0]程序健壮性增强
    if(i < 1){
        printf("输入出错!");
        return 0;
    }
    //[1]建立新元素节点
    DNode* s = CreateNewDNode(e);
    if(s == NULL) return 0;
    //[2]生成指向待插入位置的指针p
    DNode* p = GetElem(L,i-1);
    //[3]插入新节点
    s->next = p->next;
    if(p->next != NULL)
        p->next->prior = s;
    s->prior = p;
    p->next = s;
    //[4]插入成功,返回结果
    return 1;
}

/*双链表的遍历*/
int PrintDLinkList(DLinkList L){
    //[1]判空
    if (L == NULL){
        printf("该链表不存在\n");
        return 0;
    }
    
    if(L->next == NULL){
        printf("这是空表!没有需要打印的元素");   
        return 0;
    }
    //[2]生成当前要打印的元素位置的指针p
    DNode* p = L->next;
    //[3]遍历打印
    int i = 0;
    while (p!= NULL){
        i++;
        printf("第%d个元素的内容值为:%d\n",i,p->data);
        p = p->next;
        
    }
    printf("打印结束,共%d个链表结点.\n",i);
    return 0;
}

/*双链表的删除操作*/
//按位删除
bool DLinkLisDelete(DLinkList L,int i){
    //[1]判空
    if(L->next == NULL){
        printf("表空,无删除元素\n");
        return false;
    }
    //[2]定位,找到待删除元素的前一位*p和待删除元素*q
    DNode* p = GetElem(L,i-1);
    if(p == NULL) return false;
    DNode* q = p->next;
    if(q == NULL) return false; //*p没有后继结点
    //[3]执行删除操作
    p->next = q->next;
    if(q->next != NULL)         //*q不是最后一位元素
        q->next->prior = p;
    free(q);
    return true;
    //没有q的写法
    // if(p->next == NULL) false;  //最后一位,没有后继
    // p->next = p->next->next;
    // if(p->next->next != NULL)   //待删除结点不是最后一个节点
    //     p->next->next->prior = p;
    // free(p);              //释放结点空间
    // return true;
}
//删除双链表
void DestoryList(DLinkList L){
    while (L->next != NULL){
        DLinkLisDelete(L,1);
    }
}




int main(){
    //[1]不带头结点的声明定义双链表
    DLinkList L = NULL;
    //[2]带头节点的声明定义双链表
    L = DLinkListInit();
    //[3]测试:插入结点
    DLinkListInsert(L,1,1);
    DLinkListInsert(L,1,2);
    DLinkListInsert(L,1,3);
    DLinkListInsert(L,1,4);
    DLinkListInsert(L,1,5);
    DLinkListInsert(L,1,6);
    DLinkListInsert(L,1,7);
    DLinkListInsert(L,1,8);
    DLinkListInsert(L,1,9);
    DLinkListInsert(L,1,10);
    DLinkListInsert(L,1,11);
    DLinkListInsert(L,1,12);
    PrintDLinkList(L);
    DLinkListInsert(L,4,888);
    DLinkListInsert(L,7,1022);
    DLinkListInsert(L,9,963);
    DLinkListInsert(L,11,8522);
    DLinkListInsert(L,100,150);
    DLinkListInsert(L,4,12000);
    DLinkListInsert(L,8,17869);
    PrintDLinkList(L);
    DLinkLisDelete(L,8);
    PrintDLinkList(L);
    DestoryList(L);
    L = NULL;
    PrintDLinkList(L);
    // DNode* node = LocateElem(L,150);
    // printf("Node.data = %d",node->data);
return 0;
}

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

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

相关文章

【计算机视觉】CVPR 23 | 视觉 Transformer 全新学习范式!用长尾数据提升ViT性能

文章目录 一、导读二、介绍三、方法四、总结 一、导读 论文地址&#xff1a; https://arxiv.org/abs/2212.02015代码链接&#xff1a; https://github.com/XuZhengzhuo/LiVT二、介绍 在机器学习领域中&#xff0c;学习不平衡的标注数据一直是一个常见而具有挑战性的任务。近…

JDK自带的构建线程池的方式之newScheduleThreadPool

顾名思义newScheduleThreadPool是一个定时任务的线程池&#xff0c;这个线程池可以定时一定周期去执行任务&#xff0c;也可以实现延迟多久去执行任务一次。 newScheduleThreadPool方法实现展示 基于查看这个方法不难发现&#xff0c;该方法是基于ScheduledThreadPoolExecutor…

基于 Flink CDC 构建 MySQL 到 Databend 的 实时数据同步

这篇教程将展示如何基于 Flink CDC 快速构建 MySQL 到 Databend 的实时数据同步。本教程的演示都将在 Flink SQL CLI 中进行&#xff0c;只涉及 SQL&#xff0c;无需一行 Java/Scala 代码&#xff0c;也无需安装 IDE。 假设我们有电子商务业务&#xff0c;商品的数据存储在 My…

【深度学习】5-5 与学习相关的技巧 - 超参数的验证

超参数指的是&#xff0c;比如各层的神经元数量、batch大小、参数更新时的学习率或权值衰减等。如果这些超参数没有设置合适的值&#xff0c;模型的性能就会很差。 那么如何能够高效地寻找超参数的值的方法 验证数据 之前我们使用的数据集分成了训练数据和测试数据&#xff0c…

WorkPlus AI助理正式上线!为企业打造定制化的AI私有助理

毋庸置疑&#xff0c;ChatGPT的应用充满无限的想象空间。但对于企业来说&#xff0c;使用时面临的最核心的问题就是“存在回答准确性不足”的弊端。那企业都想要通过GPT构建内容生态&#xff0c;在数字化时代保持行业领先地位。 企业都想要结合行业属性、业务需求等自身特点打…

【Flutter】Flutter 数据存储 Hive 的简要使用说明

文章目录 一、前言二、Hive 包的版本号三、Hive 简介1. Hive 是什么&#xff1f;2. Hive 的特点 四、Hive 的基本使用1. Hive 的安装2. Hive 的初始化3. 创建和打开 Hive 数据库4. 数据的存储和读取5. 数据的删除 五、总结 一、前言 &#x1f389;想要精通 Flutter&#xff0c…

是时候扔掉cmder, 换上Windows Terminal

作为一个Windows的长期用户&#xff0c;一直没有给款好用的终端&#xff0c;知道遇到了 cmder&#xff0c;它拯救一个习惯用Windows敲shell命令的人。 不用跟我安利macOS真香&#xff01;公司上班一直用macOS&#xff0c;一方面确实更加习惯windows下面学习, 另一方面是上课需要…

Phantomjs实现后端将URL转换为图片

PhantomJS简介 PhantomJS is a command-line tool. – 其实就是一个命令行工具 PhantomJS的下载地址&#xff1a; Windows:phantomjs-2.1.1-windows.zip Linux:phantomjs-2.1.1-linux-x86_64.tar.bz2;phantomjs-2.1.1-linux-i686.tar.bz2 MacOS:phantomjs-2.1.1-macosx.zip…

西门子Mendix 入门 2

今天还是一直下载失败&#xff0c;就算成功了&#xff0c;速度也只有几K&#xff0c;于是使用翻墙软件&#xff0c;最终下载成功 下载成功后重新点击edit in studio pro 出现如下页面 首先先关闭安全性 进行添加任务和管理任务 点击上方绿色箭头后点击View App 出现如下页面…

ESP32-WROOM-32 UDP单播透传AT指令例程

ESP32-WROOM-32 AT指令配置TCP通讯 ESP32-WROOM-32前言固件烧录测试AT指令UDP单播通讯\透传ESP32配置SoftAPESP32与手机间的UDP通讯与透传普通传输模式演示UDP透传演示 ESP32-WROOM-32 前言 上次演示了ESP32与手机的三种TCP连接与数据传输方法&#xff0c;现在接着上一篇“ESP…

第二章 数据结构(一)——链表,栈和队列与kmp

文章目录 链表栈和队列表达式运算 单调栈单调队列kmp链表练习题826. 单链表827. 双链表 栈和队列练习题828. 模拟栈3302. 表达式求值829. 模拟队列830. 单调栈154. 滑动窗口 kmp练习题831. KMP字符串 kmp虐我一下午 链表 若用链式结构实现链表&#xff0c;效率低&#xff0c;因…

软件开发流程

目录 软件软件开发流程的演变 瀑布模型敏捷模型 XPSCRUMDevOps 1.软件 与计算机系统操作有关的计算机程序、可能有的文件、文档及数据。 软件可以分为两种主要类型&#xff1a; 独立软件&#xff1a;独立软件是一种完整的应用程序&#xff0c;可以直接在计算机或移动设备上…

Android系统安全 — 6.2 Ethernet安卓架构

1. Android Ethernet架构介绍 整个Ethernet系统架构如下图所示&#xff1a; 以太网服务&#xff08;EthernetService&#xff09;的启动与注册流程&#xff1b;应用层调用使能ethernet功能的方法流程来分析&#xff0c;从应用层如何将指令一步一步传到底层kernel&#xff1b;…

SAAS-HRM系统概述与搭建环境

SAAS-HRM系统概述与搭建环境 学习目标&#xff1a; 理解SaaS的基本概念 了解SAAS-HRM的基本需求和开发方式掌握Power Designer的用例图 完成SAAS-HRM父模块及公共模块的环境搭建完成企业微服务中企业CRUD功能 初识SaaS 云服务的三种模式 IaaS&#xff08;基础设施即服务…

使用Windows To Go工具制作你的U盘系统【含下载Windows10系统镜像】亲测已成功23.06.21

WinToGo是一款辅助工具&#xff1a;专为能够让你将系统装进U盘&#xff0c;移动硬盘里&#xff0c;让你在任意电脑都能运行U盘里装的系统&#xff01; 一、下载&#xff0c;安装“Windows To Go”工具 1、下载Windows To Go工具 口袋系统WinToGo: 安装Win 10到U盘 2、双击Wi…

从0到1精通自动化测试,pytest自动化测试框架,assert断言(七)

目录 一、前言 二、assert 三、异常信息 四、异常断言 五、常用断言 一、前言 断言是写自动化测试基本最重要的一步&#xff0c;一个用例没有断言&#xff0c;就失去了自动化测试的意义了。什么是断言呢&#xff1f; 简单来讲就是实际结果和期望结果去对比&#xff0c;符…

三分钟学习一个python小知识2-----------我的对python的类(Class)和对象(Object)的理解

文章目录 一、类&#xff08;Class&#xff09;和对象&#xff08;Object&#xff09;是什么&#xff1f;二、Python类和对象的实现1.定义类2.创建对象3.调用类的属性和方法 三、利用python实现了一个动物的类&#xff08;Animal&#xff09;和其两个子类&#xff08;Cat和Dog&…

年轻人存款难,要攒够多少存款才可以体面的养老,结论亮了

这个情况确实值得我们思考。年轻人的经济压力比较大,所以他们普遍存款比较少。而10万元确实是一个比较大的数目,对于一些年轻人来说可能确实很难达到。 然而,我认为这并不是一个“坎”。我们应该鼓励年轻人理财,增加存款,以便应对未来可能出现的各种经济问题。同时,我们…

定义一个一维数组存放10个整数,要求从键盘输入10个数,对其进行求和、求平均、求最大值/最小值及其位置的下标

目录 题目 分析思路 法一&#xff1a;在主函数直接编程 法二&#xff1a;用 调用函数 实现 代码 法一&#xff1a;在主函数直接编程 法二&#xff1a;用 调用函数 实现 题目 定义一个一维数组存放10个整数&#xff0c;要求从键盘输入10个数&#xff0c;对其进行求和、求…

新华三H3C无线控制器AC对接网络准入实现定制化Portal短信认证

随着企业办公信息化的不断发展&#xff0c;企业内网安全也面临着诸多挑战。在包含了无线 WiFi、有线网络的混合网络环境中&#xff0c;员工或访客、外包人员、合作伙伴等用户在接入网络时&#xff0c;如果无需进行身份验证及访问权限的管理&#xff0c;则很可能给不法分子可乘之…