详解C语言中双向循环链表的实现

 更新时间:2022年06月17日 10:21:15   作者:MT_125  
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。本文将用C语言实现双向循环链表,需要的可以参考一下

实现细节

1、带一个哨兵位(哨兵节点,初始节点,不存储有效数据,用来方便后期数据的存储与查找)

2、与单向链表不同的是,双向链表中每个数据节点包含两个指针,分别指向前后两个节点

3、双向链表是循环的,其尾节点后不是空指针,而是与头部的哨兵节点通过指针相连

辅助理解图

具体实现代码

1、对链表进行初始化

初始化:哨兵位的前后指针均指向哨兵节点本身

void ListInit(ListNode** pphead)
{
    *pphead = (ListNode*)malloc(sizeof(ListNode));
    if (*pphead == NULL)
    {
        perror("ListInit");
        exit(-1);
    }
    (*pphead)->date = -1;
    (*pphead)->next = *pphead;
    (*pphead)->prev = *pphead;
}

2、任意位置前的插入

注意:插入位置前后节点中的前后指针要进行相应的更换

void Any_insert(ListNode* pos,Listtype date)
{
    ListNode* Prev = pos->prev;
//建立新节点
    ListNode* NewNode = (ListNode*)malloc(sizeof(ListNode));
    if (NewNode == NULL)
    {
        perror("Any_insert");
        exit(-1);
    }
    NewNode->date = date;
    NewNode->next = pos;
    pos->prev = NewNode;
    Prev->next = NewNode;
    NewNode->prev = Prev;
}

3、任意位置的删除

细节点:当链表中没有数据时,就不用删除,因此需要建立一个函数进行判断

bool Determine(ListNode* pphead)
{//判断链表中有无元素
    assert(pphead);
    return pphead == pphead->next; 
}
 
void Any_delet(ListNode* pos)
{
    assert(!Determine(pos));
    ListNode* Next = pos->next;
    ListNode* Prev = pos->prev;
    Next->prev = Prev;
    Prev->next = Next;
    free(pos);
}

4、头插和尾删

此处的插入和删除,十分方便,即:对上面的任插和任删进行套用

头插如下:

void Head_insert(ListNode* pphead, Listtype date)
{
    ListNode* NewNode = (ListNode*)malloc(sizeof(ListNode));
    if (NewNode == NULL)
    {
        perror("Head_insert");
        exit(-1);
    }
 
    //单独实现
    //NewNode->date = date;
    //NewNode->prev = pphead;
    //NewNode->next = pphead->next;
    //pphead->next->prev = NewNode;
    //pphead->next = NewNode;
    
    //进行任插的复用
    Any_insert(pphead->next ,date);
 
}

尾删如下:

void Tail_delet(ListNode* pphead)
{
    assert(pphead);
 
    //单独实现
    //assert(Determine(pphead));
    /*ListNode* tail = pphead->prev;
    if (tail != pphead)
    {
        ListNode* tailprev = tail->prev;
        tailprev->next = pphead;
        pphead->prev = tailprev;
        free(tail);
    }*/
 
    //尾删的复用
    Any_delet(pphead->prev);
}

完整代码

头文件

#pragma once
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
 
typedef int Listtype;
 
typedef struct ListNode
{
	struct ListNode* prev;
    Listtype date;
	struct ListNode* next;
}ListNode;
 
void ListInit(ListNode** pphead);                      //链表初始化
void ListNode_ADD(ListNode* pphead, Listtype date);    //尾插
void Head_insert(ListNode* pphead, Listtype date);     //头插
void ListNode_Print(ListNode* pphead);                 //链表打印
void Tail_delet(ListNode* pphead);                     //尾删
bool Determine(ListNode* pphead);                      //判断表中有无数据
void Any_insert(ListNode* pos, Listtype date);         //任插
void Any_delet(ListNode* pos);                         //任删
void List_Destory(ListNode* pos);                      //链表清空

具体函数

#define _CRT_SECURE_NO_WARNINGS 1
#include "List.h" 
 
//链表打印
void ListNode_Print(ListNode* pphead)
{
	assert(pphead);
	ListNode* phead = pphead;
	pphead = pphead->next;
	for (; pphead != phead; pphead = pphead->next)
	{
		printf("%d ", pphead->date);
	}
	printf("\n");
}
 
bool Determine(ListNode* pphead)
{//判断链表中有无元素
	assert(pphead);
	return pphead == pphead->next; 
}
 
//链表初始化
void ListInit(ListNode** pphead)
{
	*pphead = (ListNode*)malloc(sizeof(ListNode));
	if (*pphead == NULL)
	{
		perror("ListInit");
		exit(-1);
	}
	(*pphead)->date = -1;
	(*pphead)->next = *pphead;
	(*pphead)->prev = *pphead;
}
 
//尾插
void ListNode_ADD(ListNode* pphead,Listtype date)
{
	//ListNode* NewNode = (ListNode*)malloc(sizeof(ListNode));
	//if (NewNode == NULL)
	//{
	//	perror("ADD_malloc");
	//	exit(-1);
	//}
	//NewNode->date = date;
	//NewNode->prev = pphead->prev;
	//pphead->prev->next = NewNode;
	//pphead->prev = NewNode;
	//NewNode->next = pphead;
 
	//任插的复用
	Any_insert(pphead, date);
 
}
void Head_insert(ListNode* pphead, Listtype date)
{
	ListNode* NewNode = (ListNode*)malloc(sizeof(ListNode));
	if (NewNode == NULL)
	{
		perror("Head_insert");
		exit(-1);
	}
	//NewNode->date = date;
	//NewNode->prev = pphead;
	//NewNode->next = pphead->next;
	//pphead->next->prev = NewNode;
	//pphead->next = NewNode;
    
	//进行任插的复用
	Any_insert(pphead->next ,date);
 
}
 
void Tail_delet(ListNode* pphead)
{
	assert(pphead);
	//assert(Determine(pphead));
	
	/*ListNode* tail = pphead->prev;
	if (tail != pphead)
	{
		ListNode* tailprev = tail->prev;
		tailprev->next = pphead;
		pphead->prev = tailprev;
		free(tail);
	}*/
 
	//尾删的复用
	Any_delet(pphead->prev);
}
 
//在任意位置前插入
void Any_insert(ListNode* pos,Listtype date)
{
	ListNode* Prev = pos->prev;
	ListNode* NewNode = (ListNode*)malloc(sizeof(ListNode));
	if (NewNode == NULL)
	{
		perror("Any_insert");
		exit(-1);
	}
	NewNode->date = date;
	NewNode->next = pos;
	pos->prev = NewNode;
	Prev->next = NewNode;
	NewNode->prev = Prev;
}
 
//任意位置删除
void Any_delet(ListNode* pos)
{
	assert(!Determine(pos));
	ListNode* Next = pos->next;
	ListNode* Prev = pos->prev;
	Next->prev = Prev;
	Prev->next = Next;
	free(pos);
}
 
//链表清空
void List_Destory(ListNode* pos)
{
	ListNode* head = pos,*Prev = pos->prev;
	for (pos = pos->prev; head != pos;pos = Prev)
	{
		Prev = pos->prev;
		Any_delet(pos);
	}
	printf("\n清空完成\n");
}

测试

#define _CRT_SECURE_NO_WARNINGS 1
#include "List.h"
 
void ListTest(ListNode** pphead)
{
	ListInit(pphead);
	Head_insert(*pphead, 60);
	Head_insert(*pphead, 100);
	Head_insert(*pphead, 60);
	Head_insert(*pphead, 50);
	ListNode_Print(*pphead);
 
	Tail_delet(*pphead);
	Tail_delet(*pphead);
	Tail_delet(*pphead);
 
	ListNode_Print(*pphead);
}
 
int main()
{
	ListNode* pphead = NULL;
	ListTest(&pphead);
 
	return 0 ;
}

以上就是详解C语言中双向循环链表的实现的详细内容,更多关于C语言双向循环链表的资料请关注脚本之家其它相关文章!

相关文章

  • fatal error LNK1104: 无法打开文件“libc.lib”的解决方法

    fatal error LNK1104: 无法打开文件“libc.lib”的解决方法

    本篇文章是对fatal error LNK1104: 无法打开文件“libc.lib”的解决方法进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • EasyC++编写头文件

    EasyC++编写头文件

    这篇文章主要介绍了C++编写头文件,在一个C++程序中,只包含两类文件——.cpp文件和.h文件。其中,.cpp文件被称作C++源文件,里面放的都是C++的源代码;而.h文件则被称作C++头文件,里面放的也是C++的源代码,感兴趣的小伙伴一起来看下面文章的详细介绍吧
    2021-12-12
  • 如何用C++制作LeetCode刷题小技巧-错题记录本

    如何用C++制作LeetCode刷题小技巧-错题记录本

    这篇文章主要介绍了如何用C++制作LeetCode刷题小技巧-错题记录本的方法,需要的朋友可以参考下
    2021-04-04
  • 浅谈C++的浅拷贝出现的错误

    浅谈C++的浅拷贝出现的错误

    下面小编就为大家带来一篇浅谈C++的浅拷贝出现的错误。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • C语言算法练习之佩奇存钱方案

    C语言算法练习之佩奇存钱方案

    这篇文章主要该大家分享C语言算法佩奇存钱的练习,文章主要通过描述佩奇存钱的问题然后确定程序框架将结果运算出来,下面来看详细内容吧,需要的朋友可以参考一下
    2022-04-04
  • C语言编写扫雷小程序

    C语言编写扫雷小程序

    这篇文章主要为大家详细介绍了C语言编写扫雷小程序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-09-09
  • c++连接mysql数据库的两种方法(ADO连接和mysql api连接)

    c++连接mysql数据库的两种方法(ADO连接和mysql api连接)

    现在正做一个接口,通过不同的连接字符串操作不同的数据库。要用到mysql数据库,C++连接mysql有2种方法:利用ADO连接、利用mysql自己的api函数进行连接,下面看看如何用吧
    2013-12-12
  • C语言实现链队列基本操作

    C语言实现链队列基本操作

    这篇文章主要为大家详细介绍了C语言实现链队列基本操作,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • C语言开源库iniparser解析ini文件的方法

    C语言开源库iniparser解析ini文件的方法

    INI(Initialization File)文件是一种简单直观的数据存储格式,常用于配置应用程序的初始化设置,使用 iniparser 库的应用程序可以很方便地读取和解析INI文件中的配置信息,大大简化了对配置文件的处理工作,降低了程序的开发复杂度,感兴趣的的朋友跟随小编一起看看吧
    2024-04-04
  • 深入探究C++ string的内部究竟是什么样的

    深入探究C++ string的内部究竟是什么样的

    这篇文章主要给大家介绍了关于C++ string的内部究竟是什么样的,文中通过示例代码的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01

最新评论