王道数据结构源码实战自学-链表(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

源码下载

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不会挂科i

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

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

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

打赏作者

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

抵扣说明:

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

余额充值