• 第11课-双向链表


    单链表的局限
     单链表的结点都只有一个指向下一个结点的指针
     单链表的数据元素无法直接访问其前驱元素

    单链表的改进

    双向链表的定义
    在单链表的结点中增加一个指向其前驱的pre指针

    双向链表拥有单链表的所有操作
     创建链表
     销毁链表
     获取链表长度
     清空链表
     获取第pos个元素操作
     插入元素到位置pos
     删除位置pos处的元素

    NOTE:

      教科书上大多是上面这样的四步操作,但是,由于有前面单链表的经验,我们应该把上图的1和2换一下顺序,3和4换一下顺序。当然不换也可以的,因为我们引入了两个辅助指针变量,但是,建议按照之前单链表的思想来链接节点,这是比较科学的做法,算是不成为的规定吧。

    双向链表的新操作
     获取当前游标指向的数据元素
     将游标重置指向链表中的第一个数据元素
     将游标移动指向到链表中的下一个数据元素
     将游标移动指向到链表中的上一个数据元素
     直接指定删除链表中的某个数据元素

     DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node);
    DLinkListNode* DLinkList_Reset(DLinkList* list);
    DLinkListNode* DLinkList_Current(DLinkList* list);
    DLinkListNode* DLinkList_Next(DLinkList* list);
    DLinkListNode* DLinkList_Pre(DLinkList* list);

    代码练兵场:

    必知类型声明:

    typedef struct _tag_DLinkList
    {
        DLinkListNode header;
        DLinkListNode* slider;
        int length;
    } TDLinkList;
    typedef void DLinkList;
    typedef struct _tag_DLinkListNode
    {
       struct _tag_DLinkListNode* next;
       struct _tag_DLinkListNode* pre;
    }DLinkListNode;

    链表的创建,和单链表、循环链表差不多:

    DLinkList* DLinkList_Create() 
    {
        TDLinkList* ret = (TDLinkList*)malloc(sizeof(TDLinkList));
        
        if( ret != NULL )
        {
            ret->length = 0;
            ret->header.next = NULL;
            ret->header.pre = NULL;
            ret->slider = NULL;
        }
        
        return ret;
    }

    指针域先给NULL,长度置零。

    链表的销毁,清除,获得长度:

    void DLinkList_Destroy(DLinkList* list) 
    {
        if(list!=NULL)
        {
            free(list);
            list=NULL;
        }
    }
    
    void DLinkList_Clear(DLinkList* list) 
    {
        TDLinkList* sList = (TDLinkList*)list;
        
        if( sList != NULL )
        {
            sList->length = 0;
            sList->header.next = NULL;
            sList->header.pre = NULL;
            sList->slider = NULL;
        }
    }
    
    int DLinkList_Length(DLinkList* list) 
    {
        TDLinkList* sList = (TDLinkList*)list;
        int ret = -1;
        
        if( sList != NULL )
        {
            ret = sList->length;
        }
        
        return ret;
    }

    和之前的单链表、循环链表几乎一致,也比较简单,就不在赘述了。

    链表的插入:

    int DLinkList_Insert(DLinkList* list, DLinkListNode* node, int pos) 
    { 
        TDLinkList* sList = (TDLinkList*)list;
        int ret = (sList != NULL) && (pos >= 0) && (node != NULL);
        int i = 0;
        
        if( ret )
        {
            DLinkListNode* current = (DLinkListNode*)sList;
            DLinkListNode* next = NULL;
            //current->next != NULL保证了插入的pos大于链表此刻最大长度current指针的移动也不会出错
            for(i=0; (i<pos) && (current->next != NULL); i++)
            {
                current = current->next;
            }
            
            next = current->next;//赋值辅助变量next,使其指向current->next
    
            
            node->next = next;
            current->next = node;
    
            node->pre = current;
            if( next != NULL )//next如果为NULL,证明插入到了末尾,需特殊考虑,末尾时刻next为NULL,故不应该执行下面语句当然,第一次插入的时候,既是末尾也是开头
            {
                next->pre = node;
            }
            
            
            if( sList->length == 0 )//第一次插入时,node的前继应该置成NULL(覆盖之前的值),游标指向第一个节点
            {
                node->pre = NULL;
                sList->slider = node;
            }
            
            sList->length++;
        }

    获取链表的pos位置(和之前链表的并无不同,不再赘述):

    DLinkListNode* DLinkList_Get(DLinkList* list, int pos) 
    {
        TDLinkList* sList = (TDLinkList*)list;
        DLinkListNode* ret = NULL;
        int i = 0;
        
        if( (sList != NULL) && (0 <= pos) && (pos < sList->length) )
        {
            DLinkListNode* current = (DLinkListNode*)sList;
            
            for(i=0; i<pos; i++)
            {
                current = current->next;
            }
            
            ret = current->next;
        }
        
        return ret;
    }

    删除操作:

    DLinkListNode* DLinkList_Delete(DLinkList* list, int pos) // O(n)
    {
        TDLinkList* sList = (TDLinkList*)list;
        DLinkListNode* ret = NULL;
        int i = 0;
        
        if( (sList != NULL) && (0 <= pos) && (pos < sList->length) )
        {
            DLinkListNode* current = (DLinkListNode*)sList;
            DLinkListNode* next = NULL;
            
            for(i=0; i<pos; i++)
            {
                current = current->next;
            }
            
            ret = current->next;//ret指向要删除的节点
            next = ret->next;//赋值next,使其指向删除节点的下一个节点
            
            current->next = next;//链接新节点(后继)
            
            if( next != NULL )//删除不是最后一个时的情况
            {
                next->pre = current;//链接新节点(前继)
              if(current==(DLinkListNode*)sList)//删除第一个,特殊考虑
                {
                    next->pre = NULL;//前继置空
                }
              ret->pre = NULL;//删除之后,断开ret和前后的联系
              ret->next = NULL;
            }
            else //如果删除的是最后一个
            {
                ret->pre = NULL;//前继置空,如果不这样,删除的最后一个时,next是NULL,不能对NULL指针操作前继使其指向current
            }
            
            if( sList->slider == ret )//如果删除的是游标,游标后移
            {
                sList->slider = next;
            }
            
            sList->length--;//长度减一
        }
        
        return ret;
    }
    
    DLinkListNode* DLinkList_DeleteNode(DLinkList* list, DLinkListNode* node)
    {
        TDLinkList* sList = (TDLinkList*)list;
        DLinkListNode* ret = NULL;
        int i = 0;
        
        if( sList != NULL )
        {
            DLinkListNode* current = (DLinkListNode*)sList;
            
            for(i=0; i<sList->length; i++)
            {
                if( current->next == node )
                {
                    ret = current->next;
                    break;
                }
                
                current = current->next;
            }
            
            if( ret != NULL )
            {
                DLinkList_Delete(sList, i);
            }
        }
        
        return ret;
    }

    删除操作做了一点改动,考虑到参考程序没有把删除节点的前继和后继链接断开,自己实现了这一步。

    标操作和循环链表几乎一致(不再赘述):

    DLinkListNode* DLinkList_Reset(DLinkList* list)
    {
        TDLinkList* sList = (TDLinkList*)list;
        DLinkListNode* ret = NULL;
        
        if( sList != NULL )
        {
            sList->slider = sList->header.next;
            ret = sList->slider;
        }
        
        return ret;
    }
    
    DLinkListNode* DLinkList_Current(DLinkList* list)
    {
        TDLinkList* sList = (TDLinkList*)list;
        DLinkListNode* ret = NULL;
        
        if( sList != NULL )
        {
            ret = sList->slider;
        }
        
        return ret;
    }
    
    DLinkListNode* DLinkList_Next(DLinkList* list)
    {
        TDLinkList* sList = (TDLinkList*)list;
        DLinkListNode* ret = NULL;
        
        if( (sList != NULL) && (sList->slider != NULL) )
        {
            ret = sList->slider;
            sList->slider = ret->next;
        }
        
        return ret;
    }
    
    DLinkListNode* DLinkList_Pre(DLinkList* list)
    {
        TDLinkList* sList = (TDLinkList*)list;
        DLinkListNode* ret = NULL;
        
        if( (sList != NULL) && (sList->slider != NULL) )
        {
            ret = sList->slider;
            sList->slider = ret->pre;
        }
        
        return ret;
    }

     main.c:

    #include <stdio.h>
    #include <stdlib.h>
    #include "DLinkList.h"
    struct Value
    {
        DLinkListNode header;
        int v;
    };
    
    int main(int argc, char *argv[])
    {
        int i = 0;
        DLinkList* list = DLinkList_Create();
        struct Value* pv = NULL;
        struct Value v1;
        struct Value v2;
        struct Value v3;
        struct Value v4;
        struct Value v5;
        
        v1.v = 1;
        v2.v = 2;
        v3.v = 3;
        v4.v = 4;
        v5.v = 5;
        
        DLinkList_Insert(list, (DLinkListNode*)&v1, DLinkList_Length(list));
        DLinkList_Insert(list, (DLinkListNode*)&v2, DLinkList_Length(list));
        DLinkList_Insert(list, (DLinkListNode*)&v3, DLinkList_Length(list));
        DLinkList_Insert(list, (DLinkListNode*)&v4, DLinkList_Length(list));
        DLinkList_Insert(list, (DLinkListNode*)&v5, DLinkList_Length(list));
        
        for(i=0; i<DLinkList_Length(list); i++)//遍历双向链表,打印1 2 3 4 5
        {
            pv = (struct Value*)DLinkList_Get(list, i);
            
            printf("%d
    ", pv->v);
        }
        
        printf("
    ");
        
        DLinkList_Delete(list, DLinkList_Length(list)-1);//删除最后一个节点,数据5
        DLinkList_Delete(list, 0);//删除第一个节点,数据1
        
        for(i=0; i<DLinkList_Length(list); i++)//打印删除之后剩余的元素,打印2 3 4
        {
            pv = (struct Value*)DLinkList_Next(list);
            
            printf("%d
    ", pv->v);
        }
        
        printf("
    ");
        
        DLinkList_Reset(list);//游标复位
        DLinkList_Next(list);//游标指向下一个,指向3
        
        pv = (struct Value*)DLinkList_Current(list);//获取现在游标指向
        
        printf("%d
    ", pv->v);//打印 3
        
        DLinkList_DeleteNode(list, (DLinkListNode*)pv);//删除 3
        
        pv = (struct Value*)DLinkList_Current(list);//获取游标现在的指向,指向4
        
        printf("%d
    ", pv->v);//打印4
        
        DLinkList_Pre(list);//游标前移,指向2
        
        pv = (struct Value*)DLinkList_Current(list);//获取现在游标的指向
        
        printf("%d
    ", pv->v);//打印 2
        
        printf("Length: %d
    ", DLinkList_Length(list));//输出链表长度
        
        DLinkList_Destroy(list);//销毁链表
        
        return 0;
    }

    小结
     双向链表在单链表的基础上增加了指向前驱的指针
     功能上双向链表可以完全取代单链表的使用
     循环链表的NextPreCurrent操作可以高效的遍历链表中的所有元素 

  • 相关阅读:
    document.getElementById的简便方式
    uri编解码
    javascript数组
    前端网站收藏
    html5 canvas
    interview material
    Merge into(oracle)
    机器学习入门二 ----- 机器学习术语表
    机器学习入门一 ------- 什么是机器学习,机器学习的在实际中的用处
    Dubbo 源码分析系列之一环境搭建
  • 原文地址:https://www.cnblogs.com/yangguang-it/p/7264222.html
Copyright © 2020-2023  润新知