数据结构_线性表_双向链表(C语言实现+基本操作

系列文章目录

单链表(自用-CSDN博客

数据结构_线性表_顺序表(C语言实现+超详细逐步解析-CSDN博客


代码可以在gitee获取:

ListNode · 景鹤/learning_Byte_c - 码云 - 开源中国 (gitee.com)


前言

带头双向循环链表学习!!!


一、基本概念

定义:双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱

为什么选择双向链表?

可以提高链表的查找效率!

直接上图!

结点的结构
  1. 我们把存储数据元素信息的域称为数据域。
  2. 存储数据元素之间的链接信息即下一个存储数据元素地址的部分称为指针域。
  3. 由数据域和指针域组成的数据元素a的存储映像称为节点

 链表结构


二、基本操作

1.定义

typedef int LTDateType;			//便于更改存储类型

typedef struct ListNode {		
    LTDateType data;			//存储数据元素信息
    struct ListNode *pre;		//存储上一个节点信息
    struct ListNode *next;		//存储下一个节点信息
} ListNode;

2.动态申请空间,开辟一个节点

采用malloc函数,动态开辟一块sizeof(ListNode)大小的空间进行存储

LTNode* BuyLTNode(LTDataType x) {//开辟结点
	LTNode* newNode = (LTNode*)malloc(sizeof(LTNode));

	if (newNode == NULL) {
		perror("malloc failed");
		exit(-1);
	}

	newNode->data = x;
	newNode->next = NULL;
	newNode->pre = NULL;

	return newNode;
}

3. 初始化(创建哨兵位)

//采用返回值,不使用二级指针,符合整体的代码风格
LTNode* LTInit() {//初始化  
	LTNode* phead = BuyLTNode(0);

	phead->next = phead;
	phead->pre = phead;

	return phead;
}

4.打印

注意循环停止条件,不等于头指针!

遍历的是循环链表!!!

因此在不等于头指针的时候,才停止。

void LTPrint(LTNode* phead) {//打印

	assert(phead != NULL);

	LTNode* pphead = phead->next;
	while (pphead != phead)	//注意循环停止条件,不等于头指针
	{
		printf("%d->",pphead->data);
		pphead = pphead->next;
	}
	printf("\n");
}

5.尾插

第一种写法: 直接根据尾插特点,进行编写

void LTPushBack(LTNode* phead, LTDataType x) {//尾插

	assert(phead != NULL);


	LTNode* tail = phead->pre;//找到尾结点
	LTNode* newNode = BuyLTNode(x);//开辟新节点
	
	tail->next = newNode;
	newNode->pre = tail;
	newNode->next = phead;
	phead->pre = newNode;	

}

6.尾删



void LTPopBack(LTNode* phead) {//尾删
	assert(phead != NULL);//链表不为空
	assert(phead->next != NULL);//且存在除头节点外的,第二个结点,能够进行删除


//第一种方法
	//LTNode* tail = phead->pre;//找到尾结点
	//LTNode* tailPre = tail->pre;//尾结点之前的结点
	//free(tail);

	//tailPre->next = phead;
	//phead->pre = tailPre;

//第二种方法
	LTErase(phead->pre);
}

7.头插

void LTPushFront(LTNode* phead, LTDataType x) {//头插
	assert(phead != NULL);//链表不为空

//第一种方法
	//LTNode* newNode = BuyLTNode(x);//开辟新节点
	//LTNode* first = phead->next;

	//phead->next = newNode;
	//newNode->pre = phead;

	//first->pre = newNode;
	//newNode->next = first;

//第二种方法
	LTInsert(phead->next,x);
}

8.头删

void LTPopFront(LTNode* phead) {//头删
	assert(phead != NULL);//链表不为空
	assert(phead->next != NULL);//且存在除头节点外的,一个结点,能够进行删除

//第一种方法
	//LTNode* first = phead->next;//首结点
	//LTNode* second = first->next;

	//phead->next = second;
	//second->pre = phead;
	//free(first);

//第二种方法
	LTErase(phead->next);
}

9.插入(在pos之前插入)

可以在头插/尾插 中复用!!!

//可以在头插/尾插 中复用!!!
//在pos之前插入
void LTInsert(LTNode* pos, LTDataType x) {
	assert(pos);

	LTNode* posPre = pos->pre;
	LTNode* newNode = BuyLTNode(x);
	
	posPre->next = newNode;
	newNode->pre = posPre;

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

10.删除pos位置

// 删除pos位置
void LTErase(LTNode* pos) {
	assert(pos);
	
	LTNode* front = pos->pre;
	LTNode* back = pos->next;
	free(pos);

	front->next = back;
	back->pre = front;

}

11.求表长

//表长
void  LTSize(LTNode* phead) {
	assert(phead != NULL);

	int size = 0;
	LTNode* pphead = phead->next;
	while (pphead != phead)	//注意循环停止条件,不等于头指针
	{
		size++;
		pphead = pphead->next;
	}	
	printf("length: %d", size);
}

12.销毁链表

// 销毁(手动置空)
void ListDestory(ListNode* phead) {
	ListNode* curr = phead->next;
	while (curr != phead) {
		ListNode* next = curr->next;
		free(curr);
		curr = next;
	}
	free(phead);
}

三、代码总览

1.头文件 ListNode.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int LTDataType;

typedef struct ListNode {
	struct ListNode* pre;
	struct ListNode* next;
	
	LTDataType data;

}LTNode;

LTNode* BuyLTNode(LTDataType x);//开辟结点

LTNode* LTInit();//初始化   
//采用返回值,不使用二级指针,符合整体的代码风格

void LTPrint(LTNode* phead);//打印
void LTPushBack(LTNode* phead, LTDataType x);//尾插
void LTPopBack(LTNode* phead);//尾删

void LTPushFront(LTNode* phead, LTDataType x);//头插
void LTPopFront(LTNode* phead);//头删

void LTSize(LTNode* phead);


void LTInsert(LTNode* pos, LTDataType x);
 
void LTErase(LTNode* pos);

void ListDestory(ListNode* phead);

2.接口  ListNode.c

#include"ListNode.h"
//双向循环链表
LTNode* BuyLTNode(LTDataType x) {//开辟结点
	LTNode* newNode = (LTNode*)malloc(sizeof(LTNode));

	if (newNode == NULL) {
		perror("malloc failed");
		exit(-1);
	}

	newNode->data = x;
	newNode->next = NULL;
	newNode->pre = NULL;

	return newNode;
}

//采用返回值,不使用二级指针,符合整体的代码风格
LTNode* LTInit() {//初始化  
	LTNode* phead = BuyLTNode(0);

	phead->next = phead;
	phead->pre = phead;

	return phead;
}

void LTPrint(LTNode* phead) {//打印

	assert(phead != NULL);

	LTNode* pphead = phead->next;
	while (pphead != phead)	//注意循环停止条件,不等于头指针
	{
		printf("%d->",pphead->data);
		pphead = pphead->next;
	}
	printf("\n");
}


void LTPushBack(LTNode* phead, LTDataType x) {//尾插

	assert(phead != NULL);

	LTNode* tail = phead->pre;//找到尾结点
	//LTNode* newNode = BuyLTNode(x);//开辟新节点
	//
	//tail->next = newNode;
	//newNode->pre = tail;
	//newNode->next = phead;
	//phead->pre = newNode;

	LTInsert(phead,x);
	
}



void LTPopBack(LTNode* phead) {//尾删
	assert(phead != NULL);//链表不为空
	assert(phead->next != NULL);//且存在除头节点外的,第二个结点,能够进行删除

	//LTNode* tail = phead->pre;//找到尾结点
	//LTNode* tailPre = tail->pre;//尾结点之前的结点
	//free(tail);

	//tailPre->next = phead;
	//phead->pre = tailPre;

	LTErase(phead->pre);
}


void LTPushFront(LTNode* phead, LTDataType x) {//头插
	assert(phead != NULL);//链表不为空

	//LTNode* newNode = BuyLTNode(x);//开辟新节点
	//LTNode* first = phead->next;

	//phead->next = newNode;
	//newNode->pre = phead;

	//first->pre = newNode;
	//newNode->next = first;

	LTInsert(phead->next,x);
}


void LTPopFront(LTNode* phead) {//头删
	assert(phead != NULL);//链表不为空
	assert(phead->next != NULL);//且存在除头节点外的,一个结点,能够进行删除

	//LTNode* first = phead->next;//首结点
	//LTNode* second = first->next;

	//phead->next = second;
	//second->pre = phead;
	//free(first);

	LTErase(phead->next);
}

//表长
void  LTSize(LTNode* phead) {
	assert(phead != NULL);

	int size = 0;
	LTNode* pphead = phead->next;
	while (pphead != phead)	//注意循环停止条件,不等于头指针
	{
		size++;
		pphead = pphead->next;
	}	
	printf("length: %d", size);
}


//可以在头插/尾插 中复用!!!
//在pos之前插入
void LTInsert(LTNode* pos, LTDataType x) {
	assert(pos);

	LTNode* posPre = pos->pre;
	LTNode* newNode = BuyLTNode(x);
	
	posPre->next = newNode;
	newNode->pre = posPre;

	pos->pre = newNode;
	newNode->next = pos;
}
// 删除pos位置
void LTErase(LTNode* pos) {
	assert(pos);
	
	LTNode* front = pos->pre;
	LTNode* back = pos->next;
	free(pos);

	front->next = back;
	back->pre = front;

}

// 销毁(手动置空)
void ListDestory(ListNode* phead) {
	ListNode* curr = phead->next;
	while (curr != phead) {
		ListNode* next = curr->next;
		free(curr);
		curr = next;
	}
	free(phead);
}

3.测试 Test.c

#include"ListNode.h"
void TestList1() {
	LTNode* plist = LTInit();

	LTPushBack(plist,1);
	LTPushBack(plist,2);
	LTPushBack(plist,3);
	LTPushBack(plist,4);

	LTPrint(plist);

	LTPopBack(plist);
	LTPrint(plist);
}

void TestList2() {
	LTNode* plist = LTInit();

	LTPushFront(plist, 5);
	LTPushFront(plist, 2);
	LTPushFront(plist, 3);
	LTPushFront(plist, 4);

	LTPrint(plist);
	LTPopFront(plist);
	LTPrint(plist);
	LTSize(plist);

}

//测试insert在头插尾插中复用
void TestList3() {
	LTNode* plist = LTInit();

	LTPushFront(plist, 1);
	LTPushFront(plist, 2);
	LTPushFront(plist, 3);

	printf("第一次push:");
	LTPrint(plist);

	LTPopBack(plist);

	printf("pop:");
	LTPrint(plist);

	
	

	LTPushBack(plist, 4);
	LTPushBack(plist, 5);
	LTPushBack(plist, 6);
	printf("第2次push:");
	LTPrint(plist);

	LTPopFront(plist);

	printf("pop:");
	LTPrint(plist);

	LTSize(plist);

}
int main() {
	TestList3();
	
	return 0;
}

 四、运行截图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

景鹤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值