logo头像
Snippet 博客主题

数据结构-线性表|顺序表|链表(上)

本节纲要

  • 预备知识
  • 顺序表(Sequential List)
  • 单链表(Singly Linked List )
  • 静态链表(Static list )
  • 循环链表(circular linked list)
  • 双向链表(doubly linked list)

01 预备知识

1.0 什么是线性表?

线性表(List)是零个或者多个数据元素的有限序列.

  • 首先它是一个序列.里面的元素是有顺序的,如果有多个元素,除开头和结尾以外的元素都有一个前驱和一个后继.而开头元素只有后继,结尾元素只有前驱.
  • 其次线性表是有限的,也就是里面的元素个数是有限的。

1.1 线性表的基本操作(描述)

1
2
3
4
5
6
7
8
9
10
11
12
13
ADT 线性表(List)
Data
线性表的数据对象集合为{a1, a2, a3, ......, an},每个元素类型为DataType。
Operation
InitList(L); //初始化线性表
IsEmptyList(L); //判断线性表是否为空
ClearList(L); //清空线性表
GetElemList(L, i, *e); //获取第i个位置的数据
SearchList(L, e); //查找与数据e相等的元素
InsertNodeList(L, i, e);//在第i个位置插入元素
DeleteNodeList(L, i, *e);//删除第i个位置的元素,e获取删除元素
GetLengthList(L); //获取线性表的长度
endADT

关于线性表的基本操作就上面几种,还有几个例如线性表的排序,合并,逆序等等操作。为了文章篇幅,就下次再介绍了。

1.2 什么是顺序存储结构?

线性表的顺序存储结构,就是指 用一段地址连续的存储单元一次存储线性表的数据元素。学过高级语言的朋友,相信对数组这玩意儿都不会陌生吧。数组就是一种顺序存储结构。

1.3 什么是链式存储结构?

链式存储结构就是可以用一组任意的内存单元存储线性表中的元素。与顺序存储不同的是,这组内存单元可以是连续的,也可以是不连续的。这就意味着,元素可以存储在内存的任意位置。正因为如此,在链式结构中,每个元素不仅要存它的信息,还需要存储它后继元素的存储地址。我们把存储元素信息的域称为数据域,而把存储后继元素地址的域称为指针域。由这两部分共同组成的数据元素ai,则可以称之为节点(Node)。
如下面这个图所示:

1.5 什么是链表?

链表就是链式存储的线性表。结点之间通过逻辑连接,形成链式存储结构。存储结点的内存单元,可以是连续的也可以是不连续的。逻辑连接与物理存储次序没有关系。

02 顺序表(Sequential List)

2.0 什么是顺序表?

采用顺序存储结构的线性表,就是顺序表。

2.1 顺序表的存储结构代码

这里我们统一采用C语言来描述。

1
2
3
4
5
6
7
#define MAXSIZE 20   //存储空间的初始大小
typedef int DataType //类型可根据实际情况而定
typedef struct
{
DataType data[MAXSIZE]; //数组来存储数据
int length; //实际长度
}SqlList;

可见,顺序表的几个重要特性:

  • 存储空间的位置:数组data
  • 顺序表的最大容量:数组长度MAXSIZE
  • 顺序表当前长度:length

2.2 顺序表的插入操作

相信大家在排队的时候都有过被插队的体验吧。当一个插队到你前面时,这个时候你内心os mmp外加素质三连的同时,也不得不往后挪一个位置。于是乎这个就不得了了,你后面的也要往后挪,你后面的后面也是……然后队伍里瞬间就狼烟起……
那么,这个顺序表的插入其实也差不多的。由于地址是连续存储的,那么在某个地方插入以后,其后的元素不得不往后挪一个位置。

插入算法描述:

  • 异常处理(插入位置不合理、顺序表已经满等等)。抛出异常。
  • 从最后一个元素往前遍历到第i个位置,依次将他们都往后挪一个位置。
  • 将要插入的元素放入位置i处。
  • 别忘记了表长度length++。

由于数组下标是从0开始的,我们习惯要删除的位置第i处又是从1开始算起的。本文就全部统一成,都从0开始吧。比如要在第5个位置插入一个元素,那就是a[5]。不然真的会混乱的。

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//功能:在顺序表L第i个位置之前插入元素e
int InsertSqlList(SqlList *L, int i, DataType data)
{
int k;
if(L->length==MAXSIZE || i<0 || i>L->length) //记住,都是从0开始的哦
return 0;//异常处理
if(i == L->length)
L->data[length++] = data;//尾插一步到位
if(i < L->length) //中间插,要挪人啦
{
for(k = L->length-1; k >= i;k--) //再次强调哈,都是从0开始的。
L->data[k+1]=L->data[k];//后移
L->data[i] = data;//新元素插入
L->length++;
}
return 1;
}

2.2 顺序表的删除操作

算法描述:

  • 异常处理(删除位置不合理、顺序表为空等等)
  • 尾删,直接获取然后length–。
  • 中间删,从i开始往后遍历,依次将各元素往前挪。e获取要删元素,length–即可。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //功能:在顺序表L中删除第i个数据元素,用e获取被删除值
    int DeleteElemList(SqlList *L, int i, DataType *e)
    {
    int k;
    if(L->length==0 || i<0 || i>L->length-1) //记住,都是从0开始的哦
    return 0;//异常处理
    if(i == L->length-1) //尾删,easy
    {
    *e = L->data[i];//获取要删除元素
    L->length--; //删除元素
    }
    if(i < L->length) //中间删,要挪人啦
    {
    *e = L->data[i];//获取要删除元素
    for(k = i; k < L->length-1;k++) //再次强调哈,都是从0开始的。
    L->data[k]=L->data[k+1];//前移
    L->length--;
    return 1;
    }

2.3 顺序表的完整代码

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
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 20
#define ERROR 0
#define OK 1
#define NO 0
#define YES 1

typedef int DataType;
typedef int Status;

typedef struct List
{
int data[MAXSIZE];
int length;
}SqlList;

void InitList(SqlList * L); //初始化顺序表
Status IsEmptyList(SqlList *L); //判断顺序表是否为空
void ClearList(SqlList *L); //清空线性表
Status GetElemList(SqlList *L,int i,DataType *e); //获取第i个位置的数据
int SearchList(SqlList *L, DataType e); //查找与数据e相等的元素
Status InsertNodeList(SqlList *L, int i,DataType e);//在第i个位置插入元素
Status DeleteNodeList(SqlList *L, int i, DataType *e);//删除第i个位置的元素,e获取删除元素
int GetLengthList(SqlList *L); //获取线性表的长度
void PrintList(SqlList *L); //遍历顺序表,此函数测试使用,根据实际类型编写

int main()
{
int e;
SqlList *pL = (SqlList*)malloc(sizeof(SqlList));
InitList(pL);
InsertNodeList(pL, 0, 1);
InsertNodeList(pL, 1, 2);
InsertNodeList(pL, 2, 3);
InsertNodeList(pL, 3, 4);
InsertNodeList(pL, 4, 5);
InsertNodeList(pL, 5, 6);

PrintList(pL);

DeleteNodeList(pL, 2, &e);
DeleteNodeList(pL, 4, &e);

PrintList(pL);


return 0;
}

void InitList(SqlList * L)
{
for(int i = 0; i < MAXSIZE; i++)
L->data[i] = 0;
L->length = 0; //将表设为空
}

Status IsEmptyList(SqlList *L)
{
if(L->length == 0)
return YES;//表为空
else
return NO;
}

void ClearList(SqlList *L)
{
InitList(L);//此操作跟初始化一样。
}
//这里的第i个位置,为了统一我们也是从0算起的
Status GetElemList(SqlList *L,int i,DataType *e)
{
if(i < 0 || i >= L->length || L->length == 0)
return ERROR;//异常处理
*e = L->data[i];

return OK;
}
//找到与数据e相同的节点,返回下标。-1表示没找到,ERROR表示表为空
int SearchList(SqlList *L, DataType e)
{
if(L->length == 0)
return ERROR;
for(int i = 0; i < L->length; i++)
{
if(L->data[i] == e)
return i;
}

return -1;
}
//获取顺序表的长度
int GetLengthList(SqlList *L)
{
return L->length;
}
//在位置i插入元素,再次强调,从0开始
Status InsertNodeList(SqlList *L, int i,DataType e)
{
if(i < 0 || i > L->length || L->length == MAXSIZE)
return ERROR;//异常处理
for(int k = L->length; k > i; k--)
{
L->data[k] = L->data[k-1]; //往后挪
}
L->data[i] = e;//插入数据,
L->length++; //长度也要加1

return OK;
}

Status DeleteNodeList(SqlList *L, int i, DataType *e)
{
if(i < 0 || i > L->length || L->length == 0)
return ERROR;//异常处理
*e = L->data[i];//获取数据
for(int k = i; k < L->length -1; k++)
L->data[k] = L->data[k+1];//往前挪
L->length--; //长度减1
return OK;
}

void PrintList(SqlList *L)
{
if(L->length == 0)
{
printf("顺序表为空\n");
}
printf("============遍历顺序表如下=============\n");
for(int i = 0; i < L->length; i++)
{
printf("\tdata[%d] = %d\n", i, L->data[i]);
}
printf("============共计%d个元素=============\n", L->length);

}

纯手打哈。。简单测试了一下。如果存在问题,欢迎指正,谢谢大家。

上一篇