系列文章目录
数据结构_线性表_顺序表(C语言实现+超详细逐步解析-CSDN博客
代码可以在gitee获取:
前言
带头双向循环链表学习!!!
一、基本概念
定义:双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱
为什么选择双向链表?
可以提高链表的查找效率!
直接上图!
结点的结构
- 我们把存储数据元素信息的域称为数据域。
- 存储数据元素之间的链接信息即下一个存储数据元素地址的部分称为指针域。
- 由数据域和指针域组成的数据元素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;
}