线性表之链表

       我们学习数据结构时,除了顺序表,接触更多的另一种线性表就是“链表”。链表是一种物理存储单元上非连续、非顺序的链式存储结构,数据元素的逻辑顺序是通过链表中的指针的连接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储其他结点地址的指针域。相比线性表的顺序结构,操作更复杂。
       链表的种类由节点类型大体可以分为单链表、循环链表、双向链表、双向循环链表,根据有无头结点又可以分成带头节点链表、不带头结点链表。还有一种“静态链表”。。
链表的基本操作有初始化、插入、删除、查找、求长、判空、判满、清除、销毁、逆置。。。
我们主要学习其中常用的带头结点的链表、静态链表和不带头结点的单链表。。其实这些链表的操作实现大同小异,均是在单链表实现的基础上加以变换。所以我们先实现单链表,然后对比说明其他链表~
 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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章