数据结构——双链表
目录
- 1 双链表的概念
- 2 双链表的结构
- 3 双链表的操作
- 3.1 双链表的初始化
- 3.2 双链表的尾插
- 3.3 双链表的头插
- 3.4 双链表的尾删
- 3.5 双链表的头删
- 3.6 双链表的查找
- 3.7 在指定位置后插入新结点
- 3.8 删除指定结点
- 3.9 释放双链表
- 4 双链表整体实现
1 双链表的概念
双链表一般指的是双向循环带头结点的链表
循环
链表首尾连接,可以通过尾结点找到头结点,也可以通过头结点找到尾结点
带头结点
在链表的头部设置一个结点,它不存储任何有效数据,用来简化操作
双向
链表的每个结点设置一前一后两个指针,分别指向前一个结点和后一个结点
图示
2 双链表的结构
双链表的每个结点都包含了数据域和一前一后的指针域,因此结构如下:
//双向链表
typedef int LTDataType;
typedef struct ListNode
{struct ListNode* prev; //指向前一个结点的指针LTDataType data; //数据struct ListNode* next; //指向下一个结点的指针
}LTNode;
3 双链表的操作
3.1 双链表的初始化
由于双链表有头结点,因此在创建双链表时,要先为其创建一个头结点,并使它的前后指针指向自己
代码实现
//创建新结点
LTNode* LTCreateNode(LTDataType x)
{LTNode* node = (LTNode*)malloc(sizeof(LTNode));assert(node);node->data = x;node->prev = node;node->next = node;return node;
}//双向链表初始化
LTNode* LTInit()
{LTNode* head = LTCreateNode(-1);return head;
}
3.2 双链表的尾插
head 指向了头结点,node 为新创建的结点,prev 为指向前一个结点的指针,next 为指向后一个结点的指针
void LTPushBack(LTNode* head, LTDataType x)
{assert(head);LTNode* node = LTCreateNode(x);node->prev = head->prev;node->next = head;head->prev = node;node->prev->next = node;
}
3.3 双链表的头插
head 指向了头结点,node 为新创建的结点,prev 为指向前一个结点的指针,next 为指向后一个结点的指针
代码实现
void LTPushFront(LTNode* head, LTDataType x)
{assert(head);LTNode* node = LTCreateNode(x);node->prev = head;node->next = head->next;head->next = node;node->next->prev = node;}
3.4 双链表的尾删
del 为要删除的结点,prev 指向了前一个结点,next 指向了后一个结点
代码实现
void LTPopBack(LTNode* head)
{assert(head && head->prev != head);LTNode* del = head->prev; //记录尾节点del->prev->next = head;head->prev = del->prev;free(del);del = NULL;}
3.5 双链表的头删
del 为要删除的结点,prev 指向了前一个结点,next 指向了后一个结点
代码实现
//头删
void LTPopFront(LTNode* head)
{assert(head && head->next != head);LTNode* del = head->next;del->next->prev = del->prev;del->prev->next = del->next;free(del);del = NULL;
}
3.6 双链表的查找
x 为要查找的值,通过 cur 指针来遍历链表,查找到结点时,返回结点,未查找到则返回空指针
代码实现
LTNode* LTFind(LTNode* head, LTDataType x)
{assert(head);LTNode* cur = head->next;while (cur != head){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}
3.7 在指定位置后插入新结点
pos 为指定的位置,node 为新结点,prev 指针指向前一个结点,next 指针指向后一个结点
代码实现
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* node = LTCreateNode(x);//pos node pos->nextnode->next = pos->next;node->prev = pos;pos->next->prev = node;pos->next = node;
}
3.8 删除指定结点
pos 为指定的结点,prev 指针指向前一个结点,next 指针指向后一个结点
代码实现
void LTErase(LTNode* pos)
{assert(pos);pos->next->prev = pos->prev;pos->prev->next = pos->next;free(pos);pos = NULL;}
3.9 释放双链表
cur 指向要释放的结点,tail 则用来记录下一个要释放的结点
代码实现
void LTDestroy(LTNode* head)
{assert(head);LTNode* cur = head->next;while (cur != head){LTNode* tail = cur->next;free(cur);cur = tail;}free(cur);cur = NULL;
}
4 双链表整体实现
List.c
#include "List.h"
//函数的实现//创建新结点
LTNode* LTCreateNode(LTDataType x)
{LTNode* node = (LTNode*)malloc(sizeof(LTNode));assert(node);node->data = x;node->prev = node;node->next = node;return node;
}//双向链表初始化
LTNode* LTInit()
{LTNode* head = LTCreateNode(-1);return head;
}//打印
void LTPrint(LTNode* head)
{assert(head);LTNode* cur = head->next;while (cur != head){printf("%d->", cur->data);cur = cur->next;}printf("\n");
}//尾插
void LTPushBack(LTNode* head, LTDataType x)
{assert(head);LTNode* node = LTCreateNode(x);//第一种写法//node->prev = head->prev;//node->next = head;//head->prev->next = node;//head->prev = node;//第二种写法node->prev = head->prev;node->next = head;head->prev = node;node->prev->next = node;
}//头插
void LTPushFront(LTNode* head, LTDataType x)
{assert(head);LTNode* node = LTCreateNode(x);//第一种写法//node->prev = head;//node->next = head->next;//head->next->prev = node;//head->next = node;//第二种写法node->prev = head;node->next = head->next;head->next = node;node->next->prev = node;}//尾删
void LTPopBack(LTNode* head)
{assert(head && head->prev != head);LTNode* del = head->prev; //记录尾节点del->prev->next = head;head->prev = del->prev;free(del);del = NULL;}//头删
void LTPopFront(LTNode* head)
{assert(head && head->next != head);LTNode* del = head->next;del->next->prev = del->prev;del->prev->next = del->next;free(del);del = NULL;
}//查找
LTNode* LTFind(LTNode* head, LTDataType x)
{assert(head);LTNode* cur = head->next;while (cur != head){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}//pos后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* node = LTCreateNode(x);//pos node pos->nextnode->next = pos->next;node->prev = pos;pos->next->prev = node;pos->next = node;
}//删除pos结点
void LTErase(LTNode* pos)
{assert(pos);pos->next->prev = pos->prev;pos->prev->next = pos->next;free(pos);pos = NULL;}//释放链表
void LTDestroy(LTNode* head)
{assert(head);LTNode* cur = head->next;while (cur != head){LTNode* tail = cur->next;free(cur);cur = tail;}free(cur);cur = NULL;
}//判断链表是否为空
bool LTEmpty(LTNode* head)
{assert(head);if (head->next == head)return true;return false;
}
List.h
//结构体,函数的声明
#pragma once
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <stdbool.h>//双向链表
typedef int LTDataType;
typedef struct ListNode
{LTDataType data; //数据struct ListNode* prev; //指向前一个结点的指针struct ListNode* next; //指向下一个结点的指针
}LTNode;//初始化
LTNode* LTInit();//打印
void LTPrint(LTNode* head);//尾插
void LTPushBack(LTNode* head, LTDataType x);//头插
void LTPushFront(LTNode* head, LTDataType x);//尾删
void LTPopBack(LTNode* head);//头删
void LTPopFront(LTNode* head);//查找
LTNode* LTFind(LTNode* head, LTDataType x);//pos后插入数据
void LTInsert(LTNode* pos, LTDataType x);//删除pos结点
void LTErase(LTNode* pos);//释放链表
void LTDestroy(LTNode* head);//判断是否为空链表
bool LTEmpty(LTNode* head);