數據結構-線性表-雙向鏈表

先簡單聊聊

上次學習了單向鏈表的相關內容。此次主要研究一下雙向鏈表。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.總結

以上代碼能夠有些邊界值考慮不周到的地方,你們根據具體需求進行修改吧。鏈表的操做不要去背代碼,而要去理解。想不通的地方,能夠在紙上多畫畫。本人博客新手,有很差的地方,還請你們指出,我會學着去修改。

謝謝!!!

仍是用那句話收尾:沿途的風景要比目的地更彎的否!!!

相關文章
相關標籤/搜索