广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C语言中双链表的基本操作
  • 110
分享到

C语言中双链表的基本操作

C语言双链表双链表的基本操作C语言双链表操作 2023-02-05 15:02:28 110人浏览 泡泡鱼
摘要

目录带头结点的双向循环链表基本操作创建销毁打印尾插法尾删头插头删查找元素位置任意位置插入任意位置删除完整代码及测试总结带头结点的双向循环链表 链表结构如下: 每个节点都有一个数据域和

带头结点的双向循环链表

链表结构如下:

每个节点都有一个数据域和两个指针域,这两个指针分别指向链表的前驱节点和后继节点,头节点的前驱节点是链表的最后一个节点,链表最后一个节点的后继节点是头节点。

正因为如此,带头结点的双向循环链表没有空节点,在进行遍历时,循环条件也与单链表不同:

单链表用cur->next为空判断当前节点是否为最后一个节点,带头的双向循环链表则需判断cur->next是否等于头节点。

定义:

typedef int DataType;
typedef struct Listnode
{
	DataType data;//数据域
	struct ListNode* next;//指向下一个节点
	struct ListNode* prev;//指向前一个节点
}ListNode;

基本操作

创建

创建链表需要先申请一段内存,然后再进行赋值,这里BuyListNode函数用于申请内存空间,在插入节点时,也需要调用BuyListNode函数。

申请节点:

static ListNode* BuyListNode(int x)
{
	ListNode* newNode = NULL;
	newNode = (ListNode*)malloc(sizeof(ListNode));//为节点动态申请一段内存
	if (NULL == newNode)
	{
		assert(0);
		return NULL;
	}
	//为申请的节点赋值
	newNode->data = x;
	newNode->next = NULL;
	newNode->prev = NULL;
	return newNode;
}

创建链表:

ListNode* ListCreate()
{
	ListNode*head=BuyListNode(0);//调用申请节点的函数
	//为头节点指针域赋值,链表为空时,prev和next都指向头节点
	head->next = head;
	head->prev = head;
	return head;
}

销毁

使用链表时会申请内存空间,所使用完毕后要进行释放,避免内存泄漏,这里销毁链表用了头删的思想,每次删除链表中的第一个节点并释放空间,具体过程如图:

循环执行上述过程,直到链表为空,最后再释放头节点空间。

程序如下:

void ListDestory(ListNode** plist)
{
	assert(plist);
	ListNode* cur = (*plist)->next;
	while (cur!=(*plist))
	{
		(*plist)->next = cur->next;
		free(cur);
		cur = (*plist)->next;
	}
	free(*plist);
	*plist = NULL;
}

这里需要注意的是,销毁链表要改变链表头结点的指向,所以传参时需要传二级指针。在对带头结点的双链表的操作中,只有销毁会改变指向头结点的指针plist的指向,所以只有销毁时需要传二级指针,其余操作传一级指针即可。

打印

链表打印的思想比较简单,只需要遍历打印即可。

程序:

void ListPrint(ListNode* plist)
{
	assert(plist);
	ListNode* cur = plist->next;
	while (cur != plist)//遍历的循环条件
	{
		printf("%d--->", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

尾插法

步骤

  • 申请新节点newNode
  • 对newNode的prev和next进行赋值
  • 使原链表最后一个节点的next指向新节点
  • 链表头指针的prev指向新节点

void ListPushBack(ListNode* plist, DataType x)
{
	assert(plist);
	ListNode* newNode =BuyListNode(x);
	newNode->prev = plist->prev;
	newNode->next = plist;
	plist->prev->next = newNode;
	plist->prev = newNode;
}

尾删

删除节点时需要先判断链表是否为空,先写出链表判空的方法

链表判空

看plist->next是否等于plist即可判断链表是否为空

static int IsEmpty(ListNode*plist)
{
	assert(plist);
	if (plist->next == plist)
	{
		return 1;//链表为空,返回1
	}
	return 0;//链表非空,返回0
}

尾删法

步骤

  • 用del标记最后一个节点
  • 使del前一个节点的next指向头节点
  • 头结点的prev指向del的前一个节点
  • 释放del的空间
  • 这里中间两步将del节点从链表中移除出来,最后一步则释放内存空间
  • 如图:

程序

void ListPopBack(ListNode * plist)
{
	assert(plist);
	if (IsEmpty(plist))
	{
		return;
	}
	ListNode* del = plist->prev;
	del->prev->next = plist;
	plist->prev = del->prev;
	free(del);
	del = NULL;
}

头插

步骤

  • 申请新节点newNode
  • 使新节点的prev指向头节点
  • 令新节点的next指向原来链表第二个节点
  • 令原来第二个节点的prev指向newNode
  • 令头节点的next指向newNode

程序

void ListPushFront(ListNode* plist, DataType x)
{
	assert(plist);
	ListNode* newNode = BuyListNode(0);
	newNode->prev = plist;
	newNode->next = plist->next;

	plist->next->prev = newNode;
	plist->next = newNode;
}

头删

  • 用del标记链表的第一个节点
  • 令头结点的next指向del->next
  • 原链表中第二个节点的prev指向头节点,成为新的第一个节点
  • 释放删除节点的空间

程序

void ListPopFront(ListNode* plist)
{
	assert(plist);
	if (IsEmpty(plist))//先判空
	{
		return;
	}
	ListNode* del = plist->next;

	plist->next = del->next;
	del->next->prev = plist;

	free(del);
	del = NULL;
}

查找元素位置

对链表进行遍历,比对,找到与目标值相等的数时,返回当前的地址

ListNode* ListFind(ListNode* plist, DataType x)
{
	assert(plist);
	ListNode* cur = plist->next;
	while (cur != plist)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

任意位置插入

由于双链表有两个指针域,所以可以在pos位置的前面进行插入

步骤

  • 申请一个新节点newNode
  • 为新节点的prev和next赋值,使其分别指向pos的前一个节点和pos
  • 使原来pos前一个节点的next指向新节点
  • 令pos的prev指向新节点

void ListInsert(ListNode* pos, DataType x)
{
	assert(pos);
	ListNode* newNode = BuyListNode(x);
	//在pos前面插入
	newNode->prev = pos->prev;
	newNode->next = pos;

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

任意位置删除

删除给定的节点pos

  • pos前一个节点的next指向pos的下一个节点
  • pos下一个节点的prev指向pos的前一个节点
  • 释放pos占用的内存空间

程序

void ListErase(ListNode* pos)
{
	assert(pos);
	pos->prev->next = pos->next;
	pos->next->prev = pos->prev;
	free(pos);
	pos = NULL;	
}

完整代码及测试

work.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<windows.h>
typedef int DataType;
typedef struct ListNode
{
	DataType data;
	struct ListNode* next;
	struct ListNode* prev;
}ListNode;

ListNode* ListCreate();// 创建返回链表的头结点.
void ListDestory(ListNode** plist);// 双向链表销毁
void ListPrint(ListNode* plist);// 双向链表打印
void ListPushBack(ListNode* plist, DataType x);// 双向链表尾插
void ListPopBack(ListNode* plist);// 双向链表尾删
void ListPushFront(ListNode* plist, DataType x);// 双向链表头插
void ListPopFront(ListNode* plist);// 双向链表头删
ListNode* ListFind(ListNode* plist, DataType x);// 双向链表查找
void ListInsert(ListNode* pos, DataType x);// 双向链表在pos的前面进行插入
void ListErase(ListNode* pos);// 双向链表删除pos位置的节点

work.c

#include"work.h"

//申请节点
static ListNode* BuyListNode(int x)
{
	ListNode* newNode = NULL;
	newNode = (ListNode*)malloc(sizeof(ListNode));//为节点动态申请一段内存
	if (NULL == newNode)
	{
		assert(0);
		return NULL;
	}
	//为申请的节点赋值
	newNode->data = x;
	newNode->next = NULL;
	newNode->prev = NULL;
	return newNode;
}


//创建链表
ListNode* ListCreate()
{
	ListNode*head=BuyListNode(0);//调用申请节点的函数
	//为头节点指针域赋值,链表为空时,prev和next都指向头节点
	head->next = head;
	head->prev = head;
	return head;
}

//销毁链表
void ListDestory(ListNode** plist)
{
	assert(plist);
	ListNode* cur = (*plist)->next;
	while (cur!=(*plist))
	{
		(*plist)->next = cur->next;
		free(cur);
		cur = (*plist)->next;
	}
	free(*plist);
	*plist = NULL;
}

// 打印链表
void ListPrint(ListNode* plist)
{
	assert(plist);
	ListNode* cur = plist->next;
	while (cur != plist)
	{
		printf("%d--->", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

//尾插法
void ListPushBack(ListNode* plist, DataType x)
{
	assert(plist);
	ListNode* newNode =BuyListNode(x);
	newNode->prev = plist->prev;
	newNode->next = plist;
	plist->prev->next = newNode;
	plist->prev = newNode;
}

//判断链表是否为空
static int IsEmpty(ListNode*plist)
{
	assert(plist);
	if (plist->next == plist)
	{
		return 1;
	}
	return 0;
}

// 尾删法
void ListPopBack(ListNode * plist)
{
	assert(plist);
	if (IsEmpty(plist))
	{
		return;
	}
	ListNode* del = plist->prev;
	del->prev->next = plist;
	plist->prev = del->prev;
	free(del);
	del = NULL;
}

// 双向链表头插
void ListPushFront(ListNode* plist, DataType x)
{
	assert(plist);
	ListNode* newNode = BuyListNode(0);
	newNode->prev = plist;
	newNode->next = plist->next;

	plist->next->prev = newNode;
	plist->next = newNode;
}

// 双向链表头删
void ListPopFront(ListNode* plist)
{
	assert(plist);
	if (IsEmpty(plist))
	{
		return;
	}
	ListNode* del = plist->next;

	plist->next = del->next;
	del->next->prev = plist;

	free(del);
	del = NULL;
}

// 查找元素位置
ListNode* ListFind(ListNode* plist, DataType x)
{
	assert(plist);
	ListNode* cur = plist->next;
	while (cur != plist)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

// 任意位置插入
void ListInsert(ListNode* pos, DataType x)
{
	assert(pos);
	ListNode* newNode = BuyListNode(x);
	//在pos前面插入
	newNode->prev = pos->prev;
	newNode->next = pos;

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

//任意位置删除
void ListErase(ListNode* pos)
{
	assert(pos);
	pos->prev->next = pos->next;
	pos->next->prev = pos->prev;
	free(pos);
	pos = NULL;
	
}

main.c

#include"work.h"
int main()
{
	ListNode*plist= ListCreate();//创建一个双向非循环链表

    //尾插五个节点
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	ListPushBack(plist, 5);
	ListPrint(plist);
    //头插一个节点
	ListPushFront(plist, 0);
	ListPrint(plist);
    //尾删一个节点
	ListPopBack(plist);
	ListPrint(plist);
    //头删一个节点
	ListPopFront(plist);
	ListPrint(plist);
    //在元素3前面插入一个节点
	ListInsert(ListFind(plist,3),9);
	ListPrint(plist);
    //删除值为9的节点
	ListErase(ListFind(plist,9));
	ListPrint(plist);
    //销毁链表
	ListDestory(&plist);
	system("pause");
	return 0;
}

测试结果:

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: C语言中双链表的基本操作

本文链接: https://www.lsjlt.com/news/194240.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C语言中双链表的基本操作
    目录带头结点的双向循环链表基本操作创建销毁打印尾插法尾删头插头删查找元素位置任意位置插入任意位置删除完整代码及测试总结带头结点的双向循环链表 链表结构如下: 每个节点都有一个数据域和...
    99+
    2023-02-05
    C语言双链表 双链表的基本操作 C语言双链表操作
  • ​​​​​​​C语言实现单链表基本操作方法
    目录存储结构基本功能头插法创建单链表尾插法创建单链表获取指定位置的元素在指定位置插入元素删除指定位置的元素获取单链表的长度合并两个非递减的单链表晴链表遍历打印单链表附上完整代码存储结...
    99+
    2022-11-13
  • C语言实现单链表的基本操作分享
    目录导语单链表单链表的特点定义初始化操作头插法尾插法删除第i个元素在第i个位置插入导语 无论是顺序存储结构还是链式存储结构,在内存中进行存放元素的时候,不仅需要存放该元素的相关信息,...
    99+
    2022-11-13
    C语言单链表基本操作 C语言单链表
  • C语言中单链表的基本操作指南(增删改查)
    目录1.链表概述2.链表的基本使用2.0 准备工作2.1 创建节点(结构体)2.2 全局定义链表头尾指针 方便调用2.3 创建链表,实现在链表中增加一个数据(尾添加)————增2.4...
    99+
    2022-11-12
  • C语言实现链队列基本操作
    队列的链式存储结构实现,相比于循环队列实现要复杂一些,但是没有队满的限制。 头文件声明 #include <stdio.h> #include <stdlib....
    99+
    2022-11-12
  • C语言双向链表的原理与使用操作
    目录一.引入二.双向链表的定义三.双向链表与单链表对比3.1图示对比3.2代码对比四.双向链表的操作4.1双向链表的创建4.2双向链表的插入4.3双向链表的删除4.4双向链表的销毁五...
    99+
    2022-11-13
  • Go 语言结构体链表的基本操作
    目录1. 什么是链表2. 单项链表的基本操作3. 使用 struct 定义单链表4. 尾部添加节点方法一5. 头部插入节点方法一6. 指定节点后添加新节点7. 删除节点1. 什么是链...
    99+
    2022-11-13
  • Python实现双向链表基本操作
    双向链表的基本操作的实现,供大家参考,具体内容如下 在之前的博客中介绍了三种链表,分别是单链表、单向循环链表以及双向链表。本篇博客将用Python来实现双向链表的如下操作。(用到的工...
    99+
    2022-11-11
  • C语言中单链表(不带头结点)基本操作的实现详解
    目录一、单链表的概念二、单链表的基本操作1.创建单个结点2.创建具有n个结点的链表3.打印单链表4.尾插5.尾删6.头插7.头删8.查找某个结点9.在某个结点后面插入10.在某个结点...
    99+
    2022-11-16
    C语言单链表操作 C语言单链表
  • C语言中单链表的基本操作(创建、销毁、增删查改等)
    目录链表分类单链表的介绍单链表的基本操作创建打印尾插头插尾删头删查找任意位置插入任意位置删除销毁完整代码总结链表分类 链表主要有下面三种分类方法: 单向或者双向带头或者不带头循环或者...
    99+
    2023-02-05
    C语言单链表 单链表的创建 单链表的销毁 单链表的增删查改
  • C语言类的双向链表详解
    目录前言双向链表的定义双向链表的创建节点的创建双向链表节点查找双向链表的插入双向链表的节点删除双向链表的删除总结前言 链表(linked list)是一种这样的数据结构,其中的各对象...
    99+
    2022-11-12
  • C语言链表的操作有哪些
    这篇“C语言链表的操作有哪些”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“C语言链表的操作有哪些”文章吧。前言编译工具:vs...
    99+
    2023-06-30
  • C语言数据结构之链队列的基本操作
    目录1.队列的定义2.队列的表示和实现(1)初始化操作(2)销毁队列(3)入队操作(4)出队操作附录完整代码:总结1.队列的定义 队列 (Queue)是另一种限定性的线性表,它只允许...
    99+
    2022-11-12
  • C语言数据结构之单链表与双链表的增删改查操作实现
    目录前言单链表的增删改查定义结构体以及初始化增加结点删除结点查找修改结点移除结点最终效果双链表的基本操作初始化建表遍历双链表指定位置插入结点指定位置删除结点查找结点位置最终效果结语前...
    99+
    2022-11-13
  • C语言实现线性表的基本操作详解
    目录前言一、实训名称二、实训目的三、实训要求四、实现效果五、顺序存储代码实现六、链式存储代码实现前言 这里使用的工具是DEV C++ 可以借鉴一下 一、实训名称 线性表的基本操作 二...
    99+
    2022-11-12
  • C语言 超详细模拟实现单链表的基本操作建议收藏
    目录1 链表的概念及结构2 链表的分类3 链表的实现无头+单向+非循环链表增删查改实现3.1 链表的定义3.2 链表数据的打印3.3 链表的尾插3.4 链表空间的动态申请3.5 链表...
    99+
    2022-11-13
  • C语言数据结构中链队列的基本操作是怎样的
    这篇文章将为大家详细讲解有关C语言数据结构中链队列的基本操作是怎样的,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。1.队列的定义队列 (Queue)是另一种限定性的线性表,它只允许在表的一端...
    99+
    2023-06-22
  • C语言实现单链表的基本功能详解
    1.首先简单了解一下链表的概念: 要注意的是链表是一个结构体实现的一种线性表,它只能从前往后,不可以从后往前(因为next只保存下一个节点的地址).在实现单链表的操作时,需要用指针...
    99+
    2022-11-12
  • C语言怎么实现单链表的基本功能
    本篇内容主要讲解“C语言怎么实现单链表的基本功能”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C语言怎么实现单链表的基本功能”吧!首先简单了解一下链表的概念:要注意的是链表是一个结构体实现的一种...
    99+
    2023-06-21
  • C语言如何实现单链表操作
    本篇内容介绍了“C语言如何实现单链表操作”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1 链表的概念及结构概念:链表是一种物理存储结构上非连...
    99+
    2023-06-29
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作