詳解雙向連結串列的基本操作(C語言)

嵌入式與Linux那些事發表於2020-12-16

@

1.雙向連結串列的定義

上一節學習了單向連結串列單連結串列詳解。今天學習雙連結串列。學習之前先對單向連結串列和雙向連結串列做個回顧。
單向連結串列特點
  1.我們可以輕鬆的到達下一個節點, 但是回到前一個節點是很難的.
  2.只能從頭遍歷到尾或者從尾遍歷到頭(一般從頭到尾)
雙向連結串列特點
  1.每次在插入或刪除某個節點時, 需要處理四個節點的引用, 而不是兩個. 實現起來要困難一些
  2.相對於單向連結串列, 必然佔用記憶體空間更大一些.
  3.既可以從頭遍歷到尾, 又可以從尾遍歷到頭
雙向連結串列的定義:
  雙向連結串列也叫雙連結串列,是連結串列的一種,它的每個資料結點中都有兩個指標,分別指向直接後繼和直接前驅。所以,從雙向連結串列中的任意一個結點開始,都可以很方便地訪問它的前驅結點和後繼結點。下圖為雙向連結串列的結構圖。
在這裡插入圖片描述
  從上中可以看到,雙向連結串列中各節點包含以下 3 部分資訊:
  指標域:用於指向當前節點的直接前驅節點;
  資料域:用於儲存資料元素。
  指標域:用於指向當前節點的直接後繼節點;
在這裡插入圖片描述
雙向迴圈連結串列的定義:
  雙向連結串列也可以進行首尾連線,構成雙向迴圈連結串列,如下圖所示
在建立連結串列時,只需要在最後將收尾相連即可(建立連結串列程式碼中已經標出)。其他程式碼稍加改動即可。
在這裡插入圖片描述
雙連結串列的節點結構用 C 語言實現為:

/*隨機數的範圍*/
#define MAX 100
/*節點結構*/
typedef struct Node{
    struct Node *pre;
    int data;
    struct Node *next;
}Node;

2.雙向連結串列的建立

  同單連結串列相比,雙連結串列僅是各節點多了一個用於指向直接前驅的指標域。因此,我們可以在單連結串列的基礎輕鬆實現對雙連結串列的建立。
  需要注意的是,與單連結串列不同,雙連結串列建立過程中,每建立一個新節點,都要與其前驅節點建立兩次聯絡,分別是:
  將新節點的 prior 指標指向直接前驅節點;
  將直接前驅節點的 next 指標指向新節點;
  這裡給出建立雙向連結串列的 C 語言實現程式碼:

Node* CreatList(Node * head,int length)
{
    if (length == 1)
    {
        
        return( head = CreatNode(head));
    }
    else
    {
        head = CreatNode(head);
        Node * list=head;
        for (int i=1; i<length; i++) 
        /*建立並初始化一個新結點*/
        {
            Node * body=(Node*)malloc(sizeof(Node));
            body->pre=NULL;
            body->next=NULL;
            body->data=rand()%MAX;
			/*直接前趨結點的next指標指向新結點*/
            list->next=body;
            /*新結點指向直接前趨結點*/
            body->pre=list;
            /*把body指標給list返回*/
            list=list->next;
        }
         
    }
    /*加上以下兩句就是雙向迴圈連結串列*/
    // list->next=head;
    // head->prior=list;
    return head;
}

3.雙向連結串列的插入

  根據資料新增到雙向連結串列中的位置不同,可細分為以下 3 種情況:
1.新增至表頭
  將新資料元素新增到表頭,只需要將該元素與表頭元素建立雙層邏輯關係即可。
  換句話說,假設新元素節點為 temp,表頭節點為 head,則需要做以下 2 步操作即可:
  temp->next=head; head->prior=temp;
  將 head 移至 temp,重新指向新的表頭;
  將新元素 7 新增至雙連結串列的表頭,則實現過程如下圖所示:
在這裡插入圖片描述
2.新增至表的中間位置
  同單連結串列新增資料類似,雙向連結串列中間位置新增資料需要經過以下 2 個步驟,如下圖所示:
  新節點先與其直接後繼節點建立雙層邏輯關係;
  新節點的直接前驅節點與之建立雙層邏輯關係;
在這裡插入圖片描述
3.新增至表尾
  與新增到表頭是一個道理,實現過程如下:
  找到雙連結串列中最後一個節點;
  讓新節點與最後一個節點進行雙層邏輯關係;
在這裡插入圖片描述

/*在第add位置的前面插入data節點*/
Node * InsertListHead(Node * head,int add,int data)
{
    /*新建資料域為data的結點*/
    Node * temp=(Node*)malloc(sizeof(Node));
    if(head == NULL)
    {
        printf("malloc error!\r\n");
        return NULL;
    }    
    else
    {
        temp->data=data;
        temp->pre=NULL;
        temp->next=NULL; 
    }
    /*插入到連結串列頭,要特殊考慮*/
    if (add==1)
    {
        temp->next=head;
        head->pre=temp;
        head=temp;
    }
    else
    {
        Node * body=head;
        /*找到要插入位置的前一個結點*/
        for (int i=1; i<add-1; i++)
        {
            body=body->next;
        }
        /*判斷條件為真,說明插入位置為連結串列尾*/
        if (body->next==NULL)
        {
            body->next=temp;
            temp->pre=body;
        }
        else
        {
            body->next->pre=temp;
            temp->next=body->next;
            body->next=temp;
            temp->pre=body;
        }
    }
    return head;
}

```c
/*在第add位置的後面插入data節點*/
Node * InsertListEnd(Node * head,int add,int data)
{
    int i = 1;
    /*新建資料域為data的結點*/
    Node * temp=(Node*)malloc(sizeof(Node));
    temp->data=data;
    temp->pre=NULL;
    temp->next=NULL;

    Node * body=head;
    while ((body->next)&&(i<add+1))
    {
        body=body->next;
        i++;
    }
    
    /*判斷條件為真,說明插入位置為連結串列尾*/
    if (body->next==NULL)
    {
        body->next=temp;
        temp->pre=body;
        temp->next=NULL;
    }
    else
    {
        temp->next=body->pre->next;
        temp->pre=body->pre;
        temp->pre=body->pre;
        body->pre->next=temp;

    }

    return head;
}

4.雙向連結串列的刪除

  雙連結串列刪除結點時,只需遍歷連結串列找到要刪除的結點,然後將該節點從表中摘除即可。
  例如,刪除元素 2 的操作過程如圖 所示:
在這裡插入圖片描述

Node * DeleteList(Node * head,int data)
{
    Node * temp=head;
    /*遍歷連結串列*/
    while (temp)
    {
        /*判斷當前結點中資料域和data是否相等,若相等,摘除該結點*/
        if (temp->data==data) 
        {
            /*判斷是否是頭結點*/
            if(temp->pre == NULL)
            {
                head=temp->next;
                temp->next = NULL;
                free(temp);
                return head;
            }
            /*判斷是否是尾節點*/
            else if(temp->next == NULL)
            {
                temp->pre->next=NULL;
                free(temp);
                return head;
            }
            else
            {
                temp->pre->next=temp->next;
                temp->next->pre=temp->pre;
                free(temp);
                return head;   
            }
            

        }
        temp=temp->next;
    }
    printf("Can not find %d!\r\n",data);
    return head;
}

5.雙向連結串列更改節點資料

  更改雙連結串列中指定結點資料域的操作是在查詢的基礎上完成的。實現過程是:通過遍歷找到儲存有該資料元素的結點,直接更改其資料域即可。

/*更新函式,其中,add 表示更改結點在雙連結串列中的位置,newElem 為新資料的值*/
Node *ModifyList(Node * p,int add,int newElem)
{
    Node * temp=p;
    /*遍歷到被刪除結點*/
    for (int i=1; i<add; i++) 
    {
        temp=temp->next;
    }
    temp->data=newElem;
    return p;
}

6.雙向連結串列的查詢

  通常,雙向連結串列同單連結串列一樣,都僅有一個頭指標。因此,雙連結串列查詢指定元素的實現同單連結串列類似,都是從表頭依次遍歷表中元素。

/*head為原雙連結串列,elem表示被查詢元素*/
int FindList(Node * head,int elem)
{
/*新建一個指標t,初始化為頭指標 head*/
    Node * temp=head;
    int i=1;
    while (temp) 
    {
        if (temp->data==elem)
        {
            return i;
        }
        i++;
        temp=temp->next;
    }
    /*程式執行至此處,表示查詢失敗*/
    return -1;
}

7.雙向連結串列的列印

/*輸出連結串列的功能函式*/
void PrintList(Node * head)
{
    Node * temp=head;
    while (temp) 
    {
        /*如果該節點無後繼節點,說明此節點是連結串列的最後一個節點*/
        if (temp->next==NULL) 
        {
            printf("%d\n",temp->data);
        }
        else
        {
            printf("%d->",temp->data);
        }
        temp=temp->next;
    }
}

8.測試函式及結果

int main() 
{
    Node * head=NULL;
    //建立雙連結串列
    head=CreatList(head,5);
    printf("新建立雙連結串列為\t");
    PrintList(head);
    //在表中第 5 的位置插入元素 1
    head=InsertListHead(head, 5,1);
    printf("在表中第 5 的位置插入元素 1\t");
    PrintList(head);
    //在表中第 3 的位置插入元素 7
    head=InsertListEnd(head, 3, 7);
    printf("在表中第 3 的位置插入元素 7\t");
    PrintList(head);
    // //表中刪除元素 7
    head=DeleteList(head, 7);
    printf("表中刪除元素 7\t\t\t");
    PrintList(head);
    printf("元素 1 的位置是\t:%d\n",FindList(head,1));
    //表中第 3 個節點中的資料改為儲存 6
    head = ModifyList(head,3,6);
    printf("表中第 3 個節點中的資料改為儲存6\t");
    PrintList(head);
    return 0;
}

在這裡插入圖片描述
  大家的鼓勵是我繼續創作的動力,如果覺得寫的不錯,歡迎關注,點贊,收藏,轉發,謝謝!
以上程式碼均為測試後的程式碼。如有錯誤和不妥的地方,歡迎指出。
部分內容參考網路,如有侵權,請聯絡刪除。
如遇到排版錯亂的問題,可以通過以下連結訪問我的CSDN。

CSDN:CSDN搜尋“嵌入式與Linux那些事”

歡迎歡迎關注我的公眾號:嵌入式與Linux那些事,領取秋招筆試面試大禮包(華為小米等大廠面經,嵌入式知識點總結,筆試題目,簡歷模版等)和2000G學習資料。

相關文章