詳解雙向鏈表的基本操作(C語言)

工科生一枚,熱衷於底層技術開發,有強烈的好奇心,感興趣內容包括單片機,嵌入式Linux,Uboot等,歡迎學習交流!
愛好跑步,打籃球,睡覺。
歡迎加我QQ1500836631(備註CSDN),一起學習交流問題,分享各種學習資料,電子書籍,學習視頻等。

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;
}

在這裏插入圖片描述
以上代碼均爲測試後的代碼。如有錯誤和不妥的地方,歡迎指出。
部分內容參考網絡,如有侵權,請聯繫刪除。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章