本文章主要是关于双向链表的应用,如:双向链表的初始化、双向链表的打印、尾插、尾删、头插、头删、指定值的删除、指定位置的删除、任意位置的插入、查找等操作。本次对双向链表的操作是关于对带头结点、带环的双向链表的操作。
1.头文件dblinklist.h
//头文件只被编译一次#pragma once
//自定义元素的类型,方便用户去修改元素的数据类型
typedef char DLinkType;
//定义一个结构体,存放节点元素、节点的前驱、节点的后继
typedef struct DLinkNode
{
DLinkType data;
struct DLinkNode* next;
struct DLinkNode* prev;
}DLinkNode; //其中LinkNode是对所定义的结构体的重命名,LinkNode*是定义指向该结构体的指针类型
//宏定义一个标识符,用于测试函数时打印其对应的函数名,方便了代码的编写
#define HEADER printf("============%s===========\n",__FUNCTION__);
2.对双向链表的操作
2.1 创建节点
DLinkNode* CreateDLinkNode(DLinkType 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;
}
2.2 销毁节点
void DestroyDLinkNode(DLinkNode* dst){
free(dst);
}
2.3 打印双向链表
void DLinkListPrintChar(DLinkNode* head,char* msg){
//打印一行语句
printf("%s\n",msg);
//非法输入
if(head==NULL)
return;
//空链表
if(head->next==NULL)
return;
//非空链表
DLinkNode* cur=head->next;
for(cur=head->next;cur!=head;cur=cur->next)
{
printf("[%p][%c] ",cur,cur->data);
}
printf("\n");
for(cur=head->prev;cur!=head;cur=cur->prev)
{
printf("[%p][%c] ",cur,cur->data);
}
printf("\n");
}
2.4 初始化双链表
思路:创建一个头节点表示空链表,该节点无实际意义,只是为了操作方便
void DLinkListInit(DLinkNode** phead){
//非法输入
if(phead==NULL)
return;
*phead=CreateDLinkNode(0);
}
2.5 尾插
思路:创建新节点new_node以及找到最后一个节点tail=head->prev,修改new_node与tail、head的指向
void DLinkListPushBack(DLinkNode* head,DLinkType value){
//非法输入
if(head==NULL)
return;
//找到最后一个节点
DLinkNode* tail=head->prev;
//创建新节点
DLinkNode* new_node=CreateDLinkNode(value);
//修改new_node和tail的指向
tail->next=new_node;
new_node->prev=tail;
//修改new_node和head的指向
new_node->next=head;
head->prev=new_node;
}
2.6 尾删
思路:找到最后一个要删除的节点tail以及倒数第二个节点tail->prev,修改head与tail->prev的指向
void DLinkListPopBack(DLinkNode* head){
//非法输入
if(head==NULL)
return;
//只有头结点时不能删除头节点
if(head->next==head)
return;
//正常删除
DLinkNode* to_delete=head->prev; //要删除的最后一个节点
DLinkNode* new_tail=to_delete->prev; //倒数第二个节点,即尾删后的链表的最后一个节点
head->prev=new_tail;
new_tail->next=head;
//销毁要删除的节点
DestroyDLinkNode(to_delete);
}
2.7 头插
思路:创建新节点new_node,并修改new_node与head、head->next的指向
void DLinkListPushFront(DLinkNode* head,DLinkType value){
//非法输入
if(head==NULL)
return;
//创建新节点new_node
DLinkNode* new_node=CreateDLinkNode(value);
//修改head->next与new_node的指向
DLinkNode* cur=head->next;
cur->prev=new_node;
new_node->next=cur;
//修改new_node与head的指向
new_node->prev=head;
head->next=new_node;
}
2.8 头删
思路:找到要删除的节点head->next,修改head与head->next->next的指向
void DLinkListPopFront(DLinkNode* head){
//非法输入
if(head==NULL)
return;
//判断是否有节点,只有头结点时不能删除
if(head->next==head)
return;
//找到要删除的节点
DLinkNode* to_delete=head->next;
DLinkNode* cur=to_delete->next;
//修改cur与head的指向
head->next=cur;
cur->prev=head;
//销毁要删除的节点
DestroyDLinkNode(to_delete);
}
2.9 给定pos位置,插在pos之前
思路:创建新节点new_node并找到pos的前一个节点prev_node修改new_node与prev_node、pos的指向
void DLinkListFrontInsert(DLinkNode* head,DLinkNode* pos,DLinkType value){
//非法输入
if(head==NULL||pos==NULL)
return;
//创建新节点new_node
DLinkNode* new_node=CreateDLinkNode(value);
//找到pos的前一个节点prev_node
DLinkNode* prev_node=pos->prev;
//修改prev_node与new_node的指向
prev_node->next=new_node;
new_node->prev=prev_node;
//修改new_node与pos的指向
new_node->next=pos;
pos->prev=new_node;
}
2.10 给定pos位置,插在pos之后
思路:创建新节点new_node并找到pos的后一个节点next_node修改new_node与next_node、pos的指向
void DLinkListAfterInsert(DLinkNode* head,DLinkNode* pos,DLinkType value){
//非法输入
if(head==NULL||pos==NULL)
return;
//创建新节点new_node
DLinkNode* new_node=CreateDLinkNode(value);
//找到pos的后一个节点next__node
DLinkNode* next_node=pos->next;
//修改next_node与new_node的指向
next_node->prev=new_node;
new_node->next=next_node;
//修改new_node与pos的指向
new_node->prev=pos;
pos->next=new_node;
}
2.11 按照指定值查找
思路:遍历双向链表去查找指定的值,返回该值的节点位置
DLinkNode* DLinkListFind(DLinkNode* head,DLinkType to_find){
//非法输入
if(head==NULL)
return NULL;
DLinkNode* cur=head->next;
for(;cur!=head;cur=cur->next)
{
if(cur->data==to_find)
return cur;
}
//当不存在或为空链表时
return NULL;
}
2.12 按指定位置pos删除
思路:找到pos的前一个节点prev_node、后一个节点next_node,修改它们的指向
void DLinkListPosErase(DLinkNode* head,DLinkNode* pos){
//非法输入
if(head==NULL)
return;
//pos==head时,不能删除
if(pos==head)
return;
//找到pos的前一个节点prev_node
DLinkNode* prev_node=pos->prev;
//找到pos的后一个节点next_node
DLinkNode* next_node=pos->next;
//修改prev_node和next_node的指向
prev_node->next=next_node;
next_node->prev=prev_node;
//删除pos节点
DestroyDLinkNode(pos);
}
2.13 按值删除
思路:遍历找到该值位置to_delete以及它的前一节点prev_node和后一节点next_prev,并修改它们的指向
void DLinkListValueErase(DLinkNode* head,DLinkType value){
//非法输入
if(head==NULL)
return;
//遍历
DLinkNode* cur=head->next;
for(;cur!=head;cur=cur->next)
{
if(cur->data==value)
{
//当找到value时,记录该位置的前一节点prev_node和后一节点next_node
DLinkNode* prev_node=cur->prev;
DLinkNode* next_node=cur->next;
//修改prev_node和next_node的指向
prev_node->next=next_node;
next_node->prev=prev_node;
//销毁要删除值的位置
DestroyDLinkNode(cur);
}
}
}
2.14 删除所有指定值
思路:用while(1)语句,思路和删除一个指定值类似
void DLinkListAllErase(DLinkNode* head,DLinkType value){
//非法输入
if(head==NULL)
return;
while(1)
{
//先利用DLinkListFind找到指定值的位置
DLinkNode* to_delete=DLinkListFind(head,value);
if(to_delete==NULL)
return;
//再利用DLinkListPosErase删除该位置
DLinkListPosErase(head,to_delete);
}
}
2.15 销毁双向链表
思路:遍历链表进行销毁
void DLinkListDestroy(DLinkNode** phead){
//非法输入
if(phead==NULL)
return;
DLinkNode* cur=(*phead)->next;
//遍历
while(cur!=(*phead))
{
//保存将要删除节点的下一节点
DLinkNode* next=cur->next;
DestroyDLinkNode(cur);
cur=next;
}
//销毁头结点
DestroyDLinkNode(*phead);
//野指针置空
*phead=NULL;
}
3.测试以上代码是否正确实现其功能
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include"dblinklist.h"
//1.测试DLinkListPushBaack
void Test_DLinkListPushBack(){
HEADER;
DLinkNode* head=NULL;
DLinkListInit(&head);
DLinkListPushBack(head,'a');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'c');
DLinkListPushBack(head,'d');
DLinkListPrintChar(head,"尾插元素'a''b''c''d'");
}
//2.测试DLinkListPopBack
void Test_DLinkListPopBack(){
HEADER;
DLinkNode* head=NULL;
DLinkListInit(&head);
DLinkListPushBack(head,'a');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'c');
DLinkListPushBack(head,'d');
DLinkListPopBack(head);
DLinkListPrintChar(head,"尾删一个元素");
}
//3.测试DLinkListPushFront
void Test_DLinkListPushFront(){
HEADER;
DLinkNode* head=NULL;
DLinkListInit(&head);
DLinkListPushFront(head,'a');
DLinkListPushFront(head,'b');
DLinkListPushFront(head,'c');
DLinkListPushFront(head,'d');
DLinkListPrintChar(head,"头插元素'a''b''c''d'");
}
//4.测试DLinkListPopFront
void Test_DLinkListPopFront(){
HEADER;
DLinkNode* head=NULL;
DLinkListInit(&head);
DLinkListPushBack(head,'a');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'c');
DLinkListPushBack(head,'d');
DLinkListPopFront(head);
DLinkListPrintChar(head,"头删一个元素");
}
//5.测试DLinkListFrontInsert
void Test_DLinkListFrontInsert(){
HEADER;
DLinkNode* head=NULL;
DLinkListInit(&head);
DLinkListPushBack(head,'a');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'c');
DLinkListPushBack(head,'d');
DLinkListFrontInsert(head,head->next->next,'x');
DLinkListPrintChar(head,"在'b'之前插入'x'");
}
//6.测试DLinkListAfterInsert
void Test_DLinkListAfterInsert(){
HEADER;
DLinkNode* head=NULL;
DLinkListInit(&head);
DLinkListPushBack(head,'a');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'c');
DLinkListPushBack(head,'d');
DLinkListAfterInsert(head,head->next,'g');
DLinkListPrintChar(head,"在'a'之后插入'g'");
}
//7.测试Test_DLinkListFind
void Test_DLinkListFind(){
HEADER;
DLinkNode* head=NULL;
DLinkListInit(&head);
DLinkListPushBack(head,'a');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'c');
DLinkListPushBack(head,'d');
DLinkNode* ret=DLinkListFind(head,'x');
printf("expected NULL,actual %p\n",ret);
ret=DLinkListFind(head,'c');
printf("expected c,actual %c\n",ret->data);
}
//8.测试Test_DLinkListPosErase
void Test_DLinkListPosErase(){
HEADER;
DLinkNode* head=NULL;
DLinkListInit(&head);
DLinkListPushBack(head,'a');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'c');
DLinkListPushBack(head,'d');
DLinkListPosErase(head,head->next);
DLinkListPrintChar(head,"指定位置pos删除节点'a'");
}
//9.测试Test_DLinkListValueErase
void Test_DLinkListValueErase(){
HEADER;
DLinkNode* head=NULL;
DLinkListInit(&head);
DLinkListPushBack(head,'a');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'c');
DLinkListPushBack(head,'d');
DLinkListValueErase(head,'x');
DLinkListPrintChar(head,"删除不存在的节点'x'");
DLinkListValueErase(head,'a');
DLinkListPrintChar(head,"删除节点'a'");
}
//10.测试Test_DLinkListAllErase
void Test_DLinkListAllErase(){
HEADER;
DLinkNode* head=NULL;
DLinkListInit(&head);
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'a');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'c');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'c');
DLinkListAllErase(head,'x');
DLinkListPrintChar(head,"删除不存在的节点'x'");
DLinkListAllErase(head,'b');
DLinkListPrintChar(head,"删除所有指定值'b'");
}
//11.测试DLinkListDestroy
void Test_DLinkListDestroy(){
HEADER;
DLinkNode* head=NULL;
DLinkListInit(&head);
DLinkListPushBack(head,'b');
DLinkListPushBack(head,'a');
DLinkListPushBack(head,'b');
DLinkListAllErase(head,'x');
DLinkListPrintChar(head,"销毁链表前打印");
DLinkListDestroy(&head);
DLinkListPrintChar(head,"销毁链表后打印");
}
/*===============主函数=============*/
int main(){
Test_DLinkListPushBack();
Test_DLinkListPopBack();
Test_DLinkListPushFront();
Test_DLinkListPopFront();
Test_DLinkListFrontInsert();
Test_DLinkListAfterInsert();
Test_DLinkListFind();
Test_DLinkListPosErase();
Test_DLinkListValueErase();
Test_DLinkListAllErase();
Test_DLinkListDestroy();
return 0;
}
以上就是关于双向链表的相关操作!