【数据结构】05.双向链表

一、双向链表的结构

注意:这里的"带头"跟前面我们说的"头节点"是两个概念,带头链表里的头节点,实际为"哨兵位",哨兵位节点不存储任何有效元素,只是站在这里"放哨的"。

"哨兵位"存在的意义:遍历循环链表避免死循环。

二、双向链表的实现

2.1 双向链表结点的创建

c 复制代码
typedef int DLType;
//创建结构体
typedef struct DList
{
	DLType data;
	struct DList* prev;
	struct DList* next;
}DL;

2.2 双向链表的初始化与销毁

c 复制代码
//初始化phead
DL* init(void)
{
	DL* phead = BuyNewNode(0);
	return phead;
}
c 复制代码
//销毁链表,这里需要主要phead是形参,这里的最后将phead置空并不能将原链表中的phead置空,
//因此原链表中的phead需要手动置空,详细实现见源码
void DLDesdroy(DL* phead)
{
	assert(phead);
	DL* tail =phead->next;
 
	while (tail != phead)
	{
		DL* next = tail->next;
		free(tail); 
		tail= next;
	}
	free(phead);
	phead = NULL;
}

2.3 双向链表的增删查改

由于多次创建结点,因此我们将它提炼为一个函数

c 复制代码
//创造新的节点
DL* BuyNewNode(DLType  x)
{
	DL* newnode = (DL*)malloc(sizeof(DL));
	if (newnode == NULL)
	{
		perror("malloc is failed!\n");
		return 1;
	}
	newnode->data = x;
	newnode->next = newnode->prev = newnode;
	return newnode;
}
c 复制代码
//尾插
void  DLPushBack(DL* phead,DLType x)
{
	assert(phead);
 
	DL* newnode = BuyNewNode(x);
 
	newnode->next = phead;
	phead->prev->next = newnode;
 
	newnode->prev = phead->prev;
	phead->prev = newnode;
}
 
 
//尾删
void DLPopBack(DL* phead)
{
	assert(phead);
	assert(phead->next != phead);
 
	DL* tail = phead->prev;
	DL* prev = tail->prev;
 
	prev->next = phead;
	phead->prev = prev;
	free(tail);
	tail = NULL;  
 
}
 
//头插
void DLPushFront(DL* phead, DLType x)
{
	assert(phead);
 
	DL* newnode = BuyNewNode(x);
 
	DL* first = phead->next;
 
	newnode->next = first;
	first->prev = newnode;
	phead->next = newnode;
	newnode->prev=phead;
}
 
//头删
void DLPopFront(DL* phead)
{
	assert(phead);
	assert(phead->next != phead);
 
	DL* first = phead->next;
	DL* second = first->next;
 
	phead->next = second;
	second->prev = phead;
 
	free(first);
	first = NULL;
}
 
 
//查找
DL* DLFind(DL* phead,DLType x)
{
	assert(phead);
 
	DL* cur = phead->next;
	while ( cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}
 
 
 
//pos位置插入数据
void DLInsert(DL* pos,DLType x)
{
	assert(pos);
 
	DL* newnode = BuyNewNode(x);
	DL* prev = pos->prev;
    
	prev->next = newnode;
	newnode->prev = prev;
 
    newnode->next = pos;
	pos->prev = newnode;
}
 
//pos位置之后插入数据
void DLInsertAfter(DL* pos, DLType x)
{
	assert(pos);
 
	DL* newnode = BuyNewNode(x);
	DL* next = pos->next;
 
	newnode->next = next;
	next->prev = newnode;
	pos->next = newnode;
	newnode->prev = pos;
}
 
//删除pos位置元素
void  DLErase(DL* pos)
{
	assert(pos);
 
	DL* next = pos->next;
	DL* prev = pos->prev;
 
	prev->next = next;
	next->prev = prev;
 
	free(pos);
	pos = NULL;
}

2.4 双向链表的打印

c 复制代码
//打印链表
void DLPrint(DL* phead)
{
	DL* pcur = phead->next;
	while (pcur != phead)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL\n");
}

2.5 双向链表的源码

c 复制代码
//DL.h
 
#pragma once
 
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
 
typedef int DLType;
 
typedef struct DList
{
	DLType data;
	struct DList* prev;
	struct DList* next;
}DL;
 
//初始化
DL* init(void);
//打印
void DLPrint(DL* phead);
 
//尾插、尾删
void  DLPushBack(DL* phead, DLType x);
void DLPopBack(DL* phead);
 
//头插、头删
void  DLPushFront(DL* phead, DLType x);
void  DLPopFront(DL* phead);
 
//查找
DL* DLFind(DL* phead, DLType x);
 
//指定位置插入数据、指定位置之后插入数据
void  DLInsert(DL* pos, DLType x);
void DLInsertAfter(DL* pos, DLType x);
 
//删除pos
void  DLErase(DL* phead);
 
void DLDesdroy(DL** phead);
c 复制代码
//DL.c
#include "DList.h"
 
 
//创造新的节点
DL* BuyNewNode(DLType  x)
{
	DL* newnode = (DL*)malloc(sizeof(DL));
	if (newnode == NULL)
	{
		perror("malloc is failed!\n");
		return 1;
	}
	newnode->data = x;
	newnode->next = newnode->prev = newnode;
	return newnode;
}
 
 
//初始化phead
DL* init(void)
{
	DL* phead = BuyNewNode(0);
	return phead;
}
 
 
//打印链表
void DLPrint(DL* phead)
{
	DL* pcur = phead->next;
	while (pcur != phead)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL\n");
}
 
 
//尾插
void  DLPushBack(DL* phead,DLType x)
{
	assert(phead);
 
	DL* newnode = BuyNewNode(x);
 
	newnode->next = phead;
	phead->prev->next = newnode;
 
	newnode->prev = phead->prev;
	phead->prev = newnode;
}
 
 
//尾删
void DLPopBack(DL* phead)
{
	assert(phead);
	assert(phead->next != phead);
 
	DL* tail = phead->prev;
	DL* prev = tail->prev;
 
	prev->next = phead;
	phead->prev = prev;
	free(tail);
	tail = NULL;  
 
}
 
//头插
void DLPushFront(DL* phead, DLType x)
{
	assert(phead);
 
	DL* newnode = BuyNewNode(x);
 
	DL* first = phead->next;
 
	newnode->next = first;
	first->prev = newnode;
	phead->next = newnode;
	newnode->prev=phead;
}
 
//头删
void DLPopFront(DL* phead)
{
	assert(phead);
	assert(phead->next != phead);
 
	DL* first = phead->next;
	DL* second = first->next;
 
	phead->next = second;
	second->prev = phead;
 
	free(first);
	first = NULL;
}
 
 
//查找
DL* DLFind(DL* phead,DLType x)
{
	assert(phead);
 
	DL* cur = phead->next;
	while ( cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}
 
 
 
//pos位置插入数据
void DLInsert(DL* pos,DLType x)
{
	assert(pos);
 
	DL* newnode = BuyNewNode(x);
	DL* prev = pos->prev;
    
	prev->next = newnode;
	newnode->prev = prev;
 
    newnode->next = pos;
	pos->prev = newnode;
}
 
//pos位置之后插入数据
void DLInsertAfter(DL* pos, DLType x)
{
	assert(pos);
 
	DL* newnode = BuyNewNode(x);
	DL* next = pos->next;
 
	newnode->next = next;
	next->prev = newnode;
	pos->next = newnode;
	newnode->prev = pos;
}
 
//删除
void  DLErase(DL* pos)
{
	assert(pos);
 
	DL* next = pos->next;
	DL* prev = pos->prev;
 
	prev->next = next;
	next->prev = prev;
 
	free(pos);
	pos = NULL;
}
 
//销毁链表
void DLDesdroy(DL* phead)
{
	assert(phead);
	DL* tail =phead->next;
 
	while (tail != phead)
	{
		DL* next = tail->next;
		free(tail); 
		tail= next;
	}
	free(phead);
	phead = NULL;
}
相关推荐
就爱学编程26 分钟前
重生之我在异世界学编程之C语言小项目:通讯录
c语言·开发语言·数据结构·算法
oneouto26 分钟前
selenium学习笔记(二)
笔记·学习·selenium
sealaugh3231 分钟前
aws(学习笔记第十九课) 使用ECS和Fargate进行容器开发
笔记·学习·aws
北国无红豆1 小时前
【CAN总线】STM32的CAN外设
c语言·stm32·嵌入式硬件
单片机学习之路1 小时前
【C语言】结构
c语言·开发语言·stm32·单片机·51单片机
ALISHENGYA1 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(实战项目二)
数据结构·c++·算法
DARLING Zero two♡2 小时前
【优选算法】Pointer-Slice:双指针的算法切片(下)
java·数据结构·c++·算法·leetcode
LuH11242 小时前
【论文阅读笔记】Scalable, Detailed and Mask-Free Universal Photometric Stereo
论文阅读·笔记
graceyun3 小时前
C语言初阶习题【9】数9的个数
c语言·开发语言