当前位置: 首页 > news >正文

顺据结构(C\C++)——双向链表

双向链表

      • 前置知识:链表的分类
      • 1 概念与结构
      • 2 实现双向链表
        • List.h
        • List.c
          • 初始化
          • 头插、尾插
          • 指定位置插入
          • 头删、尾删
          • 指定位置删
          • 查找
          • 摧毁
          • 完整代码
        • main.c
      • 3. 顺序表与链表的分析

前置知识:链表的分类

链表的结构⾮常多样,以下情况组合起来就有8种(2x2x2)链表结构

在这里插入图片描述

在这里插入图片描述
无头单向非循环链表:结构简单,一般不单独存数据,常作为其他数据结构的子结构,在笔试面试中出现较多。
带头双向循环链表:结构最复杂,一般用于单独存储数据。虽然结构复杂,但实现后会带来很多优势,代码实现反而简单。

1 概念与结构

在这里插入图片描述

带头双向循环链表中,“带头”的头结点实际为“哨兵位”,不存储任何有效元素,只起“放哨”作用。
在这里插入图片描述

2 实现双向链表

总的原则:
头结点要发生改变传二级指针
头结点不需要改变传一级指针

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

//双向链表结构
typedef int LTDataType;
typedef struct ListNode
{
	int data;
	struct ListNode* next;
	struct ListNode* prev;
}LTNode;

void LTPrint(LTNode* phead);
//双向链表的初始化
//void LTInit(LTNode** pphead);

LTNode* LTInit();


//头结点要发生改变传二级
// 不需要改变传一级
//尾插
void LTPushBack(LTNode* phead, LTDataType x);

//头插
void LTPushFront(LTNode* phead, LTDataType x);

//只有一个头结点的情况下,双向链表为空
bool LTEmpty(LTNode* phead);

//尾删
void LTPopBack(LTNode* phead);

//头删
void LTPopFront(LTNode* phead);

//查找
LTNode* LTFind(LTNode* phead, LTDataType x);

//在pos位置之后插入x
void LTInsert(LTNode* pos, LTDataType x);

//删除pos位置的结点
void LTErase(LTNode* pos);

//摧毁
//违背了接口一致性
//void LTDesTroy(LTNode** pphead);
//传一级:需要最后手动置为空(推荐)
void LTDesTroy(LTNode* phead);
List.c
初始化
LTNode* LTBuyNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	newnode->data = x;
	newnode->next = newnode->prev = newnode;

	return newnode;
}
/*方法一:通过形参返回*/
//void LTInit(LTNode** pphead)
//{
//	assert(pphead);
//	*pphead = LTBuyNode(-1);
//}
/*方法二:通过返回值返回*/
LTNode* LTInit()
{
	LTNode* phead = LTBuyNode(-1);
	return phead;
}

这里建议用方法二,原因是
方法二不用二级指针操作更方便,通过返回值返回;

头插、尾插

头插
在这里插入图片描述

//头插
void LTPushFront(LTNode* phead,LTDataType x)
{
	assert(phead);
	LTNode* newnode = LTBuyNode(x);


	newnode->next = phead->next;
	newnode->prev = phead;

	phead->next->prev = newnode;
	phead->next = newnode;
}

尾插
在这里插入图片描述

void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* newnode = LTBuyNode(x);
	//phead  phead->prev(尾结点) newnode

	//注意顺序,先处理newnode,再修改phead结点
	//(1)
	newnode->prev = phead->prev;
	newnode->next = phead;

	//(2)
	phead->prev->next = newnode;
	phead->prev = newnode;
}
指定位置插入

在这里插入图片描述

//在pos位置之后插入x
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	LTNode* newnode = LTBuyNode(x);

	newnode->next = pos->next;
	newnode->prev = pos;

	pos->next->prev = newnode;
	pos->next = newnode;
}

头删、尾删

头删
在这里插入图片描述

//头删
void LTPopFront(LTNode* phead)
{
	assert(phead);
	LTNode* del = phead->next;

	del->next->prev = phead;
	phead->next = del->next;
	free(del);
	del = NULL;
}

尾删
在这里插入图片描述

//尾删
void LTPopBack(LTNode* phead)
{
	assert(!LTEmpty(phead));
	LTNode* del = phead->prev;

	del->prev->next = phead;
	phead->prev = del->prev;

	free(del);
	del = NULL;
}
指定位置删

在这里插入图片描述

//删除pos位置的结点
void LTErase(LTNode* pos)
{
	assert(pos);
	//pos pos->prev pos->next
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;

	free(pos);
	pos = NULL;
}
查找
//查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* pcur = phead->next;

	while (pcur != phead)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

摧毁
//销毁
//方法一:main函数调用无需任何操作
//void LTDesTroy(LTNode** pphead)
//{
//	LTNode* pcur = (*pphead)->next;
//
//	while (pcur != *pphead)
//	{
//		LTNode* next = pcur->next;
//		free(pcur);
//		pcur = next;
//	}
//	free(*pphead);
//	*pphead = NULL;
//}
//方法二:在main函数调用后将phead == NULL
void LTDesTroy(LTNode* pphead)
{
	LTNode* pcur = pphead->next;

	while (pcur != pphead)
	{
		LTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	free(pphead);
	pphead = NULL;
}
完整代码
#include"List.h"
LTNode* LTBuyNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	newnode->data = x;
	newnode->next = newnode->prev = newnode;

	return newnode;
}
//void LTInit(LTNode** pphead)
//{
//	assert(pphead);
//	*pphead = LTBuyNode(-1);
//}

LTNode* LTInit()
{
	LTNode* phead = LTBuyNode(-1);
	return phead;
}
//打印双链表
void LTPrint(LTNode* phead)
{
	LTNode* pcur = phead->next;

	while (pcur != phead)
	{
		printf("%d -> ", pcur->data);
		pcur = pcur->next;
	}
	printf("\n");

}

//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* newnode = LTBuyNode(x);
	//phead  phead->prev(尾结点) newnode

	//注意顺序,先处理newnode,再修改phead结点
	//(1)
	newnode->prev = phead->prev;
	newnode->next = phead;

	//(2)
	phead->prev->next = newnode;
	phead->prev = newnode;
}

//头插
void LTPushFront(LTNode* phead,LTDataType x)
{
	assert(phead);
	LTNode* newnode = LTBuyNode(x);


	newnode->next = phead->next;
	newnode->prev = phead;

	phead->next->prev = newnode;
	phead->next = newnode;
}

//是否为空
bool LTEmpty(LTNode* phead)
{
	return phead == phead->next;
}

//尾删
void LTPopBack(LTNode* phead)
{
	assert(!LTEmpty(phead));
	LTNode* del = phead->prev;

	del->prev->next = phead;
	phead->prev = del->prev;

	free(del);
	del = NULL;
}

//头删
void LTPopFront(LTNode* phead)
{
	assert(phead);
	LTNode* del = phead->next;

	del->next->prev = phead;
	phead->next = del->next;
	free(del);
	del = NULL;
}

//查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* pcur = phead->next;

	while (pcur != phead)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

//在pos位置之后插入x
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	LTNode* newnode = LTBuyNode(x);

	newnode->next = pos->next;
	newnode->prev = pos;

	pos->next->prev = newnode;
	pos->next = newnode;
}

//删除pos位置的结点
void LTErase(LTNode* pos)
{
	assert(pos);
	//pos pos->prev pos->next
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;

	free(pos);
	pos = NULL;
}

//销毁
//void LTDesTroy(LTNode** pphead)
//{
//	LTNode* pcur = (*pphead)->next;
//
//	while (pcur != *pphead)
//	{
//		LTNode* next = pcur->next;
//		free(pcur);
//		pcur = next;
//	}
//	free(*pphead);
//	*pphead = NULL;
//}

void LTDesTroy(LTNode* pphead)
{
	LTNode* pcur = pphead->next;

	while (pcur != pphead)
	{
		LTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	free(pphead);
	pphead = NULL;
}
main.c
#include"List.h"

//
void test01()
{
	/*LTNode* plist = NULL;
	LTInit(&plist);*/
	LTNode* plist = LTInit();
	LTPushBack(plist, 9);
	LTPushFront(plist, 1);
	LTPushFront(plist, 2);
	LTPushFront(plist, 3);

	//LTPopBack(plist);
	//LTPopFront(plist);
	LTNode* find = LTFind(plist, 2);
	if (find == NULL)
	{
		printf("没找到\n");
	}
	else
	{
		printf("找到了\n");
	}
	LTPrint(plist);
	LTInsert(find, 100);
	LTPrint(plist);
	LTErase(find);
	find = NULL;
	LTPrint(plist);
	LTDesTroy(plist);
	plist = NULL;
	LTPrint(plist);
}

int main()
{
	test01();
	return 0;
}

3. 顺序表与链表的分析

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

相关文章:

  • 【Qt】游戏场景和图元
  • rbpf虚拟机-JIT和解释执行对比
  • 数据处理的两种范式:深入解析OLTP与OLAP系统
  • 自动驾驶实验
  • 13届省赛python A组:10.数的拆分
  • 【Linux】进程间通信(IPC)-- 无名管道、命名管道
  • 请求Header(Request Headers)详解
  • LeetCode算法题(Go语言实现)_22
  • 操作系统的运行机制
  • 运放的噪声计算
  • 同步SVPWM调制策略的初步学习记录
  • 六十天Linux从0到项目搭建(第二十四天)(共享内存)
  • 鸿蒙开发03样式相关介绍(二)
  • 《MOSFET:静电场中的无声刺客》
  • DirectX安装步骤(包含安装包)DirectX详细图文安装教程
  • LangChain 安装与环境搭建,并调用OpenAI与Ollama本地大模型
  • 31天Python入门——第17天:初识面向对象
  • Android设计模式之责任链模式
  • 小程序:富文本的处理
  • C 语言基础知识
  • 网站婚礼服务态网站建设论文/淘宝推广怎么做
  • 做文案选图片素材的网站/西安快速排名优化
  • 学校网站的系统建设方式/江苏网站seo
  • 人力资源公司注册/班级优化大师的功能有哪些
  • 武汉做网站的公司/公司seo是什么级别
  • 做系统和做网站哪个简单一些/西安网站seo推广