線性表之鏈表

       我們學習數據結構時,除了順序表,接觸更多的另一種線性表就是“鏈表”。鏈表是一種物理存儲單元上非連續、非順序的鏈式存儲結構,數據元素的邏輯順序是通過鏈表中的指針的連接次序實現的。鏈表由一系列結點(鏈表中每一個元素稱爲結點)組成,結點在運行時動態生成。每個結點包括兩個部分:一個是存儲數據元素的數據域,另一個是存儲其他結點地址的指針域。相比線性表的順序結構,操作更復雜。
       鏈表的種類由節點類型大體可以分爲單鏈表、循環鏈表、雙向鏈表、雙向循環鏈表,根據有無頭結點又可以分成帶頭節點鏈表、不帶頭結點鏈表。還有一種“靜態鏈表”。。
鏈表的基本操作有初始化、插入、刪除、查找、求長、判空、判滿、清除、銷燬、逆置。。。
我們主要學習其中常用的帶頭結點的鏈表、靜態鏈表和不帶頭結點的單鏈表。。其實這些鏈表的操作實現大同小異,均是在單鏈表實現的基礎上加以變換。所以我們先實現單鏈表,然後對比說明其他鏈表~
 C++ Code list.h
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
//帶頭節點的單鏈表
typedef struct Node
{
    
int data;
    
struct Node*next;
}Node,*List;
//List == Node*

//初始化
void InitList(List plist);
//頭插,違背生活規律,儘量少用
bool Insert_head(List plist,int val);
//尾插
bool Insert_tail(List plist,int val);
//查找
Node* Search(List plist,int key);
//刪除
bool Delete(List plist,int key);
//求長
int GetLength(List plist);
//判空
bool IsEmpty(List plist);
//清空數據
void Clear(List plist);
//銷燬單鏈表
void Destroy(List plist);
//打印鏈表
void Show(List plist);
//鏈表逆置
void Reverse(List plist);
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include 
#include 
#include 
#include "list.h"

//初始化
void InitList(List plist)
{
    assert(plist != 
NULL);
    
if(plist==NULL)
    {
        
return ;
    }

    plist->next = 
NULL;
}
//頭插
bool Insert_head(List plist,int val)
{
    Node *p = (Node *)malloc(
sizeof(Node));
    p->data = val;
    p->next = plist->next;
    plist->next = p;
    
    
return true;
}
//尾插
bool Insert_tail(List plist,int val)
{
    Node *q;
    
for(q=plist;q->next!=NULL;q=q->next);
    
//尾節點q標記爲q->next==NULL
    Node *p = (Node *)malloc(sizeof(Node));
    p->data = val;
    
//將p插在q的後面
    p->next = q->next;//p->next = NULL;
    q->next = p;

    
return true;
}
//查找
Node* Search(List plist,int key)
{
    
for(Node *p=plist->next;p!=NULL;p=p->next)
    {
        
if(p->data == key)
        {
            
return p;
        }
    }

    
return NULL;
}
//查找前驅
static Node *SearchPri(List plist,int key)
{
    
for(Node *p=plist;p->next!=NULL;p=p->next)
    {
        
if(p->next->data == key)
        {
            
return p;
        }
    }
    
return NULL;
}
//刪除
bool Delete(List plist,int key)
{
    Node *p = SearchPri(plist,key);
    
if(p == NULL)
    {
        
return false;
    }

    Node *q = p->next;
    p->next = q->next;
    free(q);

  
    
return true;
}
//求長
int GetLength(List plist)
{
    
int count = 0;
    
for(Node *p=plist->next;p!=NULL;p=p->next)
    {
        count++;
    }

    
return count;
}
//判空
bool IsEmpty(List plist)
{
    
return plist->next == NULL;
}
//清空數據
void Clear(List plist)
{
    Destroy(plist);
}
//銷燬單鏈表
void Destroy(List plist)
{
    Node *p;
    
while(plist->next != NULL)//類似排隊買飯,每次free第一個節點元素。
    {
        p = plist->next;
        plist->next = p->next;
        free(p);
    }
}
//打印
void Show(List plist)
{
    
for(Node *p=plist->next;p!=NULL;p=p->next)
    {
        printf(
"%d ",p->data);
    }
    printf(
"\n");
}
//逆置
void Reverse(List plist)
{
    
if(plist==NULL || plist->next==NULL || 
       plist->next->next==
NULL)
    {
        
return ;
    }

    Node *p = plist->next;
    Node *q;
    plist->next = 
NULL;

    
while(p != NULL)
    {
        q = p->next;
        p->next = plist->next;
        plist->next = p;
        p = q;
    }
}



循環鏈表是另一種形式的鏈式存貯結構。它的特點是將單鏈表中最後一個結點的指針域指向頭結點,整個鏈表形成一個環。不同於單鏈表,主要在其在判空操作時條件爲頭指針(指向頭結點的指針)的next(指針域)等於頭指針,即cplist->next == cplist。遍歷節點結束的條件爲節點的指針不等於頭指針,即for(p=cplist->next; p != cplist; p=p->next)
雙向鏈表指在單鏈表的基礎上,增加每個節點的一個指向前驅節點的指針域,但頭結點無前驅。不同於單鏈表的操作,在刪除任意節點時更方便,不需要查找前驅節點進行刪除,而是藉助被刪除節點的prio節點進行刪除,時間複雜度爲o(1).
雙向循環鏈表是一個尾指針指向頭結點的雙向鏈表,也形成一個環。
不帶頭結點單鏈表實現如下:

 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
#pragma once
//不帶頭結點的單鏈表

typedef struct Node
{
    
int data;
    
struct Node *next;
}Node,*NList;

//初始化

void InitList(NList *pplist);

//頭插,違背生活規律,儘量少用
bool Insert_head(NList *pplist,int val);

//尾插
bool Insert_tail(NList *pplist,int val);

//查找
Node* Search(NList plist,int key);

bool Delete(NList *pplist,int key);

int GetLength(NList plist);

bool IsEmpty(NList *plist);

//清空數據
void Clear(NList *pplist);

//銷燬順序表
void Destroy(NList *pplist);

void Show(NList plist);

 
void Reverse(NList *pplist);

 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
#include 
#include 
#include 
#include "nlist.h"
//初始化
void InitList(NList *pplist)
{
    assert(pplist != 
NULL);
    *pplist = 
NULL;
}
//頭插,違背生活規律,儘量少用
bool Insert_head(NList *pplist,int val)//pplist是存儲首元節點地址的地址(傳指針,解引用,改變首元節點的地址。。)
{
    Node *p = (Node *)malloc(
sizeof(Node));
    p->data = val;

    p->next = (*pplist);
//*pplist代表首元節點的地址
    *pplist = p;

    
return true;
}
//尾插
bool Insert_tail(NList *pplist,int val)
{
    Node *p;
    
for(p = *pplist; p != NULL; p = p->next);

    Node *q = (Node *)malloc(
sizeof(Node));
    q->data = val;
    q->next = p;
    p = q;

    
return true;
}
//查找
Node* Search(NList plist,int key)
{
    Node *p = plist;
    
for(; p != NULL; p = p->next)
    {
        
if(p->data == key)
        {
            
return p;
        }
    }
    
return NULL;
}
//刪除
bool Delete(NList *pplist,int key)
{
    Node *p = Search(*pplist, key);
    
if(p != NULL)
    {
        Node *q = p->next;
        
int tmp = p->data;
        p->data = q->data;
        q->data = tmp;  

        p->next = q->next;
        free(q);
        
return true;
    }
    
return false;
}
//求長
int GetLength(NList plist)
{
    
int count = 0;
    
for(Node *p = plist; p != NULL; p = p->next)
    {
        ++count;
    }
    
return count;
}
//判空
bool IsEmpty(NList plist)
{
    
return plist == NULL;
}
//清空數據
void Clear(NList *pplist)
{
    Destroy(pplist);
}
//銷燬順序表
void Destroy(NList *pplist)
{
    
while(*pplist != NULL)
    {
        Node *p = *pplist;
        *pplist = p->next;
        free(p);
    }
}
//打印
void Show(NList plist)
{
    Node *p = plist;
    
for(; p != NULL; p = p->next)
    {
        printf(
"%d  ", p->data);    
    }
    printf(
"\n");
}
//逆置
void Reverse(NList *pplist)
{
    
if(pplist==NULL || *pplist==NULL || (*pplist)->next==NULL)
    {
        
return ;
    }
    Node *p = *pplist;
    *pplist = 
NULL;

    
while(p != NULL)
    {
        Node *q = p->next;
        p->next = *pplist;
        *pplist = p;
        p = q;
    }
}
上面介紹的各種鏈表都是使用指針類型實現的,鏈表中的節點空間的分配和回收(釋放)均由系統提供的標準函數malloc和free動態實現,故稱之爲動態鏈表。
靜態鏈表:由於一些編程語言中沒有指針類型,所以我們採用了順序表的數組模擬實現帶頭結點的循環鏈表。用下一節點的下標代表“指針”,自己編寫從數組中“分配節點”和“回收節點”的過程。從0下標作爲有效鏈的表頭,1下標作爲空閒鏈的表頭。
 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
//靜態鏈表
//利用順序表模擬帶頭節點的循環鏈表
//0下標作爲有效鏈的表頭,1下標作爲空閒鏈的表頭

#define SIZE 10 

typedef struct SNode
{
    
int data;//數據
    int next; //指針,下一個節點的下標
}SNode,*PSList,SList[SIZE];//SList是SNode數組數據類型

void InitSList(PSList ps);

//頭插
bool Insert_head(PSList ps,int val); 

bool Delete(PSList ps,int key);

bool IsEmpty(PSList ps);

int Search(PSList ps,int key);

void Show(PSList ps);

void Clear(PSList ps);
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
#include 
#include 
#include "slist.h"

void InitSList(PSList ps)
{
    assert(ps != 
NULL);

    
for(int i=0;i
    {
        ps[i].next = i+
1;
    }

    ps[
0].next = 0;
    ps[SIZE-
1].next = 1;
}

static bool IsFull(PSList ps)
{
    
return ps[1].next == 1;
}
//頭插
bool Insert_head(PSList ps,int val)
{
    
if(IsFull(ps))
    {
        
return false;
    }

    
int p = ps[1].next;//找到一個空閒節點

    ps[
1].next = ps[p].next;//將p從空閒鏈刪除

    ps[p].data = val;
//將數據放到p節點中 

    ps[p].next = ps[
0].next; //將p頭插進有效鏈
    ps[0].next = p;

    
return true;
}
bool IsEmpty(PSList ps)
{
    
return ps[0].next == 0;
}
static int SearhcPri(PSList ps,int key)
{
    
for(int p=0;ps[p].next!=0;p=ps[p].next)
    {
        
//if(p->next->data == key)
        if(ps[ps[p].next].data == key)
        {
            
return p;
        }
    }
    
return -1;
}
int Search(PSList ps,int key)
{
    
for(int p=ps[0].next;p!=0;p=ps[p].next)
    {
        
if(ps[p].data == key)
        {
            
return p;
        }
    }
    
return -1;
}
bool Delete(PSList ps,int key)//todo
{
    
int p = Search(ps, key);
    
int q;
    
for(int q = p; q != 0; q = ps[q].next)
    {
        ps[q] = ps[q+
1];
    }
    ps[q].next = 
0;
}
void Show(PSList ps)
{
    
for(int p=ps[0].next;p!=0;p=ps[p].next)
    {
        printf(
"%d ",ps[p].data);
    }
    printf(
"\n");
}

void Clear(PSList ps)
{
    ps[
0].next = 0;
}
發佈了35 篇原創文章 · 獲贊 48 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章