线性表之链表

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

猜你喜欢

转载自blog.csdn.net/qq_19525389/article/details/79378793