首页 > 其他分享 >关于 双向不循环列表的创建、插入、删除、遍历、检索、销毁

关于 双向不循环列表的创建、插入、删除、遍历、检索、销毁

时间:2024-05-19 18:53:32浏览次数:15  
标签:检索 node head 遍历 Double 链表 销毁 printf 节点

双向循环链表公式

image

双向不循环链表代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//宏定义一个数据域 
#define DATA_LEN 60

//双向不循环链表的节点定义
typedef struct double_link_list
{
    //数据域
    char data[DATA_LEN];            // 数据域,存储数据长度

    //指针域
    struct double_link_list *next;  //后继指针,指向下一个节点的指针
    struct double_link_list *prev;  //前驱指针,指向前一个节点的指针
}Double_Link_List, * P_Double;       // 定义节点类型和指针类型别名



/* ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 函数声明文件 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ */

P_Double Create_New_Node();                             //声明一个创建新节点函数
P_Double Retrieve_Add_Node(P_Double head_node);         //声明一个检索函数
int Mode_Select(P_Double head_node);                    //声明一个功能选择函数
int Head_Add_Node(P_Double head_node);                  //声明一个头插函数
int Ergodic_List_Node(P_Double head_node);              //声明一个遍历函数
int Tail_Add_Node(P_Double head_node);                  //声明一个尾插函数
int Appoint_Add_Node(P_Double head_node);               //声明一个指定添加函数
int Del_Add_Node(P_Double head_node);                   //声明一个指定删除函数
int Destory_Double_Link_list(P_Double head_node);       //声明一个摧毁双向链表函数

/* ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 函数声明文件 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ */

// 定义一个函数Create_New_Node,来创建一个新节点
P_Double Create_New_Node()
{
    //创建新节点并申请堆内存
    P_Double new_node = (P_Double)malloc(sizeof(Double_Link_List));
    //判断申请空间是否成功
    if(new_node == (P_Double)NULL)
    {
        perror("malloc ...");
        return (P_Double)-1;
    }

    //清空新节点的数据区域,进行初始化
    memset(new_node,0,sizeof(Double_Link_List));

    //新节点的指针指向空
    new_node->next = (P_Double)NULL;
    new_node->prev = (P_Double)NULL;
    
    return new_node;//返回新节点
}

// 创建一个功能函数用来进行检测操作链表的插入、删除、遍历、移动、摧毁
int Mode_Select(P_Double head_node)
{
    //判读头节点是否为空
    if(head_node == (P_Double)NULL)
    {
        printf("头节点异常!\n");
        return -1;
    }

    //定义变量,存放用户输入编号
    int select_num;
    //死循环,直到用户退出
    while(1)
    {
        //显示菜单栏
        printf("<<<< 1 头插添加数据节点 >>>>\n");
        printf("<<<< 2 尾插添加数据节点 >>>>\n");
        printf("<<<< 3 指定检索数据节点 >>>>\n");
        printf("<<<< 4 指定添加数据节点 >>>>\n");
        printf("<<<< 5 指定删除数据节点 >>>>\n");
        printf("<<<< 6 遍历链表数据节点 >>>>\n");
        printf("<<<< 7 摧毁双向不循环链表 >>>>\n");

        //获取用户输入指令
        scanf("%d",&select_num);

        switch (select_num)
        {
            case 1:Head_Add_Node(head_node);break;                  // 头插法添加数据节点
            case 2:Tail_Add_Node(head_node);break;                  // 尾插法添加数据节点
            case 3:Retrieve_Add_Node(head_node);break;              // 检索目标数据节点并返回
            case 4:Appoint_Add_Node(head_node);break;               // 目标位置添加数据节点
            case 5:Del_Add_Node(head_node);break;                   // 指定位置删除节点 
            case 6:Ergodic_List_Node(head_node);break;              // 遍历整个链表并输出显示
            case 7:Destory_Double_Link_list(head_node);return 0;    // 销毁双向不循环链表
            default:printf("没有此项指令,请重新输入!\n");break;

        }
    }

    return 0;
}

// 定义函数 Head_Add_Node,用于在双向不循环链表中头部位置插入节点
int Head_Add_Node(P_Double head_node)
{
    //判断头节点是否异常
    if(head_node == (P_Double)NULL)
    {
        printf("头节点异常!\n");
        return -1;
    }

    //创建新节点
    P_Double new_node = Create_New_Node();
    if(new_node == (P_Double)NULL)
    {
        printf("创建新节点失败!\n");
        return -1;
    } 

    //输入要插入的节点的数据
    printf("输入头插数据节点:");
    scanf("%s",new_node->data);
    while(getchar()!='\n'); //清空输入缓存区

    //判断空链表第一次插入节点
    if(head_node->next == NULL)
    {
        head_node->next = new_node;         //头节点的下一个指针指向新节点
        new_node->prev  = head_node;        //新节点的上一个前驱指针为头节点
    }
    //非空情况下
    else
    {
        P_Double second_node  = head_node->next; // 定义一个指向第二个节点的指针
        new_node->prev        = head_node;       // 新节点的前驱指向头节点
        head_node->next       = new_node;        // 头节点的后继指向新节点
        new_node->next        = second_node;     // 新节点的后继指向原来第二个节点
        second_node->prev     = new_node;        // 原来第二个节点的前驱指向新节点
    }

    printf("头插成功!数据:%s---地址:%p\n",new_node->data,new_node);

    return 0;
}

//定义函数 Tail_Add_Node,用于在双向不循环链表中尾部位置删除节点
int Tail_Add_Node(P_Double head_node)
{
    //判断头节点是否异常
    if(head_node == (P_Double)NULL)
    {
        printf("头节点异常!\n");
        return -1;
    }

    //创建新节点
    P_Double new_node = Create_New_Node();
    if(new_node == (P_Double)NULL)
    {
        printf("创建新节点失败");
        return -1;
    }

    //输入尾插数据
    printf("输入尾插数据:");
    scanf("%s",new_node->data);
    //清空输入缓存区
    while (getchar()!='\n');

    //进行尾插
    //定义一个尾插节点,遍历整个双向不循环链表,找到尾节点
    P_Double end_node;
    for(end_node = head_node; end_node->next !=NULL; end_node = end_node->next);

    //尾插
    end_node->next = new_node;       //尾节点的下一个节点为新节点
    end_node       = new_node->prev; //新节点的前驱指针指向尾节点

    printf("尾插成功!数据:%s---地址:%p\n",new_node->data,new_node);

    return 0;
}

//定义函数 Reterieve_Add_Node,用于在双向不循环链表中找到目标节点
P_Double Retrieve_Add_Node(P_Double head_node)
{
    //判断头节点是否异常
    if(head_node ==(P_Double)NULL)
    {
        printf("头节点异常!\n");
        return (P_Double)-1;
    }
    //判断链表是否为空
    else if(head_node->next == (P_Double)NULL)
    {
        printf("空链表,无需检索!\n");
        return (P_Double)0;
    }
    else
    {
        //创建检索的目标数据,并初始化
        printf("输入检索的数据:");
        char obj_data[DATA_LEN] = "\0"; 
        scanf("%s",obj_data);

        //创建临时节点变量,遍历整个链表
        for(P_Double tmp_node = head_node->next; tmp_node != NULL; tmp_node = tmp_node->next)
        {
            //判断是否与输入的目标节点匹配
            if(strcmp(tmp_node->data,obj_data)== 0)
            {
                printf("找到目标节点!数据:%s---地址:%p\n",tmp_node->data,tmp_node);
                return tmp_node;
            }
        }
    }

    printf("未找到目标节点,重新输入!\n");

    return (P_Double)0;
}

//定义函数 Appoint_Add_Node,用于在双向不循环链表中指定添加目标节点
int Appoint_Add_Node(P_Double head_node)
{
    //判断链表是否异常
    if(head_node == (P_Double)NULL)
    {
        printf("链表异常,无法添加!\n");
        return -1;
    }
    //判断链表是否为空
    else if (head_node->next == NULL)
    {
        printf("空链表,默认尾插!\n");
        if(Tail_Add_Node(head_node) == -1)
        {
            printf("尾插失败!\n");
            return -1;
        }

    }
    else
    {
       //创建插入目标节点
       P_Double obj_node = Retrieve_Add_Node(head_node);
       if(obj_node == (P_Double)-1)
       {
            printf("链表异常,创建目标节点失败!\n");
            return -1;
       }
       else if(obj_node == (P_Double)0)
       {
            printf("未找到目标节点,重新输入!\n");
            return 0;
       }
       else
       {
            //创建带插入的新节点
            P_Double new_node = Create_New_Node(head_node);
            if(new_node == (P_Double)NULL)
            {
                printf("创建新节点失败!\n");
                return -1;
            }

            printf("输入插入数据:");
            scanf("%s",new_node->data);
            while(getchar()!='\n');//清空输入缓存区

            //判断目标节点的指针的状态
            //目标节点为尾节点
            if(obj_node->next == NULL)
            {
                obj_node->next = new_node; //目标节点的下一个指针指向新节点
                new_node->prev = obj_node; //新节点的前驱指针指向目标节点
            }
            else 
            {
                new_node->next       = obj_node->next; //新节点的下一个指向目标节点的下一个 
                obj_node->next->prev = new_node;       //新节点的下一个的上一个指向新节点
                new_node->prev       = obj_node;       //新节点的前驱指向目标节点
                obj_node->next       = new_node;       //目标节点的下一个指向新节点
            }

            printf("指定位置插入成功!数据:%s---地址:%p\n",new_node->data,new_node);
       }
    }

    return 0;
}

// 定义函数 Del_Add_Node,用于删除双向不循环链表中的指定节点
int Del_Add_Node(P_Double head_node)
{
    //判断链表是否异常
    if(head_node == (P_Double)NULL)
    {
        printf("链表异常,无法进行删除操作!\n");
        return -1;
    }
    //判断链表是否为空
    else if(head_node->next == (P_Double)NULL)
    {
        printf("空链表,无需删除!\n");
        return 0;
    }
    else
    {
        //创建待删除的节点
        P_Double del_node = Retrieve_Add_Node(head_node);
        if(del_node == (P_Double)-1)
        {
            printf("链表异常,无法删除!\n");
            return -1;
        }
        else if(del_node == (P_Double)0)
        {
            printf("无此数据,重新输入!\n");
            return 0;
        }
        else
        {
            //判断删除状态
            if(del_node->next == NULL)
            {
                //del_node是尾节点
                del_node->prev->next = NULL; //待删除节点的前一个节点的后继指针指向空             
                del_node->prev       = NULL; //待删除节点的前驱指针指向空             
            }
            else
            {
                //del_node不是尾节点
                del_node->next->prev = del_node->prev; // 将待删除节点的后继节点的前驱指针指向待删除节点的前驱节点
                del_node->prev->next = del_node->next; // 将待删除节点的前驱节点的后继指针指向待删除节点的后继节点
                del_node->next       = NULL;           // 将待删除节点的后继指针置为空
                del_node->prev       = NULL;           // 将待删除节点的前驱指针置为空
            }
            printf("删除成功!\n");
            free(del_node);
        }
    }


    return 0;
}

// 定义函数 Ergodic_List_Node,用于遍历双向不循环链表并在用户终端显示
int Ergodic_List_Node(P_Double head_node)
{
    //判断头节点是否异常
    if(head_node == (P_Double)NULL)
    {
        printf("头节点异常,无法遍历!");
        return -1;
    }
    //判断是否是空链表
    else if(head_node->next == NULL)
    {
        printf("空链表,无需遍历!\n");
        return -1;
    }
    else
    {
        //创建临时节点变量,遍历整个链表
        printf("-------------------------------------------------------------------------------------\n");
        for(P_Double tmp_node = head_node->next; tmp_node != NULL; tmp_node = tmp_node->next)
        {
            printf("输出遍历的数据:%s----地址:%p\n",tmp_node->data,tmp_node);

        }
        printf("-------------------------------------------------------------------------------------\n");
    }

    return 0;
}

// 定义函数 Destroy_Double_Link_list,用于销毁双向不循环链表
int Destory_Double_Link_list(P_Double head_node)
{
        // 检查头节点是否异常
    if(head_node == (P_Double)NULL)
    {
        printf("头节点异常!\n");
        return -1; // 返回异常值
    }
    // 检查链表是否为空链表,为空直接释放头节点!
    else if(head_node->next == NULL)
    {
        printf("该链表为空链表,直接释放头节点!\n");
        free(head_node);
    }
    else
    {
        //定义计数器,记录销毁节点的数量
        int sum = 0;
        P_Double free_node; //用于临时存放被释放的节点
        //循环遍历直到链表末尾
        while(head_node->next != NULL)
        {
            free_node = head_node->next; //将头节点的下一个节点作为被释放的第一个节点

            //删除最后后一个

            if(free_node->next == NULL)
            {
                //del_node是尾节点
                free_node->prev->next = NULL; //待删除节点的前一个节点的后继指针指向空             
                free_node->prev       = NULL; //待删除节点的前驱指针指向空 
                printf("摧毁第%d个节点\n",sum);          
            }
            else
            {
                //更行指针指向,完成节点释放
                free_node->next->prev = free_node->prev; //待释放节点的后继节点的前驱指针指向待释放节点的前驱节点
                free_node->prev->next = free_node->next; //待释放节点的前驱节点的后继指针指向待释放节点的后继节点
                free_node->next       = NULL;            //待释放节点的后继指针指向空
                free_node->prev       = NULL;            //待释放节点的前驱指针指向空
                printf("摧毁第%d个节点\n",sum);
                sum++;
            }

            free(free_node);//释放节点
        }

        free(head_node);//释放头节点
        printf("最后释放头节点!\n");
    }

    return 0;
}

int main()
{
    //创建头节点
    P_Double head_node = Create_New_Node();
    if(head_node == (P_Double)-1)
    {
        printf("双向不循环链表创建失败!\n");
        return -1;
    }
    else
    {
        printf("双向不循环链表创建成功!\n");
    }

    //选择功能函数
    Mode_Select(head_node);


    return 0;
}

标签:检索,node,head,遍历,Double,链表,销毁,printf,节点
From: https://www.cnblogs.com/zhengdianshutiao-blogs/p/18200588

相关文章

  • 二叉树 | 迭代法 102.二叉树的层序遍历 429. N 叉树的层序遍历 226.翻转二叉树
    leetcode102.二叉树的层序遍历题目102.二叉树的层序遍历给你二叉树的根节点root,返回其节点值的层序遍历。(即逐层地,从左到右访问所有节点)。解题思路实现代码classTreeNode:def__init__(self,val=0,left=None,right=None):self.val=valse......
  • 二叉树的递归遍历 144.二叉树的前序遍历 145.二叉树的后序遍历 94.二叉树的中序遍历
    leetcode144.二叉树的前序遍历题目xxx解题思路实现代码leetcode145.二叉树的后序遍历题目xxx解题思路实现代码leetcode94.二叉树的中序遍历题目xxx解题思路实现代码......
  • DashVector + ModelScope 玩转多模态检索
    本教程演示如何使用向量检索服务(DashVector),结合ModelScope上的中文CLIP多模态检索模型,构建实时的“文本搜图片”的多模态检索能力。作为示例,我们采用多模态牧歌数据集作为图片语料库,用户通过输入文本来跨模态检索最相似的图片。整体流程主要分为两个阶段:图片数据Embedding入库......
  • QStandardItemModel遍历查找搜索关键字
    (1)findItems查找内容筛选项,只能查找显示的文字中是否包含该文字,但是QList<QStandardItem*>findItems(constQString&text,Qt::MatchFlagsflags=Qt::MatchExactly,intcolumn=0)const;(2)mat......
  • QStandardItemModel 遍历勾选的项
    QStandardItemModel遍历勾选的项rowCount()不能传入 m_model->index(0,0)根节点,无法获取行数;不传,或者传入一个空QModelIndex对象,可以获取到第一级节点的数量;QMap<QString,QVariantMap>mapSelectVideo;introotRowCount=m_model->rowCount();for(inti=0;i<ro......
  • 数据结构学习笔记-先序遍历森林
    先序遍历森林问题描述:设计算法输出先序遍历的森林节点及其所在的层次【算法设计思想】1.数据结构定义首先,定义二叉树节点的数据结构。每个节点包含存储数据的data字段,以及指向左右子节点的指针(lChild和rChild)。这种数据结构是二叉树和森林表示的基础。2.先序遍历单棵树设......
  • Object.values()对象遍历
    Object.keys() 对象的遍历 返回给定对象所有可枚举属性的数组;是属性名组成的数组letobj={a:1,b:2,c:3};Object.keys(obj).map((key)=>{console.log(key,obj[key]);}); Object.values() 对象的遍历返回一个给定对象自身的所有属性值的......
  • 目录遍历(Pikachu)
    原理Web安全-目录遍历漏洞_百度搜索文件遍历漏洞-CSDN博客防御1.对用户的输入进行验证,特别是路径替代字符如“../”和“~/”。2.尽可能采用白名单的形式,验证所有的输入。3.合理配置Web服务器的目录权限。4.当程序出错时,不要显示内部相关配置细节。5.对用户传过来的文件名......
  • 通义灵码企业版正式发布,满足企业私域知识检索、数据合规、统一管理等需求
    5月9日阿里云AI峰会,阿里云智能集团首席技术官周靖人宣布,通义灵码企业版正式发布,满足企业用户的定制化需求,帮助企业提升研发效率。通义灵码是国内用户规模第一的智能编码助手,基于SOTA水准的通义千问代码模型Code-Qwen1.5研发,插件下载量已超350万。通义灵码熟练掌握J......
  • 通义灵码企业版正式发布,满足企业私域知识检索、数据合规、统一管理等需求
    5月9日阿里云AI峰会,阿里云智能集团首席技术官周靖人宣布,通义灵码企业版正式发布,满足企业用户的定制化需求,帮助企业提升研发效率。通义灵码是国内用户规模第一的智能编码助手,基于SOTA水准的通义千问代码模型Code-Qwen1.5研发,插件下载量已超350万。通义灵码熟练掌握J......