双向链表

来源:互联网 发布:缩小手机屏幕的软件 编辑:程序博客网 时间:2024/06/05 02:16

双向链表

  • 双向链表
    • 基本概念
    • 设计和实现
    • 代码实现
    • 优缺点

1.基本概念

  • 单链表的尴尬

单链表的结点都只有一个指向下一个结点的指针
单链表的数据元素无法直接访问其前驱元素
逆序访问单链表中的元素是极其耗时的操作!

len = LinkList_Length(list);for (i=len-1; len>=0; i++) //O(n){LinkListNode *p = LinkList_Get(list, i); //O(n)//访问数据元素p中的元素//}
  • 双向链表的定义
    在单链表的结点中增加一个指向其前驱的pre指针
    这里写图片描述

  • 双向链表拥有单链表的所有操作

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

2.设计和实现

  • 插入操作
    这里写图片描述

  • 删除操作

这里写图片描述

  • 双向链表的新操作
    获取当前游标指向的数据元素
    将游标重置指向链表中的第一个数据元素
    将游标移动指向到链表中的下一个数据元素
    将游标移动指向到链表中的上一个数据元素
    直接指定删除链表中的某个数据元素
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);//大家一定要注意:教科书不会告诉你 项目上如何用;哪些点是项目的重点;做一个企业级的财富库,完成你人生开发经验的积累,是我们的学习重点,要注意!

3.代码实现

  • 头文件
#ifndef __DLINKLIST_H__#define __DLINKLIST_H__#include <stdio.h>#include <stdlib.h>#include <memory.h>typedef void DLinkList;//整个链表指针typedef struct _tagDLinkListNode{    struct _tagDLinkListNode * pre;//前驱指针    struct _tagDLinkListNode * next;//后继指针}DLinkListNode;//业务节点DLinkList* DLinkList_Create();void DLinkList_Destroy(DLinkList * list);int DLinkList_Clear(DLinkList * list);int DLinkList_Length(DLinkList * list);int DLinkList_Insert(DLinkList * list, DLinkListNode * node, int pos);DLinkListNode* DLinkList_Get(DLinkList * list,int pos);DLinkListNode* DLinkList_Delete(DLinkList * list,int pos);DLinkListNode * DLinkList_Reset(DLinkList *list);DLinkListNode * DLinkList_DeleteNode(DLinkList *list, DLinkListNode * node);DLinkListNode * DLinkList_Current(DLinkList *list);DLinkListNode * DLinkList_Next(DLinkList *list);DLinkListNode * DLinkList_Pre(DLinkList *list);#endif
  • 实现文件
#include "dlinklist.h"/*表头结点*/typedef struct _tagTDLinkList{    DLinkListNode head;//里面的数据项指向第一个业务节点    DLinkListNode * slider;//游标    int length;//业务节点个数}TDLinkList;//表头结点(也是整个链表的起始部分,他的地址和整个链表的起始地址重合)/*创建链表*/DLinkList* DLinkList_Create(){    /*声明链表(头结点)变量*/    TDLinkList * tmp = NULL;    /*分配内存*/    tmp = (TDLinkList *)malloc(sizeof(TDLinkList));    /*合法性检测*/    if (NULL == tmp)    {        printf("malloc error!\n");        return NULL;    }    /*初始化表头结点(链表)*/    memset(tmp, 0, sizeof(TDLinkList));    /*返回链表(表头结点)*/    return tmp;}/*销毁链表*/void DLinkList_Destroy(DLinkList * list){    /*合法性检测*/    if (list != NULL)    {        /*回收内存*/        free(list);        list = NULL;    }    else{        printf("list is empty\n");    }}/*清空链表*/int DLinkList_Clear(DLinkList * list){    TDLinkList * tmp = NULL;    /*合法性检测*/    if (list == NULL)    {        printf("argv is error\n");        return -1;    }    tmp = (TDLinkList*)list;    /*重置表头结点*/    memset(tmp, 0, sizeof(TDLinkList));    return 0;}/*获取链表长度*/int DLinkList_Length(DLinkList * list){    TDLinkList * tmp = NULL;    /*合法性检测*/    if (list == NULL)    {        printf("argv is error\n");        return -1;    }    /*返回长度*/    tmp = (TDLinkList*)list;    return tmp->length;}/*插入节点*/int DLinkList_Insert(DLinkList * list, DLinkListNode * node, int pos){    TDLinkList * tmp = NULL;    DLinkListNode * current = NULL;    DLinkListNode * current_next = NULL;    int i = 0;    /*合法性检测*/    if (list == NULL || node  == NULL || pos < 0)    {        printf("argv is error\n");        return -1;    }    tmp = (TDLinkList*)list;    /*非  循环链表要进行容错纠正*/    if (pos >= tmp->length)    {        pos = tmp->length;    }    /*当前指针指向表头结点*/    current = (DLinkListNode *)(&(tmp->head));    /*向后跳到pos-1位置处的节点*/    for (i = 0; i < pos && current->next != NULL; i++)    {        current = current->next;    }    /*找到以后pos+1位置处的节点*/    current_next = current->next;    /************************************************************************/    /* 后继指针域的更新                                                                     */    /************************************************************************/    /*目标节点的后继指针指向目标链表的pos+1节点*/    node->next = current_next;    /*pos-1节点的后继指针指向目标节点*/    current->next = node;    /************************************************************************/    /* 前驱指针域的更新                                                                     */    /************************************************************************/    /*判断是否是在链表尾插入*/    if (current_next != NULL)    {        //不是在链表尾的话进行一般的前驱指针更新        //反之,在链表尾就什么也不做,也就是不需要将pos+1节点的前驱指针更新        current_next->pre = node;    }    /*目标节点的前驱指针域更新*/    node->pre = current;    /*如果是插入整个链表的第一个节点*/    if (tmp->length == 0)    {        //游标更新        tmp->slider = node;    }    /*如果插入的是第一个结点(表头结点和头结点不是一个概念,头结点指的是业务节点的第一个节点)*/    if (current == (DLinkListNode *)(&(tmp->head)))    {        //目标节点的前去指针域指向空指针        node->pre = NULL;    }    /*更新长度*/    tmp->length++;    return 0;}/*获取指定位置的节点*/DLinkListNode* DLinkList_Get(DLinkList * list, int pos){    TDLinkList * tmp = NULL;    DLinkListNode * current = NULL;    DLinkListNode * ret = NULL;    int i = 0;    tmp = (TDLinkList*)list;    /*合法性检测,注意长度参数的合法性*/    if (list == NULL || pos < 0 || pos >= tmp->length)    {        printf("argv is error\n");        return NULL;    }    /*当前指针指向表头结点*/    current = (DLinkListNode *)(&(tmp->head));    /*向后跳到pos-1位置,注意防止跳到空指针*/    for (i = 0; i < pos && current->next != NULL;i++)    {        current = current->next;    }    /*目标节点的指针*/    ret = current->next;    return ret;}/*删除指定位置处的节点*/DLinkListNode* DLinkList_Delete(DLinkList * list, int pos){    TDLinkList * tmp = NULL;    DLinkListNode * current = NULL;    DLinkListNode * current_next = NULL;    DLinkListNode * ret = NULL;    int i = 0;    /*合法性判断*/    if (list == NULL || pos < 0)    {        printf("argv is error\n");        return NULL;    }    tmp = (TDLinkList *)list;    /*删除的时候判断是不是空链表*/    if (tmp->length <= 0)    {        printf("list is empty\n");        return NULL;    }    /*当前指针指向表头结点*/    current = (DLinkListNode *)(&(tmp->head));    /*向后跳到pos-1位置,注意防止跳到空指针*/    for (i = 0; i < pos && current->next != NULL;i++)    {        current = current->next;    }    /*获取待删除节点*/    ret = current->next;    /*目标节点的下一个节点*/    current_next = ret->next;    /*删除操作:后继指针改变*/    current->next = current_next;    /*判断是否是删除尾节点*/    if (current_next != NULL)    {        /*如果不是删除尾节点:正常的前驱指针的更新*/        current_next->pre = current;        /*判断是否删除头结点*/        if (current == (DLinkListNode *)(&(tmp->head)))        {            /*如果删除头结点:pos + 1位置处的节点的前驱指针指向NULL*/            current_next->pre = NULL;        }    }    /*如果删除的正好是游标指向的节点*/    if (tmp->slider == ret)    {        /*游标更新指向到pos + 1位置处的节点*/        tmp->slider = current_next;    }    /*更新长度*/    tmp->length--;    /*返回被删除的节点*/    return ret;}/*游标复位*/DLinkListNode * DLinkList_Reset(DLinkList *list){    TDLinkList * tmp = NULL;    DLinkListNode * ret = NULL;    if (list == NULL)    {        printf("argv is error\n");        return NULL;    }    tmp = (TDLinkList*)list;    //游标指向表头结点的起始地址    ret = tmp->slider = tmp->head.next;    return ret;}/*删除指定的节点(直接给出节点起始地址)*/DLinkListNode * DLinkList_DeleteNode(DLinkList *list, DLinkListNode * node){    TDLinkList * tmp = NULL;    DLinkListNode * ret = NULL;    DLinkListNode * current = NULL;    int i = 0;    /*合法性检测*/    if (list == NULL || node  == NULL)    {        printf("argv is error\n");        return NULL;    }    tmp = (TDLinkList*)list;    /*删除操作注意空链表*/    if (tmp->length == 0)    {        printf("list is empty\n");        return NULL;    }    /*当前指针指向表头节点*/    current = (DLinkListNode *)&(tmp->head);    /*遍历链表寻找目标节点*/    for (i = 0; i < tmp->length; i++)    {        /*如果找到目标节点*/        if (current->next == node)        {            ret = current->next;//记录目标节点,此时i就是要删除的目标节点在链表中的位置序号            break;//跳出遍历操作        }        //进行向后跳的操作        current = current->next;    }    /*如果找到目标节点在链表中的位置序号即可调用普通删除函数即可完成操作*/    if (ret != NULL)    {        DLinkList_Delete(tmp,i);    }    /*返回被删除节点*/    return ret;}/*返回当前游标指向的节点*/DLinkListNode * DLinkList_Current(DLinkList *list){    TDLinkList * tmp = NULL;    DLinkListNode * ret = NULL;    if (list == NULL)    {        printf("argv is error\n");        return NULL;    }    tmp = (TDLinkList*)list;    ret = tmp->slider;    return ret;}/*返回当前游标指向的节点并将游标指向下一个节点*/DLinkListNode * DLinkList_Next(DLinkList *list){    TDLinkList * tmp = NULL;    DLinkListNode * ret = NULL;    if (list == NULL)    {        printf("argv is error\n");        return NULL;    }    tmp = (TDLinkList*)list;    ret = tmp->slider;    tmp->slider = ret->next;    return ret;}/*返回当前游标指向的节点并将游标指向下一个节点*/DLinkListNode * DLinkList_Pre(DLinkList *list){    TDLinkList * tmp = NULL;    DLinkListNode * ret = NULL;    if (list == NULL)    {        printf("argv is error\n");        return NULL;    }    tmp = (TDLinkList*)list;    ret = tmp->slider;    tmp->slider = ret->pre;    return ret;}
  • 测试文件
#include "dlinklist.h"typedef struct _Student{    DLinkListNode node;    int num;}TStudent;int main(void){    DLinkList * dlinklist = NULL;    int i = 0;    TStudent v1;    TStudent v2;    TStudent v3;    TStudent v4;    TStudent *tmp_v;    v1.num = 1;    v2.num = 2;    v3.num = 3;    v4.num = 4;    dlinklist = DLinkList_Create();    if (dlinklist == NULL)    {        printf("create fail\n");        return -1;    }    DLinkList_Insert(dlinklist, (DLinkListNode*)&v1, 1);        DLinkList_Insert(dlinklist, (DLinkListNode*)&v2, 2);    DLinkList_Insert(dlinklist, (DLinkListNode*)&v3, 3);    DLinkList_Insert(dlinklist, (DLinkListNode*)&v4, 4);    i = 0;    while (DLinkList_Length(dlinklist) > 0)    {        tmp_v = (TStudent  *)DLinkList_Delete(dlinklist, 0);        printf("%d: %d\n",++i,tmp_v->num);    }    DLinkList_Destroy(dlinklist);    system("pause");    return 0;}

4.优缺点

  • 优点:
      双向链表在单链表的基础上增加了指向前驱的指针
      功能上双向链表可以完全取代单链表的使用
      双向链表的Next,Pre和Current操作可以高效的遍历链表中的所有元素

  • 缺点:
      代码复杂

0 0
原创粉丝点击