数据结构-线性表-双向链表

先简单聊聊

上次学习了单向链表的相关内容。此次主要研究一下双向链表。node

在单向链表中,每次找到一个结点,都要经历一个循环。若是在找到当前结点后,想要找到前面的结点,还要从新进行一次循环。markdown

而双向链表中,是在单向链表的基础上,增长了一个指向前一个结点的指针。这样,经过空间换时间的方式,在不少操做中,会大幅的提升操做链表的效率。数据结构

下面咱们开始具体聊聊双向链表。post

1.相关概念

上篇文章单向链表,没有介绍相关链表的概念。我的感受,单向的比较简单。但在双向链表上,前面已经提到了,新增了指向钱一个结点的指针,操做起来要比单向链表复杂了一些,尤为在双向循环链表中,若是头脑中没有一个相对来讲的图形概念,很容易把本身绕晕。我在这篇文章把相关的概念补充补充。学习

1.1链表结点

1.1.1单向链表结点

单向量表结点包含两个域:数据域和指针域spa

  • 数据域:顾名思义,用来存放数据的区域。具体类型,根据需求来定,下面的代码中,使用的是int类型。
  • 指针域:用来指向下一个结点。里面存放的是地址,因此是指针类型。

1.1.2双向链表结点

与单向链表结点相比,多了一个指针域。也就是说,它有一个数据域和两个指针域。下面图片中data是数据域,指针域next和prior,分别指向后面结点的地址前面结点的地址。 指针

1.2 链表

1.2.1 单向链表

单向链表是按照前一个结点指向后一个结点的方式,依次将结点链接起来的一种表现形式。code

在这些结点中,能够再进行一下细分为:首元结点,中间结点和尾结点。

  • 首元结点:链表的第一个结点,该结点没有其余结点的指针域指向。
  • 尾结点:链表的最后一个结点,该结点的指针域指向NULL。
  • 中间(普通)结点:该结点即有其它结点的指针域指向自由,又有本身的指针域指向其余结点。

如此细分的缘由:当进行链表操做时(插入、删除、改、查),不一样类型的结点处理方式不一样。好比插入一个结点到单向量表中,插入的结点用node表示:orm

  • 插入首元结点位置:node的next指向原首元结点,链表的首指针指向新结点即新的首元结点。
  • 插入尾结点位置:原尾结点指向node,node的next指向NULL。
  • 插入中间结点位置:
  1. 先找到要插入位置的前一个结点pre
  2. node的next指向pre的next
  3. pre的next指向node

文字有点苍白,仍是图来的直观点图片

1.2.2 单向循环链表

与单向链表的区别在于尾结点指针域再也不是指向NULL,而是指向首元结点。

1.2.3 双向链表

和单向链表相比,双向链表的结点中多了一个指向前结点的指针域。直接看图,比较直观一些

1.2.4 双向循环链表

双向循环链表经过前面的概念,看看图也是很好理解的。

1.3 头结点

  • 什么是头结点?

在首元结点以前增长一个头结点,用来指向链表起始点。

  • 头结点的好处
  1. 便于首元结点处理,有了头结点,首元结点就能够看作是普通结点,代码逻辑来讲,少了一些逻辑判断。
  2. 便于空表和非空表的统一处理
  3. 头结点的数据域能够记录一些链表相关数据,好比记录链表的长度等等。
  4. 当你本身尝试的写一些链表操做代码时,你会爱上头结点的,哈哈。因此一下代码都会带有头结点。

p.s. 不信?你能够本身写写不带头结点的链表增删改查,你就知道有多恶心了!!!

2.相关代码

2.1单向链表

单向链表相关代码实现,请参考上篇文章20200401-数据结构-单向链表

2.2双向链表

我多说了,开始上代码。如下代码的主要目录流程

初始化-打印-增-删-改-查

准备代码

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

typedef int ElemType;
typedef int Status;

typedef struct Node * LinkList;

typedef struct Node{
    ElemType data;
    LinkList prior;
    LinkList next;
}Node;
复制代码

双向链表-非循环

初始化

Status createList(LinkList *list, int length) {
    *list = (LinkList)malloc(sizeof(Node));
    if (!*list) {
        return ERROR;
    }
    LinkList p = *list;
    p->data = -1;
    p->prior = NULL;
    p->next = NULL;
    
    for (int i = 1; i <= length; i++) {
        LinkList node = (LinkList)malloc(sizeof(Node));
        if (!node) {
            return ERROR;
        }
        node->data = i;
        node->prior = NULL;
        node->next = NULL;
        
        p->next = node;
        node->prior = p;
        p = p->next;
    }
    
    return OK;
}
复制代码

打印链表

Status displayList(LinkList list) {
    if (list == NULL) {
        printf("空链表不打印");
        return ERROR;
    }
    
    LinkList p = list->next;
    
    while (p) {
        printf("%5d", p->data);
        p = p->next;
    }
    
    printf("\n");
    return OK;
}
复制代码

增长元素

Status insertNode(LinkList *list, int pos, ElemType value) {
    if (pos < 1) {
        return ERROR;
    }
    
    if ((*list)->next == NULL) {
        return ERROR;
    }
    LinkList p = (*list);
    int i;
    for (i = 1; i < pos && p; i++) {
        p = p->next;
    }
    
    if (p == NULL) {
        printf("插入位置大于链表长度\n");
        return ERROR;
    }
    
    LinkList node = (LinkList)malloc(sizeof(Node));
    if (!node) {
        return ERROR;
    }
    node->data = value;
    node->prior = NULL;
    node->next = NULL;
    
    if (p->next == NULL) {
        p->next = node;
        node->prior = p;
    } else {
        p->next->prior = node;
        node->next = p->next;
        node->prior = p;
        p->next = node;
    }
    
    return OK;
}
复制代码

删除元素

//根据指定位置 删除结点
Status listDelPos(LinkList *list, int pos, ElemType *delData) {
    if (pos < 1) {
        return ERROR;
    }
    LinkList p = *list;
    if (p->next == NULL) {
        return ERROR;
    }
    
    int i;
    for (i = 1; i <= pos && p; i++) {
        p = p->next;
    }
    
    if (!p) {
        return ERROR;
    }
    *delData = p->data;
    if (p->next == NULL) {
        p->prior->next = NULL;
    } else {
        p->prior->next = p->next;
        p->next->prior = p->prior;
    }
    
    free(p);
    return OK;
}
//根据value 删除结点
Status listDelVal(LinkList *list, ElemType value) {
    LinkList p = *list;
    
    int ret = ERROR;
    while (p->next) {
        p = p->next;
        if (value == p->data) {
            p->prior->next = p->next;
            if (p->next) {
                p->next->prior = p->prior;
            }
            ret = OK;
            break;
        }
    }
    
    return ret;
}
复制代码

修改元素

Status updateListIndexNode(LinkList *list, int index, ElemType elem) {
    LinkList p = (*list)->next;
    for (int i = 1; i < index && p; i++) {
        p = p->next;
    }
    if (p) {
        p->data = elem;
    } else {
        return ERROR;
    }
    
    return OK;
}
复制代码

查找元素

int listSelectElem(LinkList list, ElemType elem) {
    int index = 1;
    LinkList p = list->next;
    while (p) {
        if (p->data == elem) {
            return index;
        }
        p = p->next;
        index++;
    }
    
    return -1;
}
复制代码

双向循环链表链表

初始化

Status createList(LinkList *list) {
    *list = (LinkList)malloc(sizeof(Node));
    if (!*list) {
        return ERROR;
    }
    LinkList p = *list;
    p->data = -1;
    p->prior = NULL;
    p->next = NULL;
    
    for (int i = 1; i <= 5; i++) {
        LinkList node = (LinkList)malloc(sizeof(Node));
        if (!node) {
            return ERROR;
        }
        node->data = i;
        node->next = *list;
        node->prior = p;
        p->next = node;
        (*list)->prior = node;
        p = p->next;
    }
    
    return OK;
}
复制代码

打印链表

Status displayList(LinkList list) {
    if (list == NULL) {
        return ERROR;
    }
    LinkList p = list->next;
    while (p != list) {
        printf("%5d", p->data);
        p = p->next;
    }
    
    printf("\n");
    return OK;
}
复制代码

增长元素

Status insertValue(LinkList *list, int pos, ElemType value) {
    LinkList p = *list;
    if (p == NULL) {
        return ERROR;
    }
    
    int i = 1;
    while (i < pos && p->next != *list) {
        p = p->next;
        i++;
    }
    
    if (i > pos) {
        return ERROR;
    }
    
    LinkList node = (LinkList)malloc(sizeof(Node));
    if (node == NULL) {
        return ERROR;
    }
    node->data = value;
    node->prior = p;
    node->next = p->next;
    node->next->prior = node;
    p->next = node;
    
    return OK;
}
复制代码

删除元素

Status delValue(LinkList *list, int pos, ElemType *e) {
    
    if (*list == NULL) {
        return ERROR;
    }
    LinkList p = (*list)->next;
    
    int i = 1;
    while (i < pos && p->next != *list) {
        p = p->next;
        i++;
    }
    
    if (i != pos && p->next == *list) {
        printf("要删除的位置大于链表的长度");
        return ERROR;
    }
    
    *e = p->data;
    p->next->prior = p->prior;
    p->prior->next = p->next;
    free(p);
    
    if ((*list)->next == NULL) {
        free(*list);
        *list = NULL;
        return OK;
    }
    
    return OK;
}
复制代码

3.总结

以上代码能够有些边界值考虑不周到的地方,你们根据具体需求进行修改吧。链表的操做不要去背代码,而要去理解。想不通的地方,能够在纸上多画画。本人博客新手,有很差的地方,还请你们指出,我会学着去修改。

谢谢!!!

仍是用那句话收尾:沿途的风景要比目的地更弯的否!!!

相关文章
相关标签/搜索