前言

上一篇文章中我们学习了单链表的实现 本节将带大家学习带头双向链表的实现,希望能够对大家有一些帮助,话不多说,直接开干!!


文章目录

  • 前言
  • 一、带头双向链表的概念和结构
  • 二. 带头双向链表的接口实现
    • 2.1 打印
    • 2.2 初始化
    • 2.3 开辟节点
    • 2.4 尾部插入节点
    • 2.5 尾部删除节点
    • 2.6 头部插入节点
    • 2.7 头部删除节点
    • 2.8 查找
    • 2.9 在Pos位置前插入
    • 2.10 在Pos位置删除
    • 2.11 销毁链表
  • 总结

一、带头双向链表的概念和结构

概念:带头双向链表是一种 带不存储数据的哨兵卫,循环,双向的一种链表结构。它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。
特点:

  • 在数据结构中具有双向指针
  • 插入数据的时候方便找到上一个
  • 同样,删除数据的是有也需要考虑前后方向的操作

和单链表的比较

1.无头单向非循环链表:结构简单,但一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶,图的邻接表等等。另外,这种结构在笔试中出现很多。。
2.带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外,这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单。后面我们代码实现了就知道了。

看代码

typedef int LTDataType;
typedef struct ListNode
{LTDataType data;struct ListNode* next;struct ListNode* prev;
}ListNode;

首先我们定义了一个结构体,结构体里的data被叫做数据域用来存放数据,next是指针域用来存放下一个节点的地址,prev是指针域用来存放上一个节点的地址。

二. 带头双向链表的接口实现

我们要实现如下的一些接口:

// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode** pHead);
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);

2.1 打印

*注意:头指针的位置不要轻易的去改动,所以在这定义了一个Cur来代替我们的头指针。

代码示例如下

// 单链表打印
void ListPrint(ListNode* pHead)
{assert(pHead);ListNode* cur=pHead->next;while(cur!=pHead) //这个条件很重要的{printf("%d->",cur->data);cur=cur->next;}
printf("NULL\n");
}

2.2 初始化

  • 注意:带头双向链表的初始化 需要创建一个哨兵卫的头指针,该位置不存储有效数据 。
// 创建返回链表的头结点.
ListNode* ListCreate()
{ListNode* phead=BuySListNode(-1);phead->next=phead;phead->prev=phead;return phead;
}

2.3 开辟节点

**当我们插入的时候需要开辟新的节点,而我们有头插、尾插和任意位置插,每次写这些插入的时候都要<br />
申请节点,所以我们不妨写一个函数就直接申请节点,后面只需要调用就好了**

代码实现:

LTDataType* BuySListNode(LTDataType x)
{LTDataType* newnode=(LTDataType*) malloc(sizeof(ListNode));assert(newnode);newnode->data=x;newnode->next=NULL;newnode->prev=NULL;return newnode;
}

2.4 尾部插入节点

要尾插的话,我们首先得要开辟一个结点才能插啊,其次是要找到尾结点,那么如何找到尾结点呢?很简单,一个while循环就搞定了。
代码示例如下:

// 单链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{ListNode* newNode = BuySListNode(x);// 先不断开原链表,然后新节点连接到链表中newNode->prev = pHead->prev;newNode->next = pHead;newNode->prev->next = newNode;pHead->prev = newNode;
}

2.5 尾部删除节点

示例代码如下:

void ListPopBack(ListNode* pHead)
{assert(pHead);assert(pHead->next!=pHead);
ListNode* cur=pHead->next;while(cur!=pHead){ListNode* prev=cur;cur=cur->next;}prev->next=pHead;pHead->next=prev;free(cur);
}

2.6 头部插入节点

示例代码如下:

void ListPushFront(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* newnode=BuySListNode( x);ListNode* next=pHead->next;pHead->next=newnode;newnode->next=next;newnode->prev=pHead;next->prev=newnode;
}

2.7 头部删除节点

示例代码如下:

void ListPopFront(ListNode* pHead)
{assert(pHead);assert(pHead->next!=pHead);ListNode* next=pHead->next;pHead->next=next->next;next->next->prev=pHead;free(next);
}

2.8 查找

// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* cur=pHead->next;while(cur!=pHead){if(cur->data==x){return cur;}cur=cur->next;}return NULL;
}

2.9 在Pos位置前插入

示例代码如下:

// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{assert(pos);ListNode* newnode=BuySListNode(x);ListNode* prev=pos->prev;prev->next=newnode;newnode->next=pos;newnode->prev=prev;pos->prev=newnode;
}

2.10 在Pos位置删除

示例代码如下:

// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{assert(pos);ListNode* next=pos->next;ListNode* prev=pos->prev;prev->next=next;next->prev=prev;free(pos);
}

2.11 销毁链表

示例代码如下:

// 双向链表销毁
void ListDestory(ListNode** pHead)
{assert(phead);ListNode* cur=(*pHead)->next;while(cur!=*pHead){ListNode* next=cur->next;free(cur);cur=next;}free(*pHead);*pHead=NULL;
}

总结

List.h完整代码:

#pragma once//防止头文件重复包含#include <stdio.h>
#include <stdlib.h>
#include <assert.h>// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{LTDataType data;struct ListNode* next;struct ListNode* prev;
}ListNode;// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode** pHead);
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);

List.c
完整代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"
// 动态申请一个节点LTDataType* BuySListNode(LTDataType x){LTDataType* newnode=(LTDataType*) malloc(sizeof(ListNode));assert(newnode);newnode->data=x;newnode->next=NULL;newnode->prev=NULL;return newnode;
}// 创建返回链表的头结点.ListNode* ListCreate()
{ListNode* phead=BuySListNode(-1);phead->next=phead;phead->prev=phead;return phead;
}// 双向链表销毁void ListDestory(ListNode** pHead)
{assert(phead);ListNode* cur=(*pHead)->next;while(cur!=*pHead){ListNode* next=cur->next;free(cur);cur=next;}free(*pHead);*pHead=NULL;
}// 双向链表打印
void ListPrint(ListNode* pHead)
{assert(pHead);ListNode* cur=pHead->next;while(cur!=pHead) //这个条件很重要的{printf("%d->",cur->data);cur=cur->next;}
printf("NULL\n");
}
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{ListNode* newNode = BuySListNode(x);// 先不断开原链表,然后新节点连接到链表中newNode->prev = pHead->prev;newNode->next = pHead;newNode->prev->next = newNode;pHead->prev = newNode;
}
// 双向链表尾删void ListPopBack(ListNode* pHead){assert(pHead);assert(pHead->next!=pHead);
ListNode* cur=pHead->next;while(cur!=pHead){ListNode* prev=cur;cur=cur->next;}prev->next=pHead;pHead->next=prev;free(cur);
}// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* newnode=BuySListNode( x);ListNode* next=pHead->next;pHead->next=newnode;newnode->next=next;newnode->prev=pHead;next->prev=newnode;
}// 双向链表头删
void ListPopFront(ListNode* pHead)
{assert(pHead);assert(pHead->next!=pHead);ListNode* next=pHead->next;pHead->next=next->next;next->next->prev=pHead;free(next);
}// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* cur=pHead->next;while(cur!=pHead){if(cur->data==x){return cur;}cur=cur->next;}return NULL;
}// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{assert(pos);ListNode* newnode=BuySListNode(x);ListNode* prev=pos->prev;prev->next=newnode;newnode->next=pos;newnode->prev=prev;pos->prev=newnode;
}// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{assert(pos);ListNode* next=pos->next;ListNode* prev=pos->prev;prev->next=next;next->prev=prev;free(pos);
}

码字不易,如果觉得内容有用的话,就给博主三连吧!!!

数据结构-带头双向链表的建立,详细教程相关推荐

  1. 【数据结构】双向链表(带头双向循环链表)——超详细代码

    文章目录 1. 双链表 1.1 前言 1.2 带头双向循环链表 2. 带头双向循环链表的实现 2.1 双向链表的定义声明 2.2 双向链表的初始化 2.3 释放双向链表 2.4 打印双向链表 2.5 ...

  2. 七牛云详细教程(包含与阿里云建立连接)

    七牛云详细教程(包含与阿里云建立连接) 1.七牛云简介 不管是设计师,还是开发者,亦或是个人.公司.我们有时会需要将图片存在网络上,然后用链接来分享给他人.或是,用来给网站做图片外链,通过CDN加速, ...

  3. ramdisk和linux PE,PE下建立Ramdisk盘的详细教程

    如何在PE下建立一个Ramdisk盘呢?之前我们有介绍过如何在PE下安装系统ghost,有看过教程的朋友应该都会安装了吧.但是如果要在PE下建立一个Ramdisk盘,要如何建立呢?今天U大侠小编就和大 ...

  4. 【数据结构】一文带你学会带头双向链表

    目录 前言 链表的实现 List.h List.c ListCreate() LTInit() ListPushBack() ListPopBack() ListPrint() ListPushFro ...

  5. 如何建立一个网站,可用互联网访问?(原创详细教程)

    总体需要准备的东西: web服务器/虚拟主机. 域名. 网页源码. FTP上传下载工具. 数据库管理软件.(若搭建静态网站则不需要) 注:在此过程中,重点需要将域名解析到服务器,服务器与域名绑定. 详 ...

  6. c语言数组指定位置插入和删除_玩转C语言链表,单链表/双向链表的建立/遍历/插入/删除...

    最近临近期末的C语言课程设计比平时练习作业一下难了不止一个档次,第一次接触到了C语言的框架开发,了解了View(界面层).Service(业务逻辑层).Persistence(持久化层)的分离和耦合, ...

  7. Ubuntu16.04在线安装MongoDB详细教程

    Ubuntu16.04在线安装MongoDB详细教程 文章目录 Ubuntu16.04在线安装MongoDB详细教程 前言 安装流程 信任MongoDB公钥 创建列表文件 安装MongoDB 常见问题 ...

  8. Linux-安装MongoDB(详细教程)

    文章目录 前言 一.概述 二.下载 三.安装与启动 四.连接 五.可能会遇到的问题 前言 MongoDB 是一个基于分布式文件存储的数据库,主要用于为 web 应用提供可扩展的高性能数据存储解决方案. ...

  9. Flink 教程 gitbook 从入门到入土(详细教程)

    Flink从入门到入土(详细教程) 和其他所有的计算框架一样,flink也有一些基础的开发步骤以及基础,核心的API,从开发步骤的角度来讲,主要分为四大部分 1.Environment Flink J ...

最新文章

  1. 腾讯云 已连接到实验云主机 linux 运维基本操作
  2. 剑指offer:剪绳子
  3. SQLiteOpenHelper的实现
  4. python判断字符串
  5. 华为mate40RS能升级鸿蒙,mate40Pro和40RS能用上鸿蒙系统吗
  6. 【计算机组成原理】磁盘存储器
  7. PAT乙级(1020 月饼)
  8. 【Elasticsearch】Elasticsearch 缓存深度剖析:一次提高一种缓存的查询速度
  9. Mqtt 客户端 java API 教程
  10. 最详细的jsp基础教程
  11. 学术规范与论文写作(期末考试答案)(方便检索版)
  12. Android----banner使用详解
  13. PC微信最新版HOOK接口3.7.6.44
  14. HTML的两种盒子模型
  15. 数学天才破解世界级难题,23岁被聘为正教授,丘成桐:运气好而已
  16. 通信电子电路(二十一) 第二章 知识点总结+作业分析
  17. PJBLOG首页调用
  18. 安装Django4.0最详细教程 pip总是报错怎么办
  19. 通过压力测试提升服务器并发性能实例
  20. 【最终版】PyQt5 自定义标题栏,实现无边框,最小化最大化关闭事件,窗口拖动移动,窗口改变大小,仿百度网盘色调美化,添加内容窗口

热门文章

  1. C#开发之——Mutex(14.8)
  2. 设计模式之静态代理模式
  3. js选项卡 php,JS实现选项卡实例详解_javascript技巧
  4. 【转】自使用linux常用命令练习题
  5. 8岁女儿写代码哄程序员爸爸开心,网友直呼:破防了
  6. Vue抛 Property or method turn is not defined on the instance but referenced during render. 的解决方法
  7. JavaScript BOM操作
  8. 第7节 代码添加书签/快速查找/定位
  9. TOTAL COMMANDER 7 ( TC7 ) 的遺憾
  10. ext-js 中 Ext.data.Model 的 phantom 属性的讨论