带头结点带环的双向链表的基本操作

首先,我们来认识一下带头结点带环的双向链表的结构:


通过对链表结构的认识,我们可以归纳出,在代码的实现过程中需要注意的是: 

在进行插入节点操作的时候:(1)创建一个节点时,要对四个指针进行修改;(2)进行节点删除操作的时候,只需修改两个指针,释放要删节点的空间即可。 

下面我们来一一介绍链表的基本操作。

首先定义链表的头文件slb.h:

#include <stdio.h>
#include<stdlib.h>
#include<stddef.h> 
typedef char DLinkType;
typedef struct DLinkNode{
    DLinkType data;
    struct DLinkNode* prev;
    struct DLinkNode* next;
}DLinkNode;
void DLinklistInit(DLinkNode** phead);//链表初始化
DLinkNode* CreateDLinkNode(DLinkType value);//创建新节点
void DLinklistPushBack(DLinkNode* head,DLinkType value);//尾插函数
void DLinklistPopBack(DLinkNode* head);//尾删函数
void DLinklistPushFront(DLinkNode* head,DLinkType value);//头插函数
void DLinklistPopFront(DLinkNode* head);//头删函数
void DLinklistInsertBefore(DLinkNode* head,DLinkNode* pos,DLinkType value);//在指定位置之前插入data值为value的节点
void DLinklistInsertBefore(DLinkNode* head,DLinkNode* pos,DLinkType value);//在指定位置之后插入data值为value的节点
DLinkNode* DLinklistFind(DLinkNode* head,DLinkType to_find);//找到指定元素的位置
void DLinklistErase(DLinkNode* head,DLinkNode* to_delete);//删除指定位置的元素
void DLinklistRemove(DLinkNode* head,DLinkType to_delete_value);//删除指定元素第一次出现的位置的节点
void DLinklistRemoveAll(DLinkNode* head,DLinkType to_delete_value);//删除链表中所有与指定元素数据相同的节点
void DLinklistDestroy(DLinkNode** phead);//置链表为空
void DLinklistSize(DLinkNode* head); //计算链表长度

创建新节点

DLinkNode* CreateDLinkNode(DLinkType value){
    DLinkNode* new_node = (DLinkNode*)malloc(sizeof(DLinkNode));
    new_node->data = value;
    new_node->prev = new_node;
    new_node->next = new_node;
    return new_node;
}

销毁节点

void DestroyDLinkNode(DLinkNode* ptr){
    free(ptr);
}

打印函数

这里我们需要注意,双链表的打印不仅需要从头到尾打印,还需要从尾到头打印一次。

void DLinklistPrint(DLinkNode* head,const char* msg){
    printf("%s\n",msg);
    DLinkNode* cur = head->next;
    for(;cur != head;cur = cur->next){
        printf("[%c] ",cur->data);
    }
    printf("\n");
    DLinkNode* cur1 = head->prev;
    for(;cur1 != head;cur1 = cur1->prev){
        printf("[%c] ",cur1->data);
    }
    printf("\n");
}

链表初始化

void DLinklistInit(DLinkNode** phead){
    if(phead == NULL){
        return;
    }
    *phead = CreateDLinkNode(0);        
}      

尾插函数

这里我们需要更改四个指针的指向:


void DLinklistPushBack(DLinkNode* head,DLinkType value){
    if(head == NULL){
        //非法输入
        return;
    }
    DLinkNode* new_node = CreateDLinkNode(value);
    DLinkNode* tail = head->prev;
    //head vs new_node 
    head->prev = new_node;
    new_node->next = head;
    //tail vs new_node
    tail->next = new_node;
    new_node->prev = tail;
}

尾删函数

这里我们需要更改两个函数的指针:

void DLinklistPopBack(DLinkNode* head){
    if(head == NULL){
        //非法输入
        return;
    }
    DLinkNode* to_delete = head->prev;
    DLinkNode* new_tail = to_delete->prev;
    head->prev = new_tail;
    new_tail->next = head;
    DestroyDLinkNode(to_delete);
}

头插函数:

头插函数和尾插函数的基本思想相同,都是改变四个指针的指向。

void DLinklistPushFront(DLinkNode* head,DLinkType value){
    if(head == NULL){
        //非法输入
        return;
    }
    DLinkNode* new_node = CreateDLinkNode(value);
    DLinkNode* head_next = head->next;
    //head vs new_node
    head->next = new_node;
    new_node->prev = head;
    //head_next vs new_node
    new_node->next = head_next;
    head_next->prev = new_node;
}

头删函数:

头删函数与尾删函数的基本思想相同,都是改变两个指针的指向。

void DLinklistPopFront(DLinkNode* head){
    if(head == NULL){
        //非法输入
        return;
    }
    DLinkNode* to_delete = head->next;
    DLinkNode* new_head_next = to_delete->next;
    head->next = new_head_next;
    new_head_next->prev = head;
    DestroyDLinkNode(to_delete);
}

在指定位置之前插入:


void DLinklistInsertBefore(DLinkNode* head,DLinkNode* pos,DLinkType value){
    if(head == NULL || pos == NULL){
       //非法输入
        return;
    }
    DLinkNode* new_node = CreateDLinkNode(value);
    DLinkNode* pos_prev = pos->prev;
    //pos vs new_node
    new_node->next = pos;
    pos->prev = new_node;
    //pos_prev vs new_node
    pos_prev->next = new_node;
    new_node->prev = pos_prev;
}

在指定位置之后插入:


void DLinklistInsertAfter(DLinkNode* head,DLinkNode* pos,DLinkType value){
    if(head == NULL || pos == NULL){
        //非法输入
        return;
    }
    DLinkNode* new_node = CreateDLinkNode(value);
    DLinkNode* pos_next = pos->next;
    //pos vs new_node
    pos->next = new_node;
    new_node->prev = pos;
    //pos_next vs new_node
    new_node->next = pos_next;
    pos_next->prev = new_node;
}

查找指定元素对应的节点位置:

DLinkNode* DLinklistFind(DLinkNode* head,DLinkType to_find){
    if(head == NULL){
        //非法输入
        return;
    }
    DLinkNode* cur = head->next;
    for(;cur != head;cur = cur->next){
        if(cur->data == to_find){
            return cur;
        }
    }
    return NULL;
}

删除指定位置的节点:


void DLinklistErase(DLinkNode* head,DLinkNode* to_delete){
    if(head == NULL || to_delete == NULL){
        return;
    }
    DLinkNode* to_delete_prev = to_delete->prev;
    DLinkNode* to_delete_next = to_delete->next;
    to_delete_prev->next = to_delete_next;
    to_delete_next->prev = to_delete_prev;
    DestroyDLinkNode(to_delete);
}

删除指定元素第一次出现的位置的节点:

先利用Find函数找到指定元素第一次出现的位置,然后用Erase函数删除即可。

void DLinklistRemove(DLinkNode* head,DLinkType to_delete_value){
    if(head == NULL){
        //非法输入
        return;
    }
    DLinkNode* to_delete = DLinklistFind(head,to_delete_value);
    if(to_delete == NULL){
        //未找到
        return;
    }
    DLinklistErase(head,to_delete);
}

删除链表中所有与指定元素数据相同的节点:

只需对Remove函数加上循环即可。

void DLinklistRemoveAll(DLinkNode* head,DLinkType to_delete_value){
    if(head == NULL){
        //非法输入
        return;
    }
    while(1){
        DLinkNode* to_delete = DLinklistFind(head,to_delete_value);
        if(to_delete == NULL){
            return;
        }
        DLinklistErase(head,to_delete);
    }
}

置链表为空:

先将除头节点之外的所有节点销毁,最后销毁头结点。

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;
}

计算链表的长度:

size_t DLinklistSize(DLinkNode* head){
    if(head == NULL){
        return;
    }
    size_t len = 0;
    DLinkNode* cur = head->next;
    while(cur != head){
        ++len;
        cur = cur->next;
    }
    return len; 
}

测试函数

#include "slb.h"
#define PRINT_HEAD printf("\n==========%s==========\n",__FUNCTION__)

void TestInit(){
    PRINT_HEAD;
    DLinkNode* node;
    DLinklistInit(&node);
}


void TestPushBack(){
    PRINT_HEAD;
    DLinkNode* head;
    DLinklistInit(&head);

    DLinklistPushBack(head,'a');
    DLinklistPushBack(head,'b');
    DLinklistPushBack(head,'c');
    DLinklistPushBack(head,'d');
    DLinklistPrint(head,"尾插四个元素");
}


void TestPopBack(){
    PRINT_HEAD;
    DLinkNode* head;
    DLinklistInit(&head);

    DLinklistPushBack(head,'a');
    DLinklistPushBack(head,'b');
    DLinklistPushBack(head,'c');
    DLinklistPushBack(head,'d');
    DLinklistPopBack(head);
    DLinklistPopBack(head);
    DLinklistPrint(head,"尾删两个元素");
    DLinklistPopBack(head);
    DLinklistPopBack(head);
    DLinklistPrint(head,"再尾删两个元素");
    DLinklistPopBack(head);
    DLinklistPrint(head,"对空链表尾删");
}


void TestPushFront(){
    PRINT_HEAD;
    DLinkNode* head;
    DLinklistInit(&head);
        
    DLinklistPushBack(head,'a');
    DLinklistPushBack(head,'b');
    DLinklistPushBack(head,'c');                                                                                                                                                         
    DLinklistPushBack(head,'d');
    DLinklistPrint(head,"尾插四个元素");
    DLinklistPushFront(head,'x');
    DLinklistPushFront(head,'y');
    DLinklistPrint(head,"头插两个元素");
}


void TestPopFront(){
    PRINT_HEAD;
    DLinkNode* head;
    DLinklistInit(&head);

    DLinklistPushBack(head,'a');
    DLinklistPushBack(head,'b'); 
    DLinklistPushBack(head,'c'); 
    DLinklistPushBack(head,'d');
    DLinklistPrint(head,"尾插四个元素");
    DLinklistPopFront(head);
    DLinklistPopFront(head);
    DLinklistPrint(head,"头删两个元素");
    DLinklistPopFront(head);
    DLinklistPopFront(head);
    DLinklistPrint(head,"再头删两个元素");
    DLinklistPopFront(head);
    DLinklistPrint(head,"对空链表进行头删");
}


void TestInsertBefore(){
    PRINT_HEAD;
    DLinkNode* head;
    DLinklistInit(&head);

    DLinklistPushBack(head,'a');
    DLinklistPushBack(head,'b'); 
    DLinklistPushBack(head,'c'); 
    DLinklistPushBack(head,'d');
    DLinkNode* pos_b = DLinklistFind(head,'b');
    DLinklistInsertBefore(head,pos_b,'x');
    DLinklistPrint(head,"在b之前插入x");
}


void TestInsertAfter(){
    PRINT_HEAD;
    DLinkNode* head;
    DLinklistInit(&head);

    DLinklistPushBack(head,'a'); 
    DLinklistPushBack(head,'b'); 
    DLinklistPushBack(head,'c'); 
    DLinklistPushBack(head,'d');
    DLinkNode* pos_b = DLinklistFind(head,'b');
    DLinklistInsertAfter(head,pos_b,'x');
    DLinklistPrint(head,"在b之后插入x");
}


void TestErase(){
    PRINT_HEAD;
    DLinkNode* head;
    DLinklistInit(&head);

    DLinklistPushBack(head,'a'); 
    DLinklistPushBack(head,'b'); 
    DLinklistPushBack(head,'c'); 
    DLinklistPushBack(head,'d');
    DLinkNode* to_delete = DLinklistFind(head,'b');
    DLinklistErase(head,to_delete);
    DLinklistPrint(head,"删除元素b");
}


void TestRemove(){
    PRINT_HEAD;
    DLinkNode* head;
    DLinklistInit(&head);

    DLinklistPushBack(head,'a'); 
    DLinklistPushBack(head,'a'); 
    DLinklistPushBack(head,'b'); 
    DLinklistPushBack(head,'c'); 
    DLinklistPushBack(head,'d');
    DLinklistRemove(head,'a');
    DLinklistPrint(head,"删除第一个元素a");
}


void TestRemoveAll(){
    PRINT_HEAD;
    DLinkNode* head;
    DLinklistInit(&head);

    DLinklistPushBack(head,'a'); 
    DLinklistPushBack(head,'b'); 
    DLinklistPushBack(head,'a'); 
    DLinklistPushBack(head,'c'); 
    DLinklistPushBack(head,'a');
    DLinklistPushBack(head,'d');
    DLinklistRemoveAll(head,'a');
    DLinklistPrint(head,"删除所有的元素a");
}


void TestDestroy(){
    PRINT_HEAD;
    DLinkNode* head;
    DLinklistInit(&head);

    DLinklistPushBack(head,'a'); 
    DLinklistPushBack(head,'b'); 
    DLinklistPushBack(head,'c'); 
    DLinklistPushBack(head,'d');
    DLinklistPrint(head,"尾插四个元素");
    DLinklistDestroy(&head);
    if(head == NULL)
        printf("非法输入");
}


void TestSize(){
    PRINT_HEAD;
    DLinkNode* head;
    DLinklistInit(&head);

    DLinklistPushBack(head,'a');
    DLinklistPushBack(head,'b');
    DLinklistPushBack(head,'c');
    DLinklistPushBack(head,'d');
    DLinklistPrint(head,"尾插四个元素");
    size_t length = DLinklistSize(head);
    printf("length = %d\n",length);
}


int main(){
    TestInit();
    TestPushBack();
    TestPopBack();
    TestPushFront();
    TestPopFront();
    TestInsertBefore();
    TestInsertAfter();
    TestErase();
    TestRemove();
    TestRemoveAll();
    TestDestroy();
    TestSize();
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值