数据结构笔记第3篇:双向链表

news2024/11/18 15:38:02

1、双向链表的结构

注意:这里的 "带头" 跟前面我们说的 "头结点" 是两个概念,实际前面的在单链表阶段称呼不严谨,但是为了同学们更好的理解就直接称为单链表的头结点。

带头链表里的头结点,实际为 "哨兵位" ,哨兵位节点不存储任何有效元素,只是站在这里 "放哨的"。

"哨兵位" 存在的意义:

遍历循环链表避免死循环。

注意:双向链表中,哨兵位的下一个节点是链表的第一个节点(头结点)。哨兵位的前一个节点是链表的最后一个节点(尾结点)。所以双向链表的头插是在哨兵位的后面插入数据。尾插则是在哨兵位之前插入数据。

哨兵位是作为头结点和尾结点的中点,是头结点的起点也是尾节点的终点。这样解释更容易理解。

2、 双向链表的实现

List.h 链表函数链表节点类型的声明:

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int SLDataType;
typedef struct ListNode
{
	SLDataType data;//存储数据
	struct ListNode* prve;//存储前一个节点的地址
	struct ListNode* next;//存储下一个节点的地址
}SList;
SList* SLInit();

void SLPushBack(SList* phead, SLDataType x);//尾插
void SLPrint(SList* phead);//显示链表数据
void SLPustFront(SList* phead, SLDataType x);//头插
void SLPopBack(SList* phead);//尾删
void SLPopFront(SList* phead);//头删
void SLInsert(SList* pos, SLDataType x);//指定位置插入
SList* SLfind(SList* phead, SLDataType x);//查找节点
void SLEarse(SList* pos);//指定位置删除
void SLDestory(SList** pphead);//链表销毁

List.c 链表函数的实现:

#include "List.h"
//链表初始化
SList* SLInit()
{
	SList* phead = (SList*)malloc(sizeof(SList));
	if (phead == NULL)
	{
		perror("malloc error");
		return NULL;
	}
	phead->data = -1;
	//因为是循环链表,所以初始化要遵循循环格式
	phead->next = phead;
	phead->prve = phead;
	return phead;
}
//创建链表节点
SList* ListBuyNode(SLDataType x)
{
	SList* retNode = (SList*)malloc(sizeof(SList));
	if (retNode == NULL)
	{
		perror("malloc error");
		return NULL;
	}
	retNode->data = x;
	retNode->prve = retNode;
	retNode->next = retNode;
	return retNode;
}
//链表尾插
void SLPushBack(SList* phead, SLDataType x)
{
	assert(phead);
	SList* Node = ListBuyNode(x);
	Node->prve = phead->prve;
	Node->next = phead;
	phead->prve->next = Node;
	phead->prve = Node;
}
//链表数据显示
void SLPrint(SList* phead)
{
	assert(phead);
	SList* pcur = phead->next;
	while (pcur != phead)//哨兵位作为结束标识
	{
		printf("%d", pcur->data);
		if (pcur->next != phead)
			printf("->");
		pcur = pcur->next;
	}
	printf("\n");
}
//链表头插
void SLPustFront(SList* phead, SLDataType x)
{
	assert(phead);
	SList* Node = ListBuyNode(x);
	Node->next = phead->next;
	Node->prve = phead;
	phead->next->prve = Node;
	phead->next = Node;
}
//链表尾删
void SLPopBack(SList* phead)
{
	assert(phead);
	assert(phead->next != phead);
	SList* del = phead->prve;
	del->prve->next = phead;
	phead->prve = del->prve;
	free(del);
	del = NULL;
}
//链表头删
void SLPopFront(SList* phead)
{
	assert(phead);
	assert(phead->next != phead);
	SList* del = phead->next;
	del->next->prve = phead;
	phead->next = del->next;
	free(del);
	del = NULL;
}
//指定位置插入
void SLInsert(SList* pos, SLDataType x)
{
	assert(pos);
	SList* Node = ListBuyNode(x);
	Node->next = pos->next;
	Node->prve = pos;
	pos->next = Node;
	Node->next->prve = Node;
}
//查找节点
SList* SLfind(SList* phead, SLDataType x)
{
	assert(phead);
	SList* pcur = phead->next;
	while (pcur != phead)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}
//指定位置删除
void SLEarse(SList* pos)
{
	assert(pos);
	pos->prve->next = pos->next;
	pos->next->prve = pos->prve;
	free(pos);
	pos = NULL;
}
//链表销毁
void SLDestory(SList** pphead)
{
	assert(pphead && *pphead);
	SList* pcur = (*pphead)->next;
	while (pcur != *pphead)
	{
		SList* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	free(*pphead);
	*pphead = NULL;
}

test.c 函数的调用:

#include "List.h"

void SLtest()
{
	SList* plist = NULL;
	plist = SLInit();
	//尾插
	SLPushBack(plist, 1);
	SLPushBack(plist, 2);
	SLPushBack(plist, 3);
	SLPushBack(plist, 4);
	SLPrint(plist);//打印:1->2->3->4
	//头插
	SLPustFront(plist, 5);
	SLPustFront(plist, 6);
	SLPustFront(plist, 7);
	SLPrint(plist);//打印:7->6->5->1->2->3->4
	//尾删
	SLPopBack(plist);
	SLPrint(plist);//打印:7->6->5->1->2->3
	//头删
	SLPopFront(plist);
	SLPrint(plist);//打印:6->5->1->2->3
	//指定位置插入
	SList* find = SLfind(plist, 5);
	SLInsert(find, 11);
	SLPrint(plist);//打印:6->5->11->1->2->3
	//指定位置删除
	find = SLfind(plist, 1);
	SLEarse(find);
	SLPrint(plist);//打印:6->5->11->2->3
	//链表销毁
	SLDestory(&plist);
}
int main()
{
	SLtest();
	return 0;
}

3、顺序表和双向链表的优缺点分析

不同点顺序表链表
存储空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持O(1)不支持O(N)
任意位置插入或者删除元素可能需要搬移元素,效率低O(N)只需要修改指针指向
插入动态顺序表,空间不够时需要扩容没有容量的概念
应用场景元素高效存储+频繁访问任意位置插入和删除频繁

数据结构第3篇笔记到这里也就结束了,我们下一篇笔记再见-

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

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

相关文章

# 职场生活之道:善于团结

在职场这个大舞台上&#xff0c;每个人都是演员&#xff0c;也是观众。要想在这个舞台上站稳脚跟&#xff0c;除了专业技能&#xff0c;更要学会如何与人相处&#xff0c;如何团结他人。团结&#xff0c;是职场生存的重要法则之一。 1. 主动团结&#xff1a;多一个朋友&#x…

UE5(c++)开发日志(3):将前面写的输出日志的方法进行封装

Public下新增一个c类&#xff1a; 选择无属性&#xff0c;因为不需要添加任何东西进去, 也不需要借助里面任何东西。 创建一个命名空间Debug&#xff0c;可以在命名空间内写一点静态方法 &#xff1a; namespace Debug{} static void Print(const FString& message, con…

试用笔记之-汇通汉诺塔-益智游戏

首先下载汇通汉诺塔&#xff1a; http://www.htsoft.com.cn/download/HanoiTower.rar 汇通汉诺塔游戏规则:把X轴的圈圈都移到Z轴过关。 还可以选择自动游戏哦&#xff0c;看看AI机器人玩汇通汉诺塔

前程无忧滑块

声明(lianxi a15018601872) 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 前言(lianxi …

启智畅想火车集装箱箱号识别系统,同时识别车皮号、火车底盘号码

1、应用背景 在现代物流体系中&#xff0c;铁路集装箱运输扮演着举足轻重的角色&#xff0c;目前各个铁路系统在管理集装箱车厢进出站时&#xff0c;方法传统&#xff0c;采用人工登记集装箱箱号、车皮号以及火车底盘号码的方式&#xff0c;效率低、错误率高、人员工作量大、信…

湖北大学2024年成人高考函授报名专升本会计学专业介绍

湖北大学&#xff0c;这所坐落于历史文化名城武汉的高等学府&#xff0c;自其创立之初&#xff0c;便秉持着“厚德博学&#xff0c;求是创新”的校训&#xff0c;致力于培养一代又一代的优秀人才。而今&#xff0c;为满足广大社会人士对高等教育的渴求&#xff0c;特别是那些已…

新的里程碑丨deepin校园联盟在拉丁美洲自由软件节上的国际风采

deepin校园联盟秉持推广开源文化与技术的宗旨&#xff0c;在全球技术交流的洪流中&#xff0c;再次于海外掀起热烈浪潮。在厄瓜多尔雅才理工大学&#xff08;Yachay Tech University&#xff09;&#xff0c;deepin校园联盟大使Mosquera率领团队&#xff0c;跨越数百公里之遥&a…

Jenkins教程-12-发送html邮件测试报告

上一小节我们学习了发送钉钉测试报告通知的方法&#xff0c;本小节我们讲解一下发送html邮件测试报告的方法。 1、自动化用例执行完后&#xff0c;使用pytest_terminal_summary钩子函数收集测试结果&#xff0c;存入本地status.txt文件中&#xff0c;供Jenkins调用 #conftest…

Optional类方法

Optional类 方法empty()方法of(T value)ofNullable(T value)filter(Predicate<? super T> predicate)get()ifPresent(Consumer<? super T> consumer)isPresent()map(Function<? super T,? extends U> mapper)orElse(T other)orElseGet(Supplier<? ex…

如何实现Action菜单

文章目录 1. 概念介绍2. 思路与方法2.1 实现思路2.2 实现方法 3. 示例代码4. 内容总结 我们在上一章回中介绍了"自定义标题栏"相关的内容&#xff0c;本章回中将介绍自定义Action菜单.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在这里提到的…

Swift 中的 StoreKit 测试

文章目录 前言创建一个 StoreKit Demo使用 SKTestSessionaskToBuyEnabled 属性总结前言 StoreKit 框架的第二次迭代是我在过去几年中应用程序中最重大的变化。最近版本的 StoreKit 框架已完全采用了 Swift 语言特性,如 async 和 await。本篇内容我们将讨论 StoreKitTest 框架…

AutoGen 两个agent讲相声

Lesson 1: Multi-Agent Conversation and Stand-up Comedy 第 1 课&#xff1a;多人对话和单口相声 Setup from utils import get_openai_api_key OPENAI_API_KEY get_openai_api_key() llm_config {"model": "gpt-3.5-turbo"}Define an AutoGen agent…

教育心理学期末考试重点

人本主义学习理论 人本主义主张&#xff0c;心理学应当把人作为一个整体来研究&#xff0c;而不是将人的心理肢解为不完整的几个部分&#xff0c;应该研究正常的人&#xff0c;而且更应该关注人的高级心理活动&#xff0c;如热情、信念、生命、尊严等内容。人本主义的学习理论…

线性代数|机器学习-P20鞍点和极值

文章目录 1 . 瑞利商的思考1.1 瑞利商的定义1.2 投影向量 2. 拉格朗日乘子法3. 鞍点4. 线性拟合4.1 范德蒙矩阵线性拟合4.2 python 代码4.3 范德蒙矩阵缺点 5. 均值和方差5.1 样本均值和方差5.2 总体期望 μ \mu μ,总体方差 σ 2 \sigma^2 σ2 1 . 瑞利商的思考 1.1 瑞利商…

算法金 | Transformer,一个神奇的算法模型!!

大侠幸会&#xff0c;在下全网同名「算法金」 0 基础转 AI 上岸&#xff0c;多个算法赛 Top 「日更万日&#xff0c;让更多人享受智能乐趣」 抱个拳&#xff0c;送个礼 在现代自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;Transformer 模型的出现带来了革命性的变…

算法 —— 滑动窗口

目录 长度最小的子数组 无重复字符的最长子串 最大连续1的个数 将x减到0的最小操作数 找到字符串中所有字母异位词 长度最小的子数组 sum比target小就进窗口&#xff0c;sum比target大就出窗口&#xff0c;由于数组是正数&#xff0c;所以相加会使sum变大&#xff0c;相减…

云计算与生成式AI的技术盛宴!亚马逊云科技深圳 Community Day 社区活动流程抢先知道!

小李哥最近要给大家分享7月7日在深圳的即将举办的亚马逊云科技生成式AI社区活动Community Day &#xff0c;干货很多内容非常硬核&#xff0c;不仅有技术分享学习前沿AI技术&#xff0c;大家在现场还可以动手实践沉浸式体验大模型&#xff0c;另外参与现场活动还可以领取诸多精…

高优先线程

你开发的时候有么有遇到过一个问题&#xff1a;服务器的一个服务线程过几个小时断连一次&#xff0c;断连之后会马上重连这种情况。这是由于CPU负载较高,线程调度时将处理数据的线程挂起了一段时间导致的。 因此&#xff0c;我有考虑到把cpu的核心进行分散开来&#xff0c;就类…

关于发那科机器人系统升级方法

首先需要镜像备份和全备份&#xff0c;最好把机器人要到刻度线位置&#xff0c;零点位置 选择控制器启动&#xff0c;也可以按preevnext进入控制器模式

技术革新:如何用数据中台实现数字化转型

作为程序员&#xff0c;我们总是对技术如何改变企业运作充满好奇。今天&#xff0c;我们将深入探讨森马集团如何利用数据中台技术&#xff0c;实现从传统数据分析到数字化转型的华丽转身。 1. 技术背景&#xff1a;森马集团的数字化挑战 森马集团&#xff0c;一个在服饰行业占…