美文网首页二叉树之下
线性表之双向链表

线性表之双向链表

作者: 此间不留白 | 来源:发表于2019-03-12 16:26 被阅读17次

    基本概念

    在之前的学习中,已经学习了单向链表及其实现,在单向链表中,查找一个数据,只能从某个结点出发,顺着指针往后查找其他结点,若需要查找结点的前驱结点,只能从表头指针重新出发。在单向链表中,查找一个结点的直接后继结点,其时间复杂度为O(1),而查找其直接前驱结点,需要的时间复杂度为O(n),为了克服这种缺点,特地引入了双向链表。
    双向链表与单向链表相比,双向链表有两个指针域,一个指向其直接后继,另一个指向其直接前驱,其结构如下图所示:

    双向链表
    双向链表的结点可以用以下结构体定义
    typedef struct dulNode {
        int pData;  //数据域
        struct dulNode *next; //后继结点
        struct dulNode *pre;  //前驱结点
    }dulNode, *dulList;
    

    双向链表的接口及其实现

    接口定义

    接口定义如下代码所示:

    #pragma once
    #ifndef DOUBLELINKLIST_H
    #define DOUBLELINKLIST_H
    typedef struct dulNode {
        int pData;  //数据域
        struct dulNode *next; //后继结点
        struct dulNode *pre;  //前驱结点
        
    }dulNode, *dulList;
    
    dulList initDulList(); //初始化
    dulList insertDulList(dulList list, int i, int elem); //位置i处插入元素elem
    dulList deleteDulList(dulList list, int i);//删除i处的结点
    int locateDulList(dulList list, int elem); //查找元素elem的位置‘
    void dulList_trave(dulList list); //遍历链表
    void freeDulList(dulList list); //销毁链表
    dulList updateDulList(dulList list, int i, int elem); //修改i处的元素
    int getLength(dulList list);
    #endif // !DOUBLELINKLIST_H
    
    

    接口实现

    • 初始化
      初始化的过程就是给链表头结点分配一块固定大小的内存,并将它的后继结点与前驱结点置空,当然也可以将前驱结点和后继结点设置为头结点本身。具体实现代码如下所示;
    dulList initDulList()
    {
        dulList list = (dulList)malloc(sizeof(dulNode));
        
        if (list)
        {
            list->next = list->pre = NULL;
            list->pData = NULL;
        
            return list;
        }
        else
            return NULL;
    }
    
    • 插入结点
      双向链表中插入一个结点与单向链表相比,要多考虑其前驱指针的变化,当插入位置是头结点之前时,直接将插入结点的后继指针指向原来的链表头结点,并将其前驱指针指向其结点本身。而当插入位置是尾结点是,需要将原来链表的后继指针指向要插入的结点,将要插入结点的前驱指针指向原来链表的尾结点。当插入位置是中间位置时,实现过程可有下图动态表示;


      插入元素

    以上过程可以由以下代码实现

    dulList insertDulList(dulList list, int i, int elem)
    {
        dulList temp = (dulList)malloc(sizeof(dulNode));
        temp->pData = elem;
        temp->next = NULL;
        temp->pre = NULL;
        
        if (i<1||i>getLength(list)+1)
        {
            printf("位置无效!");
            exit(0);
            
        }
        else if (i == 1)
        {
            
            temp->next = list;
            list->pre = temp;
            list = temp;
            
            
        }
        else
        {
             //找到插入位置的前一个结点
            dulList p = list;
            for (int j = 1; j < i-1; j++)
            {
                p = p->next;
            }
            //判断是否是尾
            if (p->next == NULL)
            {
                p->next = temp;
                temp->pre = p;
                
            }
            else
            {
                p->next->pre = temp;
                temp->next = p->next;
                p->next = temp;
                temp->pre = p;
                
            }
    
        }
        
        return list;
        
    
    }
    
    
    • 删除元素
      在双向链表中删除一个元素,需要处理好被删除结点直接前驱和直接后继结点的指针域,具体实现可以简单描述为以下两步:
    1. 将被删除结点的后继指针赋值给其直接前驱结点的后继指针;
    2. 将被删除结点的前驱指针赋值给其直接后继结点的前驱指针。
      具体过程可由以下动态图所示:


      删除一个结点

    以上过程的具体实现代码如下所示:

    dulList deleteDulList(dulList list, int i)
    {
        if (i<1 || i>getLength(list))
        {
            printf("位置错误!");
            exit(0);
        }
        dulList p = list;
        for (int j = 0; j <i-1; j++)
        {
            p = p->next;
        }
        if (p!=NULL)
        {
            p->pre->next = p->next;
            p->next->pre = p->pre;
            free(p);
        }
        return list;
    
    }
    
    
    • 查找指定元素
      双向链表查找指定元素的代码如下所示,若找到指定元素,返回其位置,否则返回-1.
      动态图演示如下:


      查找指定元素
    int locateDulList(dulList list, int elem)
    {
        int index = 0;
        dulList p = list;
        while (p!=NULL)
        {
            
            if (p->pData == elem)
            {
                return index;
            }
            index++;
            p = p->next;
        }
        return -1;
    
    }
    
    
    • 修改数据
      双向链表要修改一个数据,首先通过遍历找到要修改结点的位置,然后将其数据域更改为指定的元素即可。如下代码所示:
    dulList updateDulList(dulList list, int i, int elem)
    {
        if (i < 1 || i>getLength(list))
        {
            printf("无效位置!");
            exit(0);
        }
        dulList p = list;
        if (p != NULL)
        {
            for (int j = 1; j < i; j++)
            {
                p = p->next;
            }
            p->pData = elem;
            return list;
        }
        
        
    }
    
    • 获取链表长度
      要获得链表长度,设置计数器初始值为0,需要遍历整个链表,计数器累加即可,具体代码如下所示:
    int getLength(dulList list)
    {
        int i = 0;
        dulList p = list->next;
        while (p != NULL)
        {
            i++;
            p = p->next;
        }
        return i;
    }
    
    • 遍历链表
      遍历整个链表的实现代码如下所示:
    void dulList_trave(dulList list)
    {
        dulList p = list;
        if (p->next == NULL)
        {
            exit(0);
        }
        while (p->next)
        {
            if (p->next == NULL)
            {
                printf("%d", p->pData);
    
            }
            else
            {
                printf("%d->", p->pData);
            }
            p = p->next;
        }
    }
    
    
    • 销毁链表
      对链表销毁,就是销毁链表的每一个结点,通过遍历链表,销毁每个结点,具体实现代码如下所示:
    void freeDulList(dulList list)
    {
        dulList p = list;
        if (list)
        {
            exit(0);
        }
        while (list)
        {
            p = list;
            list = list->next;
            free(p);
        }
        
    
    }
    

    对双向链表的测试代码如下所示:

    int main()
    {
        
        struct dulList* list = initDulList();
        list = insertDulList(list, 1, 1);
        list = insertDulList(list, 2, 2);
        list = insertDulList(list, 3, 3);
        list = insertDulList(list, 4, 4);
        int index = locateDulList(list, 4);
        list = updateDulList(list, 4, 6);
        //printf("%d\n", index);
        freeDulList(list);
        //dulList_trave(list);
        system("pause");
        return 0;
    }
    

    教材:数据结构(C语言版)清华大学出版社
    辅助学习网站:https://visualgo.net

    相关文章

      网友评论

        本文标题:线性表之双向链表

        本文链接:https://www.haomeiwen.com/subject/ijyvpqtx.html