C语言:数据结构(双向链表)

2024-04-28 14:44

本文主要是介绍C语言:数据结构(双向链表),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 1、双向链表的结构
  • 2、顺序表和双向链表的优缺点分析
  • 3、双向链表的实现

1、双向链表的结构

在这里插入图片描述

注意:这⾥的“带头“跟前面我们说的“头节点”是两个概念,实际前面的在单链表阶段称呼不严谨,但是为了更好的理解就直接称为单链表的头节点。
带头链表里的头节点,实际为“放哨的”,哨兵位节点不存储任何有效元素,只是站在这里“放哨的”。
“哨兵位”存在的意义:遍历循环链表避免死循环。

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

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

3、双向链表的实现

ListNode.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//定义双向链表节点的结构
typedef int Ltdatatype;
typedef struct ListNode
{Ltdatatype data;struct ListNode* prev;//指向前一个节点的指针struct ListNode* next;//指向后一个节点的指针
}ListNode;
//双向链表的初始化
ListNode* LtInit();
//尾插
//不改变哨兵位的地址,所以传一级即可
void LtPushBack(ListNode* phead, Ltdatatype x);//插入数据之前,链表必须初始化到只有一个头结点的情况
//打印链表
void LtPrint(ListNode* phead);
//头插
void LtPushFront(ListNode* phead, Ltdatatype x);
//尾删
LtPopBack(ListNode* phead);
//头删
LtPopFront(ListNode* phead);
//查找
ListNode* LtFind(ListNode* phead, Ltdatatype x);
//指定位置前插入
void LtInsert(ListNode* pos, Ltdatatype x);
//删除pos位置
void LtErase(ListNode* pos);
//销毁链表
void LtDestroy(ListNode* phead);

ListNode.c

#define _CRT_SECURE_NO_WARNINGS
#include "ListNode.h"
//申请节点
ListNode* LtBuyNode(Ltdatatype x)
{ListNode* node = (ListNode*)malloc(sizeof(ListNode));if (node == NULL){perror("malloc fail");exit(1);}//申请成功node->data = x;node->next = node->prev = node;return node;
}
//双向链表的初始化
ListNode* LtInit()
{ListNode*phead = LtBuyNode(-1);return phead;
}
//尾插
void LtPushBack(ListNode* phead, Ltdatatype x)
{assert(phead);ListNode* newnode = LtBuyNode(x);//改变新节点的指向newnode->prev = phead->prev;newnode->next = phead;//改变尾节点和哨兵位的指向phead->prev->next = newnode;phead->prev = newnode;
}
//打印链表
void LtPrint(ListNode* phead)
{ListNode* pcur = phead->next;//遍历链表while (pcur != phead){printf("%d->", pcur->data);pcur = pcur->next;}printf("\n");
}
//头插
void LtPushFront(ListNode* phead,Ltdatatype x)
{assert(phead);ListNode* newnode = LtBuyNode(x);newnode->prev = phead;newnode->next = phead->next;//修改哨兵位和第一个有效节点的指向phead->next->prev = newnode;phead->next = newnode;
}
//尾删
LtPopBack(ListNode* phead)
{//链表必须有效且链表不能为空(只有一个哨兵位)assert(phead && phead->next != phead);ListNode* Del = phead->prev;//尾节点ListNode* DelPrev = Del->prev;//尾节点的前一个节点phead->prev = DelPrev;DelPrev->next = phead;free(Del);//删除Del节点Del = NULL;
}
//头删
LtPopFront(ListNode* phead)
{//判断链表是否有效和链表是否为空assert(phead && phead->next != phead);ListNode* Del = phead->next;//第一个有效节点ListNode* DelNext = Del->next;//有效节点的下一个节点phead->next = DelNext;DelNext->prev = phead;free(Del);//删除Del节点Del = NULL;
}
//查找
ListNode* LtFind(ListNode* phead, Ltdatatype x)
{ListNode* pcur = phead->next;//遍历链表while (pcur != phead){if (pcur->data == x)return pcur;pcur = pcur->next;//继续让pcur往下遍历}return NULL;//没有找到
}
//在pos位置之前插入数据
void LtInsert(ListNode* pos,Ltdatatype x)
{ListNode* newnode = LtBuyNode(x);newnode->prev = pos->prev;newnode->next = pos;pos->prev->next = newnode;pos->prev = newnode;
}
//删除pos位置
void LtErase(ListNode* pos)
{assert(pos);ListNode* PosPrev = pos->prev;//pos的前一个节点ListNode* PosNext = pos->next;//pos的后一个节点PosPrev->next = PosNext;PosNext->prev = PosPrev;free(pos);//pos = NULL;这里就算置空了,也不会影响实参
}
//销毁链表
void LtDestroy(ListNode* phead)
{ListNode* pcur = phead->next;//边遍历边释放节点while (pcur != phead){ListNode* Next = pcur->next;//保存要释放掉节点的下一个地址free(pcur);pcur = Next;}//此时pcur指向phead,而phead还没有被销毁free(phead);pcur = NULL;
}

text.c

#define _CRT_SECURE_NO_WARNINGS
#include "ListNode.h"
void LtnodeTest()
{//测试初始化ListNode* plist = LtInit();//测试尾插LtPushBack(plist,1);LtPushBack(plist,2);LtPushBack(plist,3);//测试打印LtPrint(plist);//测试头插//LtPushFront(plist,4);//LtPushFront(plist,5);//LtPushFront(plist,6);//LtPrint(plist);//测试尾删LtPopBack(plist);LtPrint(plist);//测试头删//LtPopFront(plist);//LtPrint(plist);//测试查找//ListNode*find = LtFind(plist,2);//if (find)//	printf("找到了!\n");//else//	printf("没找到!\n");//测试在pos位置之前插入数据//LtInsert(find,88);//LtPrint(plist);//测试删除pos位置//LtErase(find);//find = NULL;//形参的改变不会影响实参,所以要在函数调用结束之后置为空//LtPrint(plist);//测试销毁链表//LtDestroy(plist);//plist = NULL;
}
int main()
{LtnodeTest();return 0;
}

如果对你有所帮助的话,别忘了一键三连哟,谢谢宝子们😘!

这篇关于C语言:数据结构(双向链表)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/943520

相关文章

数据结构--AVL树

一、什么是AVL树 1、AVL树的概念         二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查 找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年 发明了一种解决上述问题的方法: 当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超

【C语言每日题解】三题:回文检查、刘备 关羽 张飞三人过年放鞭炮、犹太人死亡游戏(难度up,推荐⭐✨)

🥰欢迎关注 轻松拿捏C语言系列,来和 小哇 一起进步!✊ 🌈感谢大家的阅读、点赞、收藏和关注 🥰希望大家喜欢我本次的讲解 🌟非常推荐最后一道题 🌹 犹太人死亡游戏,建议观看 🌙目录 💕题目一:回文检查 🎉题目二:刘备、关羽、张飞过年放鞭炮 🌹题目三: 犹太人死亡游戏         所以我们要做的就是如何让数组中最后一个元素过了之后又来到开头的元素。 其次

【数据结构与算法】LeetCode:哈希表

文章目录 哈希表一般查找 (键查找)计数查找 (值查找)键、值查找缓存复杂链表的复制 哈希表 哈希表的用法主要包括: 查找:在哈希表中,数据被存储在键值对中,键可以用作索引来访问数组中的数据,因此可以快速查找任何键对应的值。 计数:例如,统计一个字符串中每个字符出现的次数,或者统计一个数组中不同元素的数量等。 缓存:可以将经常访问的数据缓存在哈希表中,这样在需要访问这些

怎样把一个遥感影像用C语言进行处理并且输出?

怎样把一个遥感影像用C语言进行处理并且输出? 如果将影像另存为ASCII码,做成.txt文件,会因为该文件过大而难以处理。所以应当另存为ENVI standard文件,并写上后缀.dat。 然后查看其有多少行多少列以及字符类型(当然读入的字符类型也可能不是它,它只是用来参考,具体读入的字符类型的确定下面马上说!)。 对该.dat文件右键查看其属性。看它有多少字节。从而计算出每个像素占有几

[大师C语言(第六篇)]C语言程序不同退出方式背后的秘密

C语言程序不同退出方式背后的秘密(一) 在C语言中,程序的退出方式有多种,每种方式背后都有其技术原理。本篇文章将探讨C语言程序的退出方式,包括main函数的返回值、exit函数、_exit函数以及abort函数。 main函数的返回值 在C语言中,main函数是程序的入口点,它的返回值通常用于表示程序的退出状态。 返回0:表示程序成功执行。返回非零值:表示程序执行过程中出现了错误或异常情况

C语言 | Leetcode C语言题解之第89题格雷编码

题目: 题解: int* grayCode(int n, int* returnSize) {int ret_size = 1 << n;int *ret = (int *)malloc(ret_size * sizeof(int));for (int i = 0; i < ret_size; i++) {ret[i] = (i >> 1) ^ i;}*returnSize = ret_s

深入浅出Java中的数据结构:LinkedHashMap详解

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云;欢迎大家常来逛逛   今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。   我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初

【数据结构】栈的实现(链式栈)

文章目录 栈的实现(链式栈)栈的定义初始化栈进栈判断是否为空栈出栈销毁栈获取栈顶元素获取栈的长度栈的打印 完整代码(包括测试代码)Stack.hStack.ctest.c 栈的实现(链式栈) 首先新建一个工程: Stack.h(链式栈的类型定义、接口函数声明、引用的头文件) Stack.c(链式栈接口函数的实现) test.c(主函数、测试栈各个接口功能) 完整的代码放在后

Spring的表达式语言(SpEL)使用

Spring表达式语言(Spring Expression Language,简称SpEL)是Spring框架提供的一种强大的表达式语言,它在Spring 2.0版本引入。SpEL的设计灵感来源于传统的EL(Expression Language),即JSP中的表达式语言,但它提供了更多的功能和灵活性。 SpEL 的主要特点: 表达式评估: 允许在运行时评估表达式字符串。 对象图导航: 提

【数据结构与算法】关于--树

目录 一:什么是树? 二:树的相关概念 三:树的表示 一:什么是树? 树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。将它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。  有一个特殊的结点,称为根结点,根节点没有前驱结点。除根节点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,