数据结构-双向带头循环链表

6、双向链表

双链表也是链表的一种,双链表的每个数据节点中都有两个指针,分别指向前驱节点和后继结点。

双向,带头,循环。以上几种特性可以随机结合,生成一种链表。链表种类有很多,这里重点讲一下双向带头循环链表

1、双向带头循环链表

带头双向循环链表是链表中带头(哨兵位)、双向、循环三种属性的结合体;

带头即带哨兵位,哨兵位只负责存储第一个具有有效数据的节点,本身不存放数据,该处因为为双向循环链表,代表也可访问该链表的尾节点;

双向即表示,每个节点不仅能访问该节点的后一个节点,同时也可访问本节点的前一个节点;

循环即表示,第一个节点的prev指向尾节点;

带头双向循环链表虽然在结构中是所有链表中最为复杂的,但是相比较于单链表的优势在于不需要多次对链表为空进行判断,避免了边界问题;

同时如单链表所言,单链表在进行每次尾删或者尾插时,都需要遍历到尾节点且保存尾节点的前一个节点,而对于双向链表而言,只需要访问头节点的prev即可;

总而言之,双向带头循环链表而言,虽然在结构上要比单链表复杂的多,但是在操作上确是比单链表而言简单;

2、双向带头循环链表的操作

2.1、main.c

#include <stdio.h>
#include "01.h"
int main() {
    fn4();
    return 0;
}

2.2、0.1h

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <assert.h>
typedef int ele;
typedef struct DLink_List_Node {
    struct DLink_List_Node* pre;//指向上一个节点指针
    ele val;//存储节点元素
    struct DLink_List_Node* next;//指向下一个节点指针
}DLK;

void fn4(void);
void printf_menu(void);
DLK* create_node();
DLK* create_list();
void hand_insert(DLK* DList, ele val);
void printf_Dlist(DLK* DList);
void end_insert(DLK* DList, ele val);
void head_delete(DLK* DList);
void end_delete(DLK* DList);
DLK* find_item(DLK* DList, ele val);
void mid_delete(DLK* ptr);
void mid_insert(DLK* DList, ele val, ele newval);
void edit_list(DLK* DList, ele val, ele newval);
void destroy_list(DLK** DList);

2.3、0.1c

#include "01.h"


void fn4(void) {
    printf_menu();
    int order = 0;

    DLK* DList = NULL;//存储双链表的哨兵位(头)

    ele val = 0;
    ele newval = 0;


    while (1) {
        printf("请输入您的操作指令:");
        scanf("%d", &order);
        switch (order) {
        case 1:
            //链表初始化
            DList = create_list();
            break;
        case 2:
            //打印链表
            printf_Dlist(DList);
            break;
        case 3:
            //链表头插
            printf("请输入要头插的元素");
            scanf("%d", &val);
            hand_insert(DList,val);
            break;
        case 4:
            //链表尾插
            printf("请输入要尾插的元素");
            scanf("%d", &val);
            end_insert(DList, val);
            break;
        case 5:
            //链表头删
            head_delete(DList);
            break;
        case 6:
            //链表尾删
            end_delete(DList);
            break;
        case 7:
            //链表的查找
            //思路:找到返回该元素所在的节点指针,找不到返回NULL
            printf("请输入要查找的元素");
            scanf("%d", &val);
            DLK * res = find_item(DList, val);
            if (res == NULL) {
                printf("没有找到此元素\n");
            }
            else {
                printf("找到了该元素%d\n", res->val);
            }
            break;
        case 8:
            //链表的中间删
            //先查找要删除的元素位置,再删除
            printf("请输入要删除的元素");
            scanf("%d", &val);
            DLK* ptr = find_item(DList, val);
            if (ptr == NULL) {
                printf("没有此元素,无法删除\n");
            }
            else {
                mid_delete(ptr);
            }
            break;
        case 9:
            //链表的中间插  在指定元素后插入
            printf("请输入您要在哪个元素后插入新元素");
            scanf("%d", &val);
            printf("请输入要插入的新元素");
            scanf("%d", &newval);
            mid_insert(DList, val, newval);
            break;
        case 10:
            //链表修改
            printf("请输入您要要修改的元素");
            scanf("%d", &val);
            printf("请输入修改后的新元素");
            scanf("%d", &newval);
            edit_list(DList, val, newval);
            break;
        case 11:
            //链表销毁
            destroy_list(&DList);
            break;
        case 12:
            //退出
            return;

        default:
            printf("指令输入有误");
            break;
        }
    }
}

//打印菜单
void printf_menu(void) {
    system("cls");//系统函数 用于屏幕清空
    printf("操作指令说明:\n");
    printf("1:链表初始化\n2:打印链表\n");
    printf("3:链表头插\n4:链表尾插\n");
    printf("5:链表头删\n6:链表尾删\n");
    printf("7:链表的查找\n8:链表的中间删\n");
    printf("9:链表的中间插\n10:链表修改\n");
    printf("11:链表销毁\n12:退出\n");
}

//辅助函数,创建新节点
DLK* create_node() {
    DLK* newnode = (DLK*)malloc(sizeof(DLK));
    if (newnode == NULL) {
        printf("内存申请失败,无法创建新节点\n");
        return NULL;
    }
    //内存清0
    memset(newnode, 0, sizeof(DLK));
    return newnode;
}


//初始化哨兵位
DLK* create_list() {
    //创建新节点
    DLK* newnode = create_node();
    //给新节点赋值
    newnode->pre = newnode;
    newnode->next = newnode;
    printf("初始化成功\n");
    return newnode;

}

//头插
void hand_insert(DLK* DList, ele val) {
    assert(DList);
    //创建新节点
    DLK* newnode = create_node();
    //给新节点赋值
    newnode->val = val;
    //将新节点插入
    newnode->next = DList->next;
    DList->next->pre = newnode;
    newnode->pre = DList;
    DList->next = newnode;
    printf("头插成功\n");
}

//打印
void printf_Dlist(DLK* DList) {
    assert(DList);
    DLK* temp = DList->next;//存储当前节点
    while (temp!= DList) {
        printf("%d ", temp->val);
        temp = temp->next;
    }
    printf("\n");
}

//尾插
void end_insert(DLK* DList, ele val) {
    assert(DList);
    //创建新节点
    DLK* newnode = create_node();
    newnode->val= val,
    newnode->pre = DList->pre, 
    DList->pre->next = newnode;
    DList->pre = newnode;
    newnode->next = DList;
    printf("尾插成功\n");
}

//头删
void head_delete(DLK* DList) {
    DLK* temp = DList->next;//保存头元素节点
    DList->next = temp->next;
    temp->next->pre = DList;
    free(temp);
    printf("头删成功\n");
}

//尾删
void end_delete(DLK* DList) {
    DLK* temp = DList->pre;//保存尾元素节点
    DList->pre = temp->pre;
    temp->pre->next = DList;
    free(temp);
    printf("尾删成功\n");
}

//查找
DLK* find_item(DLK* DList, ele val) {
    assert(DList);
    //遍历链表元素节点,判断是否是目标元素
    DLK* temp = DList->next;//存储当前节点
    while (temp != DList) {
        if (temp->val == val) {
            return temp;
        }
        temp = temp->next;
    }
    return NULL;
}

//中间删
void mid_delete(DLK* ptr) {
    ptr->pre->next = ptr->next;
    ptr->next->pre = ptr->pre;
    free(ptr);//释放当前节点
    printf("中间删成功\n");
}

//中间插
void mid_insert(DLK* DList, ele val, ele newval) {
    assert(DList);
    //先找到val元素的位置
    DLK* ptr = find_item(DList, val);
    if (ptr == NULL) {
        printf("无法插入\n");
        return;
    }


    //插入操作
    //创建新节点
    DLK* newnode = create_node();
    newnode->val = newval;
    newnode->next = ptr->next;
    ptr->next->pre = newnode;
    ptr->next = newnode;
    newnode->pre = ptr;
    printf("中间插成功\n");
}

//元素修改
void edit_list(DLK* DList, ele val, ele newval) {
    assert(DList);
    //先查找va1元素所在的位置
    DLK* ptr = find_item(DList, val);
    if (ptr == NULL) {
        printf("您要修改的元素不存在\n");
    }
    ptr->val = val;
    printf("修改成功\n");
}

//销毁
void destroy_list(DLK** DList) {
    DLK* temp = (*DList)->next;
    while (temp != *DList) {
        DLK* ptr = temp->next;//先保存下一个元素
        free(temp);
        temp = ptr;
    }
    free(*DList);//释放哨兵位
    *DList = NULL;
    printf("销毁成功\n");
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值