双向链表

        本文章主要是关于双向链表的应用如:双向链表的初始化、双向链表的打印、尾插、尾删、头插、头删、指定值的删除、指定位置的删除、任意位置的插入、查找等操作。本次对双向链表的操作是关于对带头结点、带环的双向链表的操作。

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;

}

以上就是关于双向链表的相关操作!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值