双链表详解

目录

一.双链表的定义

二.要实现的功能

三.功能实现

0、创建结点

1、初始化

2、打印

3、尾插

4、尾删

5、头插

6、头删

7、任意插入

8、任意删除

9、查找

四.全部代码的实现

1、text.c

2、List.c

3、List.h


一.双链表的定义

双链表详解_第1张图片

由图这就是一个双链表,可以看出它是一个由尾和头双向指向的环,且每个结点之间都是双向指向的。

二.要实现的功能

双链表是一种数据结构,所谓数据结构就是用来储存数据的工具,功能大致就和基本储存数据的数组或单链表相类似。无非就是:

初始化、打印、尾插、尾删、头插、头删、任意插入、任意删除、查找、更改。这些基本的数据操作。

三.功能实现


0、创建结点


即创建一个新的双链表结点,由于不知道应该连接在什么地方,所以且先让它指向NULL。在将需要的数据放在新的结点中,这样新链表的初始化就好了

双链表详解_第2张图片

 


//创建结点
ListNode* BuyListNode(ListDateType x)
{
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	if (node == NULL)
	{
		exit(-1);
	}
	node->next = NULL;
	node->prev = NULL;
	node->date = x;
	return node;
}

1、初始化


即创建一个空的双链表,由双链表的特新,双向指向,我们需要创建一个由中间指向自己的哨兵位的头结点,不存放有效的数据


双链表详解_第3张图片


//初始化
ListNode* ListInit()
{
	ListNode* phead = BuyListNode(0);

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

	return phead;
}

2、打印


打印需要遍历一遍链表,所以要用到循环,那么就需要结束条件,由下图可以看出,头和尾是相连的,所以循环结束的的条件就是当访问的下一个结点是头结点时就停止


双链表详解_第4张图片


 

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

3、尾插


先创建一个新的结点,再将其与tail尾结点连接起来即可,因为双链表的特性,所以可以直接由头结点得到尾结点,而不需要去遍历一遍链表,时间复杂度就直线降低为o(1)了


双链表详解_第5张图片


//尾插
void ListPushBack(ListNode* phead, ListDateType x)
{
	assert(phead);
	ListNode* newnode = BuyListNode(x);
	ListNode* tail = phead->prev;

	tail->next = newnode;
	newnode->prev = tail;

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

	//ListInsert(phead, x);//可以用任意插入简化简化
}

4、尾删


尾删只需要记录tail尾结点的前一个结点即可,将头节点与尾结点连接起来后,再进行tail结点的删除


双链表详解_第6张图片


 

//尾删
void ListPopBack(ListNode* phead)
{
	assert(phead);
	assert(phead->next != NULL);

	ListNode* tailprev = phead->prev->prev;
	ListNode* tail = phead->prev;
	free(tail);
	tail = NULL;

	tailprev->next = phead;
	
	phead->prev = tailprev;

	//ListErase(phead->prev);//用任意删除简化
}

5、头插

因为双链表有一个哨兵位的头结点,所以头插就相当于是在phead头结点和frist第一个结点中间插入一个结点,可以先记录第一个结点,在将新的结点与头结点连接起来后,再将新结点与第一个结点连接起来



双链表详解_第7张图片


 

void ListPushFront(ListNode* phead, ListDateType x)
{
	assert(phead);

	ListNode* newnode = BuyListNode(x);
	ListNode* frist = phead->next;

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

	newnode->next = frist;
	frist->prev = newnode;

	//ListInsert(phead->next, x);//用任意插入简化
}

6、头删


头删就像相当于在头结点与第一个结点之间删除一个结点,记录第一个结点,将头节点与第二个结点删除后,再q第一个结点。


双链表详解_第8张图片


//头删
void ListPopFront(ListNode* phead)
{
	assert(phead);
	assert(phead->next != phead);

	ListNode* frist = phead->next;
	ListNode* second = frist->next;

	phead->next = second;
	second->prev = phead;

	free(frist);
	frist = NULL;

	//ListErase(phead->next);用任意删除简化
}

7、任意插入


对双链表来说就是两个结点之间的插入,因为每一个链表旁边一定有两个结点(环结构)。

首先先遍历一遍链表找到这个待插入处的结点,然后记录待插入结点处的前一个结点,再将新结点连接这个记录的结点,然后再连接待插入结点处的结点。


双链表详解_第9张图片


 

//任意结点插入
void ListInsert(ListNode* pos, ListDateType x)
{
	assert(pos);
	ListNode* newNode = BuyListNode(x);
	ListNode* posPrev = pos->prev;

	newNode->prev = posPrev;
	posPrev->next = newNode;

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

8、任意删除


任意的删除在单链表中我们分了好几种情况,单在双链表中我们只需要按正常的情况考虑即可,因为它没有NULL指向这种非法访问空间机会。

删除时需要记录待删除结点pos前和后的结点,在将前后两个结点连接后再进行pos结点的删除


双链表详解_第10张图片


 

//任意结点的删除
void ListErase(ListNode* pos)
{
	assert(pos);

	ListNode* posPrev = pos->prev;
	ListNode* posNext = pos->next;

	posPrev->next = posNext;
	posNext->prev = posPrev;

	free(pos);
	pos = NULL;
}

9、查找


遍历链表对比数据,找到值返回那个结点就行了


//查找
ListNode* ListFind(ListNode* phead, ListDateType x)
{
	assert(phead);
	assert(phead->next != NULL);

	ListNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->date == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	printf("找不到\n");
	return NULL;
}

四.全部代码的实现

1、text.c

#define  _CRT_SECURE_NO_WARNINGS 1

#include "List.h"

void ListText()
{
	ListNode* phead = NULL;
	phead = ListInit();
	ListPushBack(phead,1);
	ListPushBack(phead,2);
	ListPushBack(phead,3);
	ListPrint(phead);
	ListPopBack(phead);
	ListPrint(phead);
	ListPushFront(phead, 99);
	ListPrint(phead);
	ListPopFront(phead);
	ListPrint(phead);
}

void ListText1()
{
	ListNode* phead = ListInit();
	ListPushBack(phead, 1);
	ListPushBack(phead, 2);
	ListPushBack(phead, 3);
	ListPushBack(phead, 4);
	ListPushBack(phead, 5);

	ListNode* pos = ListFind(phead, 3);
	ListInsert(pos, 99);
	ListErase(pos);
	ListPrint(phead);

	ListDestory(&phead);
}
int main()
{
	//ListText();
	ListText1();

	return 0;
}

2、List.c

#define  _CRT_SECURE_NO_WARNINGS 1

#include "List.h"
//创建结点
ListNode* BuyListNode(ListDateType x)
{
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	if (node == NULL)
	{
		exit(-1);
	}
	node->next = NULL;
	node->prev = NULL;
	node->date = x;
	return node;
}
//初始化
ListNode* ListInit()
{
	ListNode* phead = BuyListNode(0);

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

	return phead;
}
//清理
void ListClear(ListNode* phead)
{
	assert(phead);
	//清理全部数据,保留头结点
	ListNode* cur = phead->next;
	while (cur != phead)
	{
		ListNode* next = cur->next;
		free(cur);
		cur = NULL;
		cur = next;
	}
}
//销毁
void ListDestory(ListNode** pphead)
{
	assert(*pphead);

	ListClear(*pphead);
	free(*pphead);
	*pphead = NULL;
}
//打印
void ListPrint(ListNode* phead)
{
	assert(phead);
	ListNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d ", cur->date);
		cur = cur->next;
	}
	printf("\n");
}
//尾插
void ListPushBack(ListNode* phead, ListDateType x)
{
	/*assert(phead);
	ListNode* newnode = BuyListNode(x);
	ListNode* tail = phead->prev;

	tail->next = newnode;
	newnode->prev = tail;

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

	ListInsert(phead, x);
}
//尾删
void ListPopBack(ListNode* phead)
{
	/*assert(phead);
	assert(phead->next != NULL);

	ListNode* tailprev = phead->prev->prev;
	ListNode* tail = phead->prev;
	free(tail);
	tail = NULL;

	tailprev->next = phead;
	
	phead->prev = tailprev;*/

	ListErase(phead->prev);
}
//头插
void ListPushFront(ListNode* phead, ListDateType x)
{
	/*assert(phead);

	ListNode* newnode = BuyListNode(x);
	ListNode* frist = phead->next;

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

	newnode->next = frist;
	frist->prev = newnode;*/

	ListInsert(phead->next, x);
}
//头删
void ListPopFront(ListNode* phead)
{
	/*assert(phead);
	assert(phead->next != phead);

	ListNode* frist = phead->next;
	ListNode* second = frist->next;

	phead->next = second;
	second->prev = phead;

	free(frist);
	frist = NULL;*/

	ListErase(phead->next);
}
//查找
ListNode* ListFind(ListNode* phead, ListDateType x)
{
	assert(phead);
	assert(phead->next != NULL);

	ListNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->date == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	printf("找不到\n");
	return NULL;
}
//任意结点插入
void ListInsert(ListNode* pos, ListDateType x)
{
	assert(pos);
	ListNode* newNode = BuyListNode(x);
	ListNode* posPrev = pos->prev;

	newNode->prev = posPrev;
	posPrev->next = newNode;

	pos->prev = newNode;
	newNode->next = pos;
}
//任意结点的删除
void ListErase(ListNode* pos)
{
	assert(pos);

	ListNode* posPrev = pos->prev;
	ListNode* posNext = pos->next;

	posPrev->next = posNext;
	posNext->prev = posPrev;

	free(pos);
	pos = NULL;
}

3、List.h

#pragma once

#include 
#include 
#include 

typedef int ListDateType;
//双向链表
typedef struct ListNode
{
	struct ListNode* next;
	struct ListNode* prev;
	ListDateType date;
}ListNode;

//初始化
ListNode* ListInit();
//销毁
void ListDestory(ListNode** pphead);
//清理
void ListClear(ListNode* phead);
//打印
void ListPrint(ListNode* phead);
//尾插
void ListPushBack(ListNode* phead, ListDateType x);
//尾删
void ListPopBack(ListNode* phead);
//头插
void ListPushFront(ListNode* phead, ListDateType x);
//头删
void ListPopFront(ListNode* phead);
//查找
ListNode* ListFind(ListNode* phead, ListDateType x);
//任意结点插入
void ListInsert(ListNode* pos, ListDateType x);
//任意结点的删除
void ListErase(ListNode* pos);

你可能感兴趣的:(数据结构,数据结构)