基本数据结构-线性表

Macre算法系列笔记是综合 严卫敏《数据结构(c语言版)》书籍,以及考研408辅导讲义–王道408计算机考研的内容上,结合 罗永浩《算法竞赛》内容,加强编码能力和解决问题的实践能力。 感谢以上所提及的个人和机构对计算机教学领域做出的杰出贡献。(应该在有一定编程语言和调试能力下尽量去coding和调试!)。

可以作为数据结构与算法课程的实验去copy本内容代码!

先记录基本数据结构具体内容,算法应用在最后!

一、线性表基本定义

一个数据结构包含逻辑结构、存储结构以及作用在元素上的方法。

(1)逻辑结构

线性表(linear line)是最简单的数据结构。非空表记为D=(a1, a2 , …, ai-1, ai , …, an)。用数学表达式表示为:
D =    { a i ∣ a i ∈ T , i = 1 , 2 , . . . , n , n ≥ 0 } D=\,\,\left\{ ai|ai∈T,i=1,2,…,n,n≥0 \right\} D={ ai∣ai∈T,i=1,2,…,n,n≥0}

其是一个有限序列,表中每个表项都是相继排列的,每两个相邻表象都有直接前驱和直接后继关系,也就是说,线性表中仅存在唯一的一个表头(haed)、唯一的一个表尾(tail)。这表明表中数据之间的关系是线性表中相邻的数据元素ai-1和ai之间存在序偶关系(ai-1, ai),即ai-1是ai的前驱, ai是ai-1的后继;a1无前驱,an无后继,其它每个元素有且仅有一个前驱和一个后继。
R = { < a i − 1 , a i > ∣ a i − 1 , a i ∈ D , i = 2 , . . . , n } R\text{=}\left\{ <a_{i-1},a_i>|a_{i-1},a_i∈D,i=2,…,n \right\} R={ <ai−1,ai>∣ai−1,ai∈D,i=2,…,n}

(2)存储结构

主要是顺序存储和链式存储。

顺序存储:逻辑上相邻的元素在物理位置上也相邻。

链式存储:逻辑与物理存储位置无关。

(3)作用在次数据结构上的操作:

一个线性表内:元素的增加、删除、查找等;

线性表之间:合并、比较等

二、顺序存储的线性表- 顺序表 SQlist

pic_3c2af060.png

pic_8aa786e2.png

1
2
3
4
#include <stdio.h>
#include <stdlib.h>
typedef int Elemtype;
typedef int Status;

2.1 静态数组

1
2
3
4
5
6
7
8
9
#define Maxsize 20
typedef int Status;
// 静态数组
typedef struct Sqlsit
{
/* data */
Elemtype data[Maxsize];
int length;
}Sqlist;

2.2 动态数组

1
2
3
4
5
6
7
typedef struct 
{
/* data */
Elemtype *elem;
int length;
int size;
}Sqlist;

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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
Sqlist initSqlist()
{
// 初始化函数
Sqlist L;
L.elem = (Elemtype *)malloc(Maxsize*sizeof(Elemtype));
if (!L.elem)
{
printf("初始化失败!");
exit(0);
}
L.length = 0;
L.size = Maxsize;
return L;
}

Status creatList(Sqlist *L,int n)
{
//
if(n < 0) return 0;
if (n > L->size)
{
Elemtype *new = (Elemtype *)realloc(L->elem,L->size*2*sizeof(Elemtype));
L->elem = new;
L->size *=2;
}
for (int i = 0; i < n; i++)
{
// 用空白符结尾时,scanf会跳过空白符去读下一个字符,scanf在之前只读了10个数给数组初始化,后面必须多读一个数来作为结束信号,所以你必须再输入一个数。
// 删除空格符号就可以
scanf("%d",&L->elem[i]);
L->length++;
}
return 1;
}

void displayTable(Sqlist t)
{
// 打印展示 for循环打引
for (int i = 0; i < t.length; i++)
{
printf("%d ",t.elem[i]);
}
printf("\n");
}

Status listinsert(Sqlist *L,int i,Elemtype x)
{
// 插入 插入后整体往后移动一位。 L->elem[j+1] = L->elem[j];后一个等于其前一个。
if (L->length>=L->size)
{
Elemtype *new = (Elemtype *)realloc(L->elem,sizeof(Elemtype)*L->size*2);
L->size *= 2;
}
if (i < 0 || i > L->length)
{
printf("请输入合法的位置!");
return 0;
}
for (int j = L->length-1; j >= i-1 ; j--)
{
L->elem[j+1] = L->elem[j];
}
L->elem[i-1] = x;
L->length+=1;
return 1;
}

Status Listclear(Sqlist *L)
{
//清零
//将length变为零后就没有办法通过length访问元素,相当于上进行清零的操作。
L->length = 0;
return 1;
}

Status ListDestory(Sqlist *L)
{
// 销毁一个顺序表
free(L->elem);
//释放后的无效指针必须置为空,不然会导致内存泄漏
L->elem =NULL;
L->length = 0;
L->size = 0;
}

int ListLength(Sqlist L){
// 获取长度
return L.length;
}

Status ListEmpty(Sqlist L)
{
// 判空
if (L.length == 0) return 1;
else return 0;
}

Status GetElem(Sqlist L,int i,Elemtype *e)
{
if (i <= 0 || i >L.length) return 0;
// *e = L.elem[i-1];
e = *(L.elem +i -1);
return 1;

}

Status ListDelete(Sqlist *L,int i,Elemtype *e)
{
if (i<1 || i> L->length) return 0;
// 取这个位置上的指针
Elemtype *p = &(L->elem[i-1]);
e= *p;
// 取尾元素的指针
Elemtype *q = L->elem+L->length-1;
for (++p; p<=q; p++)
{
*(p-1) = *p;
}
--L->length;
return 1;
}

Status cmp(Elemtype a,Elemtype b)
{
if (a == b) return 1;
return 0;
}

int LocateElem(Sqlist L,Elemtype *e, Status (*cmp)(Elemtype,Elemtype))
{
// 找到与e相同的第一个元素,
int i =1;
Elemtype *p = L.elem;
while (i <= L.length && !(*cmp)(*p++,*e)) ++i;
if (i < L.length) return i;
return 0;
}

void Union_1(Sqlist *L1,Sqlist L2)
{
int L1_len =L1->length , L2_len = ListLength(L2);
Elemtype *e;

for (int i = 1; i <= L2.length; i++)
{
/* code */
// GetElem(L2,i,e);
e = L2.elem+i-1;

if (!LocateElem(*L1,e,cmp)) listinsert(L1,++L1->length,*e);
}

}

// void MergeList(Sqlist L1,Sqlist L2,Sqlist *L3)
// {
// // *L3 = initSqlist();
// int i= 1,j =1,k =0;
// int len1 = ListLength(L1),len2 = ListLength(L2);
// Elemtype *a,*b;
// while ((i <= len1)&&(j <= len2))
// {
// a = L1.elem+i-1;
// b = L2.elem+j-1;
// if (*a <= *b)
// {
// listinsert(L3,++k,*a);
// ++i;
// }else{
// listinsert(L3,++k,*b);
// ++j;
// }
// }
// while (i<=len1)
// {
// a = L1.elem+i-1;
// listinsert(L3,++k,*a);
// ++i;
// }
// while (j<=len2)
// {
// b = L2.elem+j-1;
// listinsert(L3,++k,*b);
// ++j;
// }
// L3->length-=1;
// }

void MergeListSq(Sqlist L1,Sqlist L2,Sqlist *L3)
{
Elemtype *p1 = L1.elem,*p2 = L2.elem;
L3->length = L1.length+L2.length;
L3->size = L3->length;
L3->elem = (Elemtype *)malloc(L3->size*sizeof(Elemtype));
Elemtype *P3 = L3->elem;
if (!L3->elem) exit(0);
Elemtype *p1_e = L1.elem+L1.length-1,*p2_e =L2.elem+L2.length-1;
while (p1 <= p1_e && p2 <= p2_e)
{
if (*p1 < *p2) *P3++ = *p1++;
else *P3++ = *p2++;
}
while (p1 <= p1_e) *P3++ = *p1++;
while (p2 <= p2_e) *P3++ = *p2++;
}

可以在main里调用和测试方法;

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
// 上面加上
int main()
{
Sqlist L = initSqlist();
Sqlist L1 = initSqlist();
Sqlist L3 = initSqlist();
int a[5] ={1,3,5,7,9};
int b[5] = {2,4,6,8,10};

// 用两个数组初始化线性表;
for (int i = 1; i <= 5; i++)
{
/* code */
L.elem[i-1] = a[i-1];
L.length++;
}
for (size_t i = 1; i <= 5; i++)
{
L1.elem[i-1] = b[i-1];
L1.length++;
}

// 方法
displayTable(L);
displayTable(L1);
MergeListSq(L,L1,&L3);

}

pic_d39dd97e.png

3.1 静态链表

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传]pic_9096f822.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#define Maxsize 1000

typedef int Elemtype;

typedef struct{
Elemtype data;
int cur; // 游标 指示下一个
}node,SlinkList[Maxsize];


int main()
{

return 0;
}

3.2 动态链表

1
2
3
4
5
6
7
8
9
#include <stdio.h>
typedef int Elemtype;
typedef int Status;
typedef struct
{
Elemtype data;
struct LNode *next;

}LNode,*Linklist;

3.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
void List_headInsert(Linklist *L)
{
// 头插法建立单链表 带头结点
// 传入的是一个指向指针变量的指针
LNode *s; Elemtype x;
(*L) = (Linklist)malloc(sizeof(LNode));
(*L)->next = NULL;
scanf("%d",&x);
while (x!= 9999)
{
s = (LNode *)malloc(sizeof(LNode));
s->data = x;
s->next = (*L)->next;
(*L)->next = s;
scanf("%d",&x);
}
}

void List_tailInsert(Linklist *L)
{
// 尾插法建立单链表 带头结点
Elemtype x;
(*L) = (Linklist)malloc(sizeof(LNode));
LNode *s,*r = (*L);
scanf("%d",&x);
while (x!=9999)
{
s = (Linklist)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
scanf("%d",&x);
}
r->next = NULL;
}

LNode *Getelem_ID(LNode L,int i)
{
// 按序号查找
if (i<1) return NULL;
int j = 1;
LNode *p = L.next;
while (p!= NULL && j < i)
{
p = p->next;
j++;
}
return p;
}

LNode *Getelem_V(LNode L,Elemtype x)
{
// 指向首元素
LNode *p = L.next;
while (p!= NULL && p->data != x) p = p->next;
return p;
}

void showList(LNode L)
{
Linklist j = L.next;
while(j)
{
printf("%d ",j->data);
j = j->next;
}
}

Status ListInsert_L(Linklist *L,int i,Elemtype x)
{
// 在第i个位置之前插入x;
Linklist p = *L;
int j = 0;
while ( p && j<i-1) {
p = p->next;
++j;
}
if( !p || j >i-1) return 0; //第i个位置的节点不存在
Linklist s = (Linklist)malloc(sizeof(LNode));
s->data = x;
s->next = p->next;
p->next = s;
return 1;
}

Status ListDelete(Linklist *L,int i,Elemtype *e)
{
// 删除第i个位置的元素
Linklist p = *L;
int j = 0;
while (p->next && j<i-1)
{
p = p->next;
j++;
}
if (!(p->next) || j>i-1) return 0;
Linklist q = p->next;
p->next = q->next;
*e = q->data;
free(q);
}

// 有错误
void MergeList(Linklist L1,Linklist L2,Linklist *L3)
{
Linklist p1 = L1->next, p2 = L2->next;
Linklist p3 = *L3;
p3->next = NULL;
while (p1 && p2)
{
if (p1->data <= p2->data)
{
p3->next = p1;p3 = p1;p1 = p1->next;
}
else{
p3->next = p2;p3 = p2; p2 = p2->next;
}
}
p3->next = p1 ? p1 : p2;
}

测试,运行时输入值,输入9999 时推出插入,采用头差,也可以采用尾插。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main()
{
Linklist L;
Linklist L1;
List_headInsert(&L);
Linklist k = L->next;
while(k)
{
printf("%d ",k->data);
k = k->next;

}
return 0
}

四、算法应用

约瑟夫问题

题目:n个人围成一圈,初始编号从1~n排列,从约定编号为x的人开始报数,数到第m个人出圈,接着又从1开始报数,报到第m个数的人又退出圈,以此类推,最后圈内只剩下一个人,这个人就是赢家,求出赢家的编号。

(1)动态链表
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
#include<bits/stdc++.h>

struct node{
int data;
struct node * next;
};

int main(){
// 处理输入输出
int n,m;
scanf("%d %d",&n,&m);
// 初始化动态链表
node *head,*now,*p,*prev;
head = new node;
head->data = 1; head->next = NULL;
now = head;
for (int i = 2; i <= n; i++){
p = new node; p->data = i; p->next = NULL;
now->next = p;
now = p;
}
// 循环链表,头尾链接起来
now->next = head;
//
now = head;
prev = head;
while((n--)> 1){
for (int i = 1; i < m; i++){
prev = now;
now = now->next;
}
printf("%d ",now->data);
prev->next = now->next;
delete now;
now = prev->next;
}
// 由于最后一个已经是确定的了,所以只要最后输出一个而不是再循环m次,所以循环只到1;
printf("%d ",now->data);
delete now;
return 0;
}
(2) 静态链表

结构体数组单向:

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
#include <bits/stdc++.h>
const int N = 105; // 要在全局声明

// 结构体数组单向链表
struct node{
int id,nextid;
}nodes[N];

int main(){
int n,m;
scanf("%d %d",&n,&m);
nodes[0].id = 0;nodes->nextid =1;
for (int i = 1;i <= n; i++){nodes[i].id = i;nodes[i].nextid = i+1;}
nodes[n].nextid =1;
int now=1;int prve = 1;
while((n--) >1){
for (int i = 1; i < m; i++){
prve = nodes[now].id;
now = nodes[now].nextid;
}
printf("%d ",nodes[now].id);
nodes[prve].nextid = nodes[now].nextid;
now = nodes[prve].nextid;
}
printf("%d ",nodes[now].id);
return 0;
}

结构体数组双向:

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
#include<bits/stdc++.h>
const int N = 105;
struct node
{
int id;
// int data;
int preid,nextid;
}nodes[N];

int main(){
int n,m;
scanf("%d %d",&n,&m);
nodes[0].nextid=1;
for (int i = 1; i <= n; i++){
nodes[i].id= i;
nodes[i].preid = i-1;
nodes[i].nextid = i+1;
}
nodes[1].preid = n;
nodes[n].nextid = 1;
int now = 1;
int prev,nextv;
while ((n--)>1){
for (int i = 1; i < m; i++) now = nodes[now].nextid;
printf("%d ",nodes[now].id);
prev = nodes[now].preid;
nextv = nodes[now].nextid;
nodes[prev].nextid = nodes[nextv].id;
nodes[nextv].preid = nodes[prev].id;
now = nextv;
}
printf("%d ",nodes[now].id);
return 0;

}

数组单向链表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<bits/stdc++.h>

int A[150];

int main(){
int n,m;
scanf("%d %d",&n,&m);
for(int i = 1; i < n ; i ++) A[i] = i+1;
A[n] = 1;
int now =1;int prev = 1;
while ((n--)>1){
for(int i = 1;i < m; i++){
prev = now;
now = A[now];
}
printf("%d ",now);
A[prev] = A[now];
now = A[prev];
}
printf("%d ",now);
return 0;

}
(3)STL list

以后更新博客!

s[now].nextid;
nodes[prev].nextid = nodes[nextv].id;
nodes[nextv].preid = nodes[prev].id;
now = nextv;
}
printf("%d ",nodes[now].id);
return 0;

}

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
数组单向链表:

~~~C++
#include<bits/stdc++.h>

int A[150];

int main(){
int n,m;
scanf("%d %d",&n,&m);
for(int i = 1; i < n ; i ++) A[i] = i+1;
A[n] = 1;
int now =1;int prev = 1;
while ((n--)>1){
for(int i = 1;i < m; i++){
prev = now;
now = A[now];
}
printf("%d ",now);
A[prev] = A[now];
now = A[prev];
}
printf("%d ",now);
return 0;

}
(3)STL list

以后更新博客!