2-4 线性表之双链表
时间:2022-06-24
本文章向大家介绍2-4 线性表之双链表,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
双向链表除了相当于在单链表的基础上,每个结点多了一个指针域prior,用于存储其直接前驱的地址。同时保留有next,用于存储其直接后继的地址。
所以对于带头结点的双链表,其实很多操作都和 带头结点的单链表是一样的,因为你完全可以忽视掉它有个 prior指针,这样就可以当做单链表来使用。所以,这里只写一下定义和初始化的过程,其他的操作基本可以参照带头结点的单链表。
另外因为有了prior指针,所以在插入和删除新元素时,应该考虑到这一点,
所以如果在队尾插入元素时,就不用改动后面指针的prior指针,因为后面为空,如果在其他位置插入,就需要改动;
删除操作也是一样。
1、结点的结构
typedef struct Dul_node { int data; Dul_node *prior; Dul_node * next; } dul_node;
2、初始化函数
void InitList(dul_node **h) {
(*h) = new dul_node; (*h)->next = nullptr; (*h)->prior = nullptr; }
一般来说,我们都是用双链表来构造循环链表。
3.头文件:
#ifndef DUL_LINK_LIST_H_
#define DUL_LINK_LIST_H_
typedef struct Dul_node
{
int data;
struct Dul_node *prior;
struct Dul_node * next;
} dul_node;
void InitList(dul_node **h);
void DestroyList(dul_node **h);
void CreateList(dul_node **h);
int LenList(dul_node *h);
void ShowList(dul_node *h);
int GetIndex(dul_node *h, int k);
void InsertElem(dul_node *h, int k, int x);
void DeleteElem(dul_node *h, int k);
#endif // !DUL_LINKLIST_H_
4.函数定义文件
#include<iostream>
#include"dul_link_list.h"
using std::cin;
using std::cout;
using std::endl;
/*初始化*/
void InitList(dul_node **h) {
(*h) = new dul_node;
(*h)->next = nullptr;//直接后继为空
(*h)->prior = nullptr;//直接前驱也为空
}
/*销毁链表*/
void DestroyList(dul_node **h) {
dul_node *p;
while ((*h) != nullptr) {
p = (*h)->next;
delete (*h);
(*h) = p;
}
}
void CreateList(dul_node **h) {
/*初始化*/
(*h) = new dul_node;
(*h)->next = nullptr;
(*h)->prior = nullptr;
/**/
dul_node *p = (*h);
int x = 0;
cout << "n请依次输入数据以创建链表,以 q 为停止信号!" << endl;
while (cin >> x) {
/*由于是尾插法,所以首先后继是空,但是前驱先不指定值*/
dul_node *s = new dul_node;
s->data = x;
s->next = nullptr;
while (p->next != nullptr)
p = p->next;
p->next = s;
s->prior = p;//在这里讲新元素的前驱 赋值为原来的最后一个元素的地址
}
cin.clear();
while (cin.get() != 'n')
continue;
}
int LenList(dul_node *h) {
dul_node *p = h;
int j = 0;
while (p->next != nullptr) {
p = p->next;
j++;
}
return j;
}
void ShowList(dul_node *h) {
dul_node *p = h;
int j = 0;
while (p->next != nullptr) {
p = p->next;
j++;
cout << "List[" << j << "]=: " << p->data << " ";
}
if (j == 0)
cout << endl << "n这是一个空链表" << endl;
cout << endl;
}
int GetIndex(dul_node *h, int k) {
dul_node *p = h;
if (h->next == nullptr) {
cout << "n链表为空,不执行查找操作!n" << endl;
return NAN;
}
int j = 0;
while (p->next != nullptr && j < k) {
p = p->next;
j++;
}
if (j != k || k<1) {
cout << "输入的位置 " << k << " 不合理!n" << endl;
return NAN;
}
return p->data;
}
void InsertElem(dul_node *h, int k, int x) {
dul_node *p = h;
int j = 0;
while (p->next != nullptr && j < k - 1) {
p = p->next;
j++;
}
if (j != k - 1) {
cout << "想要插入的位置_" << k << "_不合理!" << endl;
return;
}
dul_node *s = new dul_node;
s->data = x;
if (p->next != nullptr)
p->next->prior = s;//如果p还有后继元素,则其前驱改为s
s->next = p->next; //s的后继改为p的后继
//将s插入p后
p->next = s;// p的后继改为s
s->prior = p;//s的前驱改为p
}
void DeleteElem(dul_node *h, int k) {
if (h->next == nullptr) {
cout << "n链表为空,不执行删除操作!" << endl;
return;
}
dul_node *p = h;
int j = 0;
while (p->next != nullptr && j < k - 1) {
p = p->next;
j++;
}
if (j != k - 1 || p->next == nullptr) {
cout << "想要删除的位置_" << k << "_不合理!不执行删除操作 " << endl;
return;
}
dul_node *s = p->next;//s指向待删除元素
p->next = s->next;//p的后继改为s的后继
if(s->next!=nullptr)
s->next->prior = p;//s的后继的前驱改为p
delete s;
}
5.主函数文件
#include<iostream>
#include"dul_link_list.h"
using std::cin;
using std::cout;
using std::endl;
int main() {
dul_node *list1, *list2;
InitList(&list1);
cout << "nlength of list1 is: " << LenList(list1) << endl;
/*用Insert方法依次在被初始化过的链表list1尾部插入新元素,以此创建链表*/
cout << "nPlease input the int number:n";
int x1;
for (int k = 1; k < 9; k++) {
cin >> x1;
InsertElem(list1, k, x1);
}
cin.get();
cout << "nthe length of list1: " << LenList(list1) << endl;
ShowList(list1);
/*也可以用我写的那个Create程序创建新链表,但是要注意一点:
我那个程序是针对没有被初始化过的链表指针,因为那个函数里面有初始化语句,
所以如果你输入一个已经被初始化过的链表,哪怕是空链表,的头指针,也会有个问题存在,
那就是头指针的值被更新为 程序中使用 new创建的那个内存块的地址,但是你又没有释放原来头指针指向的内存块的地址,
这样不符合程序规定,容易造成溢出, 所以应该使用没有被初始化过的链表指针,比如此程序中的list2*/
CreateList(&list2);
cout << "nThe length of list2: " << LenList(list2) << endl;
ShowList(list2);
/*删除函数测试*/
cout << "nnow test the Delete funtion, every we turn delete the 1st element of the list:n";
for (int i = LenList(list2); i >0; i--) {
DeleteElem(list2, 1);
ShowList(list2);
}
cin.get();
DestroyList(&list1);
DestroyList(&list2);
return 0;
}
若有错误,请不吝指出,谢谢
- Hadoop离线数据分析平台实战——520项目总结Hadoop离线数据分析平台实战——520项目总结
- 爬虫入门到精通-mongodb的基本使用
- 拒绝撕逼,用数据来告诉你选择器到底哪家强
- 爬虫入门到精通-headers的详细讲解(If-modified-since)
- Linux配置网卡
- 【爬虫军火库】生成指定日期间的日期列表
- 手把手教你安装大数据开发测试环境手把手教你安装大数据开发测试环境
- Humble Numbers(丑数) 超详解!
- 1284 2 3 5 7的倍数
- 爬虫入门到精通-爬虫之异步加载(实战花瓣网)
- 【爬虫军火库】Chrome F12使用Network分析异步请求
- 1305 Pairwise Sum and Divide
- mysql分布式数据库中间件对比mysql分布式数据库中间件对比
- 爬虫入门到精通-headers的详细讲解(模拟登录知乎)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 只要30行代码!7步教会你Python爬取网页抖音热门视频
- python苦短-CV2
- python实现语音在线合成,让你的小说自己念给你听
- Python爬虫实战:模拟登录淘宝网站
- Python制作动态二维码(附源码,复制再贴修改路径名字即可)
- 新手小白的福利,零基础也能上手的项目——学生信息管理系统
- Python制作小脚本,一键可以让你同事的电脑在你指定时间关机
- Python小白爬虫入门的第一个案例:爬取全站小说
- Python基础入门知识点——字符串的介绍
- 文章要保存为TXT文件,其中的图片要怎么办?Python帮你解决
- 分析B站弹幕,川普同志暴露的那一天,没有一个鬼畜up是无辜的
- python-爬取地理坐标
- Python基础第一个案例:猜数字游戏,这个都写不出,那就放弃吧
- 现在听歌要各大平台到处跑,嫌麻烦?制作个人专属的音乐下载器
- 爬取上市公司数据、分析数据,并用可视化现实全国各地区公司数量