資料結構:程式加圖示分析單鏈表的插入和刪除操作

來源:互聯網
上載者:User

展示了單鏈表的基本結構:

head指標是鏈表的頭指標,指向第一個節點,每個節點的next指標域指向下一個節點,最後一個節點的next指標域為NULL,在圖中用0表示。

下面先來看程式(棧的鏈式儲存實現,另外一個實現點這裡)和對應的輸出(注意輸出前進行了鏈表反轉(見《單鏈表反轉》,否則程式後面的while迴圈輸出的順序是250,200,100),接著來剖析器:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* linkedlist.h */

#ifndef LINKEDLIST_H
#define LINKEDLIST_H

typedef struct node *link;
struct node
{
    unsigned char item;
    link next;
};

link make_node(unsigned char item);
void free_node(link p);
link search(unsigned char key);
void insert(link p);
void deletep(link p);
void traverse(void (*visit)(link));
void reverse(void);
void destroy(void);
void push(link p);
link pop(void);

#endif

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/* linkedlist.c */
#include <stdlib.h>
#include <stdio.h>
#include "linkedlist.h"

static link head = NULL;

link make_node(unsigned char item)
{
    link p = malloc(sizeof(*p));
    p->item = item;
    p->next = NULL;
    printf("make node from Item %d\n", item);
    return p;
}

void free_node(link p)
{
    printf("free node ...\n");
    free(p);
}

link search(unsigned char key)
{
    link p;
    printf("search by key %d\n", key);
    for (p = head; p; p = p->next)
        if (p->item == key)
            return p;
    return NULL;
}

void insert(link p)
{
    printf("insert node from head ...\n");
    p->next = head;
    head = p;
}

/*
void delete(link p)
{
    link pre;
    printf("delete node from ptr ...\n");
    if (p == head) {
        head = p->next;
        return;
    }
    for (pre = head; pre; pre = pre->next) 
        if (pre->next == p) {
            pre->next = p->next;
            return;
     }
}
*/

void deletep(link p)
{
    link *pnext;
    printf("delete node from ptr ...\n");
    for (pnext = &head; *pnext; pnext = &(*pnext)->next)
        if (*pnext == p)
        {
            *pnext = p->next;
            return;
        }
}

void traverse(void (*visit) (link))
{
    link p;
    printf("linkedlist traverse ...\n");
    for (p = head; p; p = p->next)
        visit(p);
}

void reverse(void)
{
    link pnode = head;
    link pprev = NULL;
    printf("reverse linkedlist ...\n");
    while (pnode != NULL)
    {
        // get the next node, and save it at pNext
        link pnext = pnode->next;
        // reverse the linkage between nodes
        pnode->next = pprev;
        // move forward on the the list
        pprev = pnode;
        pnode = pnext;

    }
    head = pprev;
}

void destroy(void)
{
    link q, p = head;
    printf("destory linkedlist ...\n");
    head = NULL;
    while (p)
    {
        q = p;
        p = p->next;
        free_node(q);
    }
}

void push(link p)
{
    printf("push item from head ...\n");
    insert(p);
}

link pop(void)
{
    if (head == NULL)
        return NULL;
    else
    {
        link p = head;
        printf("pop item from head ...\n");
        head = head->next;
        return p;
    }
}

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/*************************************************************************
    > File Name: main.c
    > Author: Simba
    > Mail: dameng34@163.com
    > Created Time: Fri 28 Dec 2012 01:22:24 PM CST
 ************************************************************************/

#include<stdio.h>
#include "linkedlist.h"

void print_item(link p)
{
    printf("print item %d \n", p->item);
}

int main(void)
{
    link p = make_node(10);
    insert(p);
    p = make_node(5);
    insert(p);
    p = make_node(90);
    insert(p);
    p = search(5);
    deletep(p);
    free_node(p);
    traverse(print_item);
    destroy();
    printf("..................\n");
    p = make_node(100);
    push(p);
    p = make_node(200);
    push(p);
    p = make_node(250);
    push(p);

    reverse();//鏈表反轉

    while ((p = pop()))
    {

        print_item(p);
        free_node(p);
    }

    return 0;
}

輸出為:

分析:

在初始化時把頭指標head初始化為NULL,表示空鏈表(不帶頭結點)。然後main函數調用make_node建立幾個節點,分別調用insert插入到鏈表中。

鏈表的插入操作如:

正如所示,insert函數雖然簡單,其中也隱含了一種特殊情況(Special Case)的處理,當head為NULL時,執行insert操作插入第一個節點之後,head指向第一個節點,而第一個節點的next指標域成為NULL,這很合理,因為它也是最後一個節點。所以空鏈表雖然是一種特殊情況,卻不需要特殊的代碼來處理,和一般情況用同樣的代碼處理即可,這樣寫出來的代碼更簡潔,但是在讀代碼時要想到可能存在的特殊情況。當然,insert函數傳進來的參數p也可能有特殊情況,傳進來的p可能是NULL,甚至是野指標,本章的函數代碼都假定調用者的傳進來的參數是合法的,不對參數做特別檢查。事實上,對指標參數做檢查是不現實的,如果傳進來的是NULL還可以檢查一下,如果傳進來的是野指標,根本無法檢查它指向的記憶體單元是不是合法的,C標準庫的函數通常也不做這種檢查,例如strcpy(p,
NULL)就會引起段錯誤。

接下來main函數調用search在鏈表中尋找某個節點,如果找到就返回指向該節點的指標,找不到就返回NULL。

search函數其實也隱含了對於空鏈表這種特殊情況的處理,如果是空鏈表則迴圈體一次都不執行,直接返回NULL。

然後main函數調用delete從鏈表中摘除用search找到的節點,最後調用free_node釋放它的儲存空間。

鏈表的刪除操作如:


從可以看出,要摘除一個節點需要首先找到它的前趨然後才能做摘除操作,而在單鏈表中通過某個節點只能找到它的後繼而不能找到它的前趨,所以刪除操作要麻煩一些,需要從第一個節點開始依次尋找要摘除的節點的前趨。delete操作也要處理一種特殊情況,如果要摘除的節點是鏈表的第一個節點,它是沒有前趨的,這種情況要用特殊的代碼處理,而不能和一般情況用同樣的代碼處理。這樣很不爽,能不能把這種特殊情況轉化為一般情況呢?可以把delete函數改成上述程式那樣:

消除特殊情況的鏈表刪除操作如:


定義一個指向指標的指標pnext,在for迴圈中pnext遍曆的是指向鏈表中各節點的指標域,這樣就把head指標和各節點的next指標統一起來了,可以在一個迴圈中處理。(其實增加一個前端節點也可以消除delete的特殊情況《線性表的鏈式儲存結構》)
然後main函數調用traverse函數遍曆整個鏈表,調用destroy函數銷毀整個鏈表。


參考:《linux c 編程一站式學習》

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.