双向链表
- 前面的博客向大家介绍了单链表,我们发现单链表在进行查询、删除、添加、修改链表的值时都需要遍历整个链表,这样无疑增加了对链表操作的时间复杂度。而双向链表由于有一个prev指针,这样可以更快的找到前一个节点,减小了时间复杂度,但是,相对应的,由于多开辟了个新的指针,会增加空间复杂度。单链表和双向链表就是一个用空间换时间的例子
- 比如快餐店订餐时就适合单链表,因为一般领餐后不需要叫上一个顾客;设计系统流程的时候就可以用双链表,因为经常查看前一流程和后一流程,使用时要看具体的情况。
下面我将介绍双向链表的一些基本操作。
首先,我们来看一个双向链表:
将x节点插入、修改、删除双向链表都只需找到要修改位置的前后节点,然后分别修改两对指针:prev和next即可。
一些基本操作的代码如下:
//DLinkList.h
#pragma once
#include<stdio.h>
#include<unistd.h>
#include<stddef.h>
#include<stdlib.h>
#define TestType printf("\n###################################### %s ########################################\n",__FUNCTION__)
#define DLinkNodeType char
typedef struct DLinkNode{
DLinkNodeType data;
struct DLinkNode* prev;
struct DLinkNode* next;
}DLinkNode;
//初始化链表
void DLinkListInit(DLinkNode** phead);
//创建新的节点
DLinkNode* DLinkListCreateNode(DLinkNodeType value);
//打印双向链表
void DLinkListPrint(DLinkNode* head,const char* msg);
//尾插
void DLinkListPushBack(DLinkNode* head,DLinkNodeType value);
//头插
void DLinkListPushFront(DLinkNode* head,DLinkNodeType value);
//尾删
void DLinkListPopBack(DLinkNode* head);
//头删
void DLinkListPopFront(DLinkNode* head);
//在指定位置后插入
void DLinkListInsert(DLinkNode* head,DLinkNode* pos,DLinkNodeType value);
//寻找节点
DLinkNode* DLinkListFind(DLinkNode* head,DLinkNodeType to_find);
//删除指定位置的节点
void DLinkListErasePos(DLinkNode* head,DLinkNode* pos);
//删除指定值的节点
void DLinkListEraseValue(DLinkNode* head,DLinkNodeType to_delete);
//销毁链表
void DestroyDLinkList(DLinkNode** phead);
//求链表长度
size_t DLinkListSize(DLinkNode* head);
//在指定位置前插入节点
void DLinkListInsertBefore(DLinkNode* head,DLinkNode* pos,DLinkNodeType value);
//DLinkList.c
#include"DLinkList.h"
DLinkNode* DLinkListCreateNode(DLinkNodeType value){
DLinkNode* new_node = (DLinkNode*)malloc(sizeof(DLinkNode));
new_node->data = value;
new_node->next = new_node;
new_node->prev = new_node;
return new_node;
}
void DLinkListDestroyNode(DLinkNode* to_delete){
free(to_delete);
}
void DLinkListInit(DLinkNode** phead){
if(phead == NULL){
return;
}
if(*phead == NULL){
return;
}
*phead = DLinkListCreateNode('0');
return;
}
void DLinkListPrint(DLinkNode* head,const char* msg){
if(head == NULL){
printf("链表不存在!非法输入!\n");
return;
}
printf("[ %s ]:\n",msg);
DLinkNode* cur = head;
if(cur->next == head){
printf("[%c][%p]\n",cur->data,cur);
return;
}
printf("[%c][%p]<=>",cur->data,cur);
cur = cur->next;
while(cur != head){
printf("[%c][%p]<=>",cur->data,cur);
cur = cur->next;
}
printf("[%c][%p]\n",cur->data,cur);
return;
}
void DLinkListPushBack(DLinkNode* head,DLinkNodeType value){
if(head == NULL){
return;
}
DLinkNode* new_node = DLinkListCreateNode(value);
DLinkNode* cur = head->prev;
cur->next = new_node;
new_node->prev = cur;
new_node->next = head;
head->prev = new_node;
return;
}
void DLinkListPushFront(DLinkNode* head,DLinkNodeType value){
if(head == NULL){
return;
}
DLinkNode* new_node = DLinkListCreateNode(value);
DLinkNode* tmp = head->next;
head->next = new_node;
new_node->prev = head;
new_node->next = tmp;
tmp->prev = new_node;
return;
}
void DLinkListPopBack(DLinkNode* head){
if(head == NULL){
return;
}
if(head->next == head){
printf("无法删除头结点!\n");
return;
}
DLinkNode* to_delete = head->prev;
to_delete->prev->next = head;
head->prev = to_delete->prev;
DLinkListDestroyNode(to_delete);
}
void DLinkListPopFront(DLinkNode* head){
if(head == NULL){
return;
}
if(head->next == head){
printf("无法删除头结点!\n");
return;
}
DLinkNode* to_delete = head->next;
DLinkNode* to_delete_next = to_delete->next;
head->next = to_delete_next;
to_delete_next->prev = head;
DLinkListDestroyNode(to_delete);
}
DLinkNode* DLinkListFind(DLinkNode* head,DLinkNodeType to_find){
if(head == NULL){
return NULL;
}
DLinkNode* cur = head->next;
while(cur != head){
if(cur->data == to_find){
return cur;
}
cur = cur->next;
}
return NULL;
}
//pos之后插入节点
void DLinkListInsert(DLinkNode* head,DLinkNode* pos,DLinkNodeType value){
if(head == NULL || pos == NULL){
return;
}
DLinkNode* new_node = DLinkListCreateNode(value);
DLinkNode* pos_next = pos->next;
pos->next = new_node;
new_node->prev = pos;
new_node->next = pos_next;
pos_next->prev = new_node;
return;
}
//pos之前插入节点
void DLinkListInsertBefore(DLinkNode* head,DLinkNode* pos,DLinkNodeType value){
if(head == NULL || pos == NULL){
return;
}
DLinkNode* pos_prev = pos->prev;
DLinkNode* new_node = DLinkListCreateNode(value);
pos_prev->next = new_node;
new_node->prev = pos_prev;
new_node->next = pos;
pos->prev = new_node;
return;
}
//删除指定位置的节点
void DLinkListErasePos(DLinkNode* head,DLinkNode* pos){
if(head == NULL || pos == NULL){
return;
}
DLinkNode* pos_prev = pos->prev;
DLinkNode* pos_next = pos->next;
pos_prev->next = pos_next;
pos_next->prev = pos_prev;
DLinkListDestroyNode(pos);
return;
}
//删除指定值的节点
void DLinkListEraseValue(DLinkNode* head,DLinkNodeType to_delete){
if(head == NULL){
return;
}
DLinkNode* to_delete_pos = DLinkListFind(head,to_delete);
DLinkListErasePos(head,to_delete_pos);
return;
}
//销毁链表
void DestroyDLinkList(DLinkNode** phead){
if(phead == NULL){
return;
}
if(*phead == NULL){
return;
}
DLinkNode* cur = (*phead)->next;
while(cur != *phead){
DLinkListErasePos(*phead,cur);
cur = (*phead)->next;
}
DLinkListDestroyNode(*phead);
*phead = NULL;
return;
}
size_t DLinkListSize(DLinkNode* head){
if(head == NULL){
return 0;
}
DLinkNode* cur = head->next;
size_t count = 0;
while(cur != head){
count++;
cur = cur->next;
}
return count;
}
//测试函数main.c
#include"DLinkList.h"
void TestDLinkListInit(){
TestType;
DLinkNode* head;
DLinkListInit(&head);
DLinkListPrint(head,"初始化链表");
return;
}
void TestDLinkListPushBack(){
TestType;
DLinkNode* head;
DLinkListInit(&head);
DLinkListPushBack(head,'a');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'c');
DLinkListPushBack(head,'d');
DLinkListPrint(head,"尾插abcd");
return;
}
void TestDLinkListPushFront(){
TestType;
DLinkNode* head;
DLinkListInit(&head);
DLinkListPushFront(head,'a');
DLinkListPushFront(head,'b');
DLinkListPushFront(head,'c');
DLinkListPushFront(head,'d');
DLinkListPrint(head,"头插abcd");
}
void TestDLinkListPopBack(){
TestType;
DLinkNode* head;
DLinkListInit(&head);
DLinkListPopBack(head);
DLinkListPushBack(head,'a');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'c');
DLinkListPushBack(head,'d');
DLinkListPrint(head,"尾插abcd");
DLinkListPopBack(head);
DLinkListPrint(head,"尾删d");
return;
}
void TestDLinkListPopFront(){
TestType;
DLinkNode* head;
DLinkListInit(&head);
DLinkListPopFront(head);
DLinkListPushBack(head,'a');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'c');
DLinkListPushBack(head,'d');
DLinkListPrint(head,"尾插abcd");
DLinkListPopFront(head);
DLinkListPrint(head,"头删a");
return;
}
void TestDLinkListInsert(){
TestType;
DLinkNode* head;
DLinkListInit(&head);
DLinkListPushBack(head,'a');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'c');
DLinkListPushBack(head,'d');
DLinkListPrint(head,"尾插abcd");
DLinkNode* pos = DLinkListFind(head,'c');
DLinkListInsert(head,pos,'x');
DLinkListPrint(head,"c后插入x");
DLinkNode* pos2 = DLinkListFind(head,'d');
DLinkListInsert(head,pos2,'y');
DLinkListPrint(head,"d后插入y");
return;
}
void TestDLinkListInsertBefore(){
TestType;
DLinkNode* head;
DLinkListInit(&head);
DLinkListPushBack(head,'a');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'c');
DLinkListPushBack(head,'d');
DLinkListPrint(head,"尾插abcd");
DLinkNode* pos_c = DLinkListFind(head,'c');
DLinkListInsertBefore(head,pos_c,'x');
DLinkListPrint(head,"c前插入x");
DLinkNode* pos_a = DLinkListFind(head,'a');
DLinkListInsertBefore(head,pos_a,'y');
DLinkListPrint(head,"a前插入y");
return;
}
void TestDLinkListErasePos(){
TestType;
DLinkNode* head;
DLinkListInit(&head);
DLinkListPushBack(head,'a');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'c');
DLinkListPushBack(head,'d');
DLinkListPrint(head,"尾插abcd");
DLinkNode* pos_c = DLinkListFind(head,'c');
DLinkListInsert(head,pos_c,'x');
DLinkListPrint(head,"c后插入x");
DLinkNode* pos_x = DLinkListFind(head,'x');
DLinkListErasePos(head,pos_x);
DLinkListPrint(head,"删除x");
DLinkNode* pos_d = DLinkListFind(head,'d');
DLinkListInsert(head,pos_d,'y');
DLinkListPrint(head,"d后插入y");
DLinkNode* pos_y = DLinkListFind(head,'y');
DLinkListErasePos(head,pos_y);
DLinkListPrint(head,"删除y");
return;
}
void TestDLinkListEraseValue(){
TestType;
DLinkNode* head;
DLinkListInit(&head);
DLinkListPushBack(head,'a');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'c');
DLinkListPushBack(head,'d');
DLinkListPrint(head,"尾插abcd");
DLinkNode* pos_c = DLinkListFind(head,'c');
DLinkListInsert(head,pos_c,'x');
DLinkListPrint(head,"c后插入x");
DLinkListEraseValue(head,'x');
DLinkListPrint(head,"删除x");
return;
}
void TestDestroyDLinkList(){
TestType;
DLinkNode* head;
DLinkListInit(&head);
DLinkListPushBack(head,'a');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'c');
DLinkListPushBack(head,'d');
DLinkListPrint(head,"尾插abcd");
DestroyDLinkList(&head);
DLinkListPrint(head,"销毁链表");
return;
}
void TestDLinkListSize(){
TestType;
DLinkNode* head;
DLinkListInit(&head);
DLinkListPushBack(head,'a');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'c');
DLinkListPushBack(head,'d');
DLinkListPushBack(head,'e');
DLinkListPushBack(head,'f');
size_t size = DLinkListSize(head);
printf("expect:6,actual:%zu\n",size);
}
int main(){
TestDLinkListInit();
TestDLinkListPushBack();
TestDLinkListPushFront();
TestDLinkListPopBack();
TestDLinkListPopFront();
TestDLinkListInsert();
TestDLinkListInsertBefore();
TestDLinkListErasePos();
TestDLinkListEraseValue();
TestDestroyDLinkList();
TestDLinkListSize();
printf("\n\n\n");
return 0;
}
最后奉上Makefile
DLinkList:DLinkList.c main.c
gcc $^ -o $@ -g
.PHONY:clean
clean:
rm -f DLinkList
由于个人水平有限,以上代码可能有BUG或者不妥之处,欢迎发邮件到我的邮箱(Cyrus_wen@163.vom)批评指正。