王道数据结构源码实战自学-链表(C++版)

王道数据结构源码实战自学-链表(C++版)

  • 头结点和头指针的区别

    注意:头结点里面不放数据,从a1开始才放的数据

    ​ 因为L(头指针)指向的是头结点,所以就算链表为空,头结点还存在,L就不是空指针

在这里插入图片描述

在这里插入图片描述

1 定义结构体

typedef int ElemType;
//定义结点的结构体
typedef struct LNode
{
	ElemType data;
	LNode* next;//指向下一个结点
}LNode,*LinkList;

这里使用的C语言的语法。LinkList等价于LNode *

2 头插法创建链表

//头插法创建链表
LinkList CreatList1(LinkList& L) {
	LNode* s;//用来后面创建结点
	int x;//用来后面接收结点的数据
	L = (LinkList)malloc(sizeof(LNode));//带头结点的链表
	L->next = NULL;
	cin >> x;
	//输入 3 4 5 6 7 9999(9999代表着结束)
	while (x!=9999)
	{
		s = (LNode*)malloc(sizeof(LNode));//申请一个新的空间。注意强转为 LNode * 
		s->data = x;//将读取到的值,给新空间中的data成员
		s->next = L->next;
		L->next = s;
		cin >> x;
	}
	return L;
}

写完后调试查看内存情况,输入 3 4 6 7 9999

image-20220709220114750

image-20220709220047665

因为是头插法,所以越早插入的值越靠后

3 打印链表内容

打印的方法说明。

函数里使用了L = L -> next,但是参数并不是引用的写法,所以不会修改链表本身。L是头结点没有数据,所以开始要让它等于第一个有数据的结点,然后依次遍历,直到等于NULL

//打印链表
void PrintList(LinkList L) {
	L = L->next;
	while (L != NULL) {
		cout << L->data << " ";
		L = L->next;
	}
	cout << endl;
}

在这里插入图片描述

4 尾插法创建链表

使用尾插法创建链表的时候需要创建一个尾结点的指针,告诉我们链表的尾部在哪,方便插入

因为新建链表,所以尾结点和头结点相同。如果是存在数据的链表,需要判断尾结点在哪

注意:插入完成后,记得把尾结点的next指向为NULL!!!

//尾插法创建新的链表

LinkList CreatList2(LinkList& L) {

	L = (LinkList)malloc(sizeof(LNode));
	int x;//用来接收新结点的数据
	LNode* s;//用来接收新添加的结点
	LNode* r = L;//r代表链表表尾结点,指向链表尾部。!因为是新建链表,所以尾节点和头结点相同!
	cin >> x;
	while (x != 9999) {
		s = (LNode *)malloc(sizeof(LNode));
		s->data = x;
		r->next = s;//让尾节点r指向新加入的结点
		r = s;//此时s变为新的尾节点了,更新r,使r变成最新的尾节点
		cin >> x;
	}
	r->next = NULL;//尾节点的next指针赋值为NULL
	return L;
}

在这里插入图片描述

如果没有将尾结点的next指向为NULL。会报如下错误

原因是打印函数PrintList循环遍历输出,遇到data值为null的时候停止,但是创建的时候没有将尾结点的next变为NULL,那么打印函数打印完尾结点后继续打印下一个结点。但是,下一个结点没有数据,存的是默认的0xcdcdcdcd,微软将这个地址设为不可读,就会出现读取访问权限冲突

image-20220709230514468

5 按序号查找结点值

这里需要注意的是,要创建一个结点指针变量来遍历每一个结点,接收遍历到的结点的值。

但是头结点没有data值,所以要让它从头结点的下一个结点(第一个有值的结点)开始遍历

//按序号查找结点值
LNode* GetElem(LinkList L, int i) {
	int j = 1;
	LNode* p = L->next;//让p指向第一个有数据的结点
	if (i == 0) {
		return L;
	}
	if (i<0)
	{
		return NULL;
	}
	while (p && j < i) {
		p = p->next;
		j++;
	}
	return p;
}

在这里插入图片描述

6 按值查找结点

整体思路类似按序号查找

//按值查找结点
LNode* LocateElem(LinkList L, ElemType e) {
	LNode* p = L->next;
	while (p && p->data != e) {
		p = p->next;
	}
	return p;
}

在这里插入图片描述

7 往第i个位置插入元素

关键是找到要插入位置的前一个结点

插入顺序:让新结点的next等于前一个结点的next

​ 前一个结点的next等于新结点

下面例子:

​ 构建链表(3 4 5 6 7 8),测试往第二个位置插入99,插入成功打印链表

//往第i个位置插入元素
bool ListFrontInsert(LinkList& L, int i, ElemType e) {
	LNode * p = GetElem(L, i - 1);//查找到插入位置的前一个元素结点
	if (NULL == p) {
		return false;//插入的位置不对
	}
	else
	{
		LNode* s = (LNode*)malloc(sizeof(LNode));//给新插入的结点申请空间
		s->data = e;//要插入的值放入对应空间
		s->next = p->next;
		p->next = s;
	}
	return true;
}

image-20220710135323325

8 删除第i个位置的元素

关键还是找到要删除点的前一个结点

让前一个结点的next等于要删除结点的next,再释放要删除的结点的内存

例子:

​ 新建链表(3 4 5 6 7 8),在第二个位置插入99,然后再删除第二个位置的值

//删除第i个位置的元素
bool ListDelete(LinkList& L, int i) {
	LNode* p = GetElem(L, i - 1);//找到删除元素的前一个元素
	if (NULL == p) {
		return false;
	}
	LNode* q = p->next;//获取到要删除的元素
	if (NULL == q)
	{
		return false;//要删除的元素不存在
	}
	p->next = q->next;
	free(q);//释放q的内存
	q = NULL;//为了避免野指针,考研不考察是否置为NULL
	return true;
}

image-20220710143859929

9 双向链表

考研大题还没有出过双向链表的大题

主要掌握增删查

typedef int ElemType;

typedef struct DNode {

	ElemType data;

	DNode* prior;//前驱
	DNode* next;//后继
}DNode,*DLinkList;

9.1 头插法

//头插法
DLinkList Dlist_head_insert(DLinkList& DL) {
	DL = (DLinkList)malloc(sizeof(DNode));
	DNode* s;
	int x;
	DL->prior = NULL;
	DL->next = NULL;
	cin >> x;
	while (x != 9999) {
		s = (DNode*)malloc(sizeof(DNode));
		s->data = x;
		s->next = DL->next;
		if (DL->next != NULL) {//对于最开始插入第一个结点的时候,不执行这个操作
			DL->next->prior = s;
		}
		s->prior = DL;
		DL->next = s;
		cin >> x;
	}
	return DL;
}

查看内存情况

image-20220710161645901

9.2 链表打印

//打印
void PrintDList(DLinkList DL) {
	DL = DL->next;
	while (DL)
	{
		cout << DL->data << " ";
		DL = DL->next;
	}
	cout << endl;
}

在这里插入图片描述

9.3 尾插法

//尾插法
DLinkList Dlist_tail_insert(DLinkList& DL) {
	DL = (DLinkList)malloc(sizeof(DNode));
	DNode* s;
	DNode* r;//尾指针
	r = DL;
	int x;
	cin >> x;
	while (x != 9999) {
		s = (DNode*)malloc(sizeof(DNode));
		s->data = x;
		r->next = s;
		s->prior = r;
		r = s;
		cin >> x;
	}
	r->next = NULL;
	return DL;
}

在这里插入图片描述

9.4 获取元素

//获取元素
DNode* GetElem(DLinkList DL, int i) {
	int j = 1;
	DNode* p = DL->next;
	if (i==0)
	{
		return DL;
	}
	if (i<0)
	{
		return NULL;
	}
	while (p&&j<i)
	{
		p = p->next;
		j++;
	}
	return p;
}

在这里插入图片描述

9.5 中间插入

演示在第二个位置插入66

//中间插入
bool DListFrontInsert(DLinkList& DL, int i, ElemType e) {
	DNode* p = GetElem(DL, i - 1);
	if (NULL==p)
	{
		return false;

	}
	DNode* s = (DNode*)malloc(sizeof(DNode));
	s->data = e;
	s->next = p->next;
	p->next->prior = s;
	p->next = s;
	s->prior = p;
	return true;
}

在这里插入图片描述

9.6 删除第i个结点

这里模拟删除第二个结点

//删除第i个结点
bool DListDelete(DLinkList& DL, int i) {
	DNode* p = GetElem(DL, i - 1);//获得前一个结点
	if (NULL == p) {
		return false;
	}
	DNode* q = p->next;//q为要删除的结点
	if (NULL == q)//要删除的元素不存在
	{
		return false;
	}
	p->next = q->next;
	if (p->next!=NULL)
	{
		q->next->prior = p;
	}
	free(q);
	return true;
}

在这里插入图片描述

10 循环链表

循环链表了解原理即可。实现的时候就是把最后一个结点的next指向头指针L

image-20220710180840558

11 循环双链表

了解原理即可。循环双链表实现起来将最后一个结点的next指向头指针,头指针的prior指向最后一个结点

image-20220710181233316

12 静态链表

考研还没有考过,现实生活中使用较少

image-20220710181432887

源码下载

内含资源如下: 1.基本数据结构 1.1.Array ........... 动态数组 1.2.LinkedList ... 链表 1.3.BST .............. 二分搜索树 1.4.MapBST ..... 二分搜索树(用于实现映射) 1.5.AVLTree ...... AVL树 2.接口 2.1.Queue ........... 队列接口 2.2.Stack .............. 栈接口 2.3.Set .................. 集合接口 2.4.Map ............... 映射接口 2.5.Merger .......... 自定义函数接口 2.6.UnionFind ..... 并查集接口 3.高级数据结构 3.1.ArrayQueue .......................... 队列_基于动态数组实现 3.2.LinkedListQueue .................. 队列__基于链表实现 3.3.LoopQueue ........................... 循环队列_基于动态数组实现 3.4.PriorityQueue ....................... 优先队列_基于最大二叉堆实现 3.5.ArrayPriorityQueue ............. 优先队列_基于动态数组实现 3.6.LinkedListPriorityQueue ..... 优先队列_基于链表实现 3.7.ArrayStack ............................. 栈_基于动态数组实现 3.8.LinkedListStack ..................... 栈_基于链表实现 3.9.BSTSet ..................................... 集合_基于二分搜索树实现 3.10.LinkedListSet ....................... 集合_基于链表实现 3.11.BSTMap ................................ 映射_基于二分搜索树实现 3.12.AVLTreeMap ....................... 映射_ 基于AVL树实现 3.13.LinkedListMap .................... 映射_基于链表实现 3.14.MaxHeap ............................. 最大二叉堆 3.15.SegmentTree ...................... 线段树 3.16.Trie ......................................... 字典树 3.17.QuickFind ............................ 并查集_基于数组实现 3.18.QuickUnion ......................... 并查集_基于树思想实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会挂科i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值