数据结构2线性表——顺序表
前言:
本专栏属于数据结构相关内容,附带一些代码加深对一些内容的理解,为方便读者观看,本专栏内的所有文章会同时附带C语言和Python对应的代码,(可自行通过目录跳转到对应的部分)辅助不同主修语言的读者去更好的理解对应的内容,若是代码0基础的读者,可先去博主其他专栏学习一下基础的语法及知识点:
魔法天才的跳转链接:
C语言:C基础_Gu_shiwww的博客-CSDN博客
Python语言:python1_Gu_shiwww的博客-CSDN博客
其他数据结构内容可见:数据结构_Gu_shiwww的博客-CSDN博客
什么是线性表?
线性表是最基本、最简单、也是最常用的一种数据结构,可以存储逻辑关系为线性的数据。线性表(linear list)是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列。
包含:顺序表(数组或列表)、链表(单向链表、单向循环链表、双向链表、双向循环链表)、栈(顺序栈、链式栈)、队列(循环队列、链式队列)
逻辑结构:线性结构
存储结构:顺序存储(通过数组或列表)或链式存储(通过指针)
特点:一对一,每个节点最多一个前驱和后继,首节点无前驱,尾节点无后继。
1. 基本概念
顺序表的顺序存储实现指的是用一组连续的存储单元存储线性表的数据元素
顺序表存储数据的具体实现方案是:将数据全部存储到一整块内存空间中,数据元素之间按照次序挨个存放。(注意,C语言中用数组存放顺序表,而Python用列表存放顺序表)
举个简单的例子,将 {1,2,3,4,5} 这些数据使用顺序表存储,数据最终的存储状态如下图所示:
2. 具体操作
2.1 引入
下面,首先在数组或列表中通过构造函数实现以下几个功能:
1. 实现insert()思想
功能:给定一个列表或数组,实现在想要的位置插入一个元素
注意遇到越界问题
2. 遍历数组或列表中的所有有效元素
用C语言实现:
#include <stdio.h>/* (1)插入数组元素功能:向数组的第几个位置插数据函数:void insertIntoA(int *p,int n, int post, int data);参数:int *p: 保存数组首地址int n: 有效数据元素的个数int post: 插入元素下标int data: 数据
*/
void insertIntoA(int *p, int n, int post, int data) //insert: 插入
{int i;//1.把最后一个元素到插入位置元素向后移动一个单位(赋值操作)for (i = n - 1; i >= post; i--)p[i + 1] = p[i];//2. 把数据存放到指定位置(赋值)p[post] = data;
}/* (2)遍历数组功能:遍历数组中的有效元素函数:void showA(int *p,int n);参数:int *p:保存数组首地址int n:有效数据元素的个数
*/
void showA(int *p, int n) //show展示
{for (int i = 0; i <= n-1; i++)printf("%d ", p[i]);printf("\n");
}int main(int argc, char const *argv[])
{int a[100] = {1, 2, 3, 4, 5, 6, 7, 8};insertIntoA(a, 8, 3, 100);showA(a,9);return 0;
}
用Python语言实现:
def insert(buf, index, data):''':param buf:列表的首地址:param index:插入的位置:param data:给列表中插入的数据:return:'''for i in range(len(buf) - 1, index, -1):# 防止越界# 把最后一个元素到插入位置元素向后移动一个单位(赋值操作)buf[i] = buf[i - 1]buf[index] = datadef show(buf):for i in range(len(buf)):print("buf[%s] = %s" % (i, buf[i]))if __name__ == '__main__':buf = [1, 520, 996, 4, 5]# 列表也可与C语言中的数组一样,在定义的一开始就设置一个定长,像给定的此例子,列表长度为5,往里面增加元素必定会把最后一个数据给顶掉,导致数据丢失show(buf)insert(buf, 3, 888)show(buf)
由以上的例子我们可以发现,关于顺序表,我们无法在一个已经装满元素的数组或列表中再次增加元素,除非空间足够大,故我们的顺序表必须在初始时需要设定具体的空间大小,且尽量满足需求
2.2 设置last变量
在设置了空间大小上线之后,我们可以引入一个last变量,记录最后一个有效元素的位置
修改成last版本之后,全局变量:last始终表示最后一个有效元素的下标
C语言编写:
#include <stdio.h>
int last = 7; //代表最后一个有效元素下标, 相当于之前的n-1/* (1)插入元素功能:向数组的第几个位置插数据函数:void insertIntoA(int *p, int post, int data);参数:int *p: 保存数组首地址int post: 插入元素下标int data: 数据
*/
void insertIntoA(int *p, int post, int data) //insert: 插入
{int i;//1.把最后一个元素到插入位置元素向后移动一个单位(赋值操作)for (i = last; i >= post; i--)p[i + 1] = p[i];//2. 把数据存放到指定位置(赋值)p[post] = data;//3. 让last加一last++;
}/*功能:遍历数组中的有效元素函数:void showA(int *p);参数:int *p:保存数组首地址
*/
void showA(int *p) //show展示
{for (int i = 0; i <= last; i++)printf("%d ", p[i]);printf("\n");
}int main(int argc, char const *argv[])
{int a[100] = {1, 2, 3, 4, 5, 6, 7, 8};insertIntoA(a, 3, 100);showA(a);deleteIntoA(a, 3);showA(a);return 0;
}
Python语言编写:
last = 4def insert(buf, index, data):''':param buf:列表的首地址:param index:插入的位置:param data:给列表中插入的数据:return:'''global last # 声明last是一个全局变量for i in range(last, index - 1, -1): # last 0 index = 5 ;index + 1# 防止越界buf[i + 1] = buf[i]buf[index] = datalast = last + 1def show(buf):for i in range(last + 1):print("buf[%s] = %s" % (i, buf[i]))if __name__ == '__main__':buf = [1, 520, 996, 4, 5] + [0] * 16show(buf)insert(buf, 3, 888)show(buf)
3. 顺序表编程实现
由以上例子我们知道,顺序表是一个长度固定,元素顺序存储的线性存储结构,在顺序表中某一个位置插入元素时,后面所有的元素都要进行位置交换,相应的在顺序表删除元素的时候,也会对删除元素之后的元素进行交换操作,接下来我们进行顺序表的编程实现:
C语言编程实现顺序表
首先给出顺序表中要完成的一些具体的操作,接下来分别去实现对应的操作(前言:我设置了一个长度大小为5的顺序表,若读者想要自行定义顺序表长度的大小,可以把代码粘贴到本地编译器修改宏定义的变量N的值)
C.* C函数接口
#ifndef _SEQLIST_H__
#define _SEQLIST_H__
#include <stdio.h>
#include <stdlib.h>#define N 5
typedef struct seq
{int data[N];int last;
}seqlist_t;//1.创建一个空的顺序表
seqlist_t *CreateEpSeqlist();//返回的是申请空间的首地址
//2.判断顺序表是否为满,满返回1 未满返回0
int IsFullSeqlist(seqlist_t *p);
//3.判断顺序表是否为空
int IsEpSeqlist(seqlist_t *p);
//4.清空顺序表
void ClearSeqList(seqlist_t *p);
//5.遍历顺序表sequence 顺序 list 表
void ShowSeqlist(seqlist_t *p);
//6.向顺序表的指定位置插入数据
int InsertIntoSeqlist(seqlist_t *p, int post,int data);//post第几个位置,data插入的数据
//7.删除顺序表中指定位置的数据post删除位置
int DeletePostSeqlist(seqlist_t *p, int post);
//8.修改指定位置的数据
int ChangePostSeqList(seqlist_t *p,int post,int data);//post被修改的位置,data修改成的数据
//9.查找指定数据出现的位置
int SearchDataSeqList(seqlist_t *p,int data);//data代表被查找的数据
C.1 创建一个空的seqlist顺序表
// 1.创建一个空的顺序表
seqlist_t *CreateEpSeqlist() // 返回的是申请空间的首地址
{// 1.开辟一个结构体大小的空间seqlist_t *p = (seqlist_t *)malloc(sizeof(seqlist_t));if (p == NULL){perror("malloc err");return NULL;}// 2.对last初始化p->last = -1; // 空return p;
}
C语言通过动态开辟函数malloc开辟一个结构体大小的空间,结构体内部存储着一个大小为5的数组,该数组可以理解为我们要去进行操作的顺序表,可以通过对该结构体的内部访问来进行对顺序表的操作。
last置为-1,可以理解为此时顺序表内部没有任何元素,当内部添加一个元素之后,last的下标会变为0,我们可以得知last实际上对应的是顺序表内部有效元素的最后一个下标。
C.2 判断顺序表是否为满
满返回1,未满返回0
// 2.判断顺序表是否为满,满返回1 未满返回0
int IsFullSeqlist(seqlist_t *p)
{return p->last + 1 == N;
}
我们在结构体内部设置了一个last变量,last变量指向了顺序表内部的最后一个有效元素的下标,所有我们可以通过有效元素+1是否与我们的顺序表上限。
C.3 判断顺序表是否为空
// 3.判断顺序表是否为满,满返回1 未满返回0
int IsEpSeqlist(seqlist_t *p)
{return p->last == -1;
}
last变量指向最后一个元素,可以通过改变last变量指向的值找到最后一个有效变量的下标,当last为-1的时候,顺序表为空。
C.4 清空顺序表
// 4.清空顺序表 (清空是变为空顺序表,而不是销毁内存)
void ClearSeqList(seqlist_t *p)
{p->last = -1;
}
清空顺序表不需要返回任何实际的值,所以由void定义,将last变量置为-1,即顺序表内部无任何有效元素,但并不是将原来的数据进行删除或者清零,这是因为在访问的时候会到last停止,不会读到之后的元素。
C.5 遍历顺序表sequence
// 5.遍历顺序表sequence顺序list表
void ShowSeqlist(seqlist_t *p)
{int i;for(i = 0 ; i <= p->last ; i++)printf("%d ",p->data[i]);printf("\n");
}
last为最后一个有效数据,遍历时访问到last为止,所以上述清空顺序表可以直接修改last的值进行逻辑清空,不会往后继续访问。
C.6 向顺序表的指定位置插入数据
// 6.向顺序表的指定位置插入 insert插入
int InsertIntoSeqlist(seqlist_p p, int post, int data)
{//1. 容错判断if (IsFullSeqlist(p) || post < 0 || post > p->last + 1){printf("InsertIntoSeqlist err\n");return -1; //错误返回}// 2. 从最后一个元素到插入位置元素往后移动一个单位for (int i = p->last; i >= post; i--) //插入第一个元素(也就是下标为0):i=p->last =-1, post=0p->data[i + 1] = p->data[i];// 3. 将数据插入p->data[post] = data;// 4. 让last加一p->last++;return 0;
}
首先容错判断,因为我们需要往顺序表中插入元素,且顺序表的空间大小是一定的,如果顺序表已经满了,则不会再往顺序表中插入数据,若插入的位置小于0或者插入的位置超过顺序表中已有数目+1(因为可以往最后一个元素后面进行插入,所以last可以+1)
之后,我们需要把要插入元素之后的所有元素向后移动一个位置,给我们要插入的元素腾出一个位置,且不会影响到后面的数据,即我们的for循环从last开始遍历到post位置,倒序遍历,用[i + 1]与[i] 进行数据交换,实现所有位置后移的操作。
而在所有的元素后移结束之后,post位置的值赋值为我们当前传入的元素。最后让last+1.
C.7 删除顺序表中指定位置的数据
// 7.删除顺序表中指定位置的数据,post为删除位置
int DeleteIntoSeqlist(seqlist_p p, int post)
{//1. 容错判断if (IsEpSeqlist(p) || post < 0 || post > p->last){printf("DeleteIntoSeqlist err\n");return -1;}//2. 从删除位置后一个元素到最后一个元素向前移动一个单位for (int i = post + 1; i <= p->last; i++)p->data[i - 1] = p->data[i];//3. 让last减一p->last--;return 0;
}
删除操作首先也需要进行容错判断,首先判空,如果队列为空也不需要继续删除元素,其次判断post的输入,post不能小于0,也不能大于当前有效元素的下标last。
之后从我们删除元素的位置开始,将之后的每一个元素赋给前面一个位置,依次遍历操作到last位置,这样就将每一个元素都往前移动了一个位置,最后将我们的last减1,此时last位置的元素还是之前的元素,并未发生改变,但是因为last的改变,我们不会再遍历到此元素,所以在逻辑上我们把该元素进行删除了,物理上时post位置上的元素被后面的所有元素依次向前替换。
C.8 修改指定位置上的元素
// 8.修改指定位置上数据
int ChangePostSeqList(seqlist_p p, int post, int data)
{//1. 容错判断if (IsEpSeqlist(p) || post < 0 || post > p->last){printf("ChangePostSeqList err\n");return -1;}//2.修改指定位置数据p->data[post] = data;return 0;
}
顺序表的修改,同样需要进行容错判断,首先判空,若顺序表为空则无元素可修改,其次要被判断的是传入的post值,不能小于0,不能大于有效数据last(同删除判断)
修改操作不需要循环遍历,直接将指定下标的元素进行修改即可
C.9 查找指定数据出现的位置
// 9.查找指定数据出现的位置,返回下标,未找到返回-1
int SearchDataSeqList(seqlist_p p, int data)
{for (int i = 0; i <= p->last; i++){if (p->data[i] == data)return i;}return -1;
}
循环遍历整个顺序表,如果某个下标对应的元素与我们要查找的元素相同,则返回这个下标。
C.10 完整代码(可运行)
#include <stdio.h>
#include <stdlib.h>#define N 10
typedef struct seqlist
{int data[N]; //顺序表中存储数据的数组int last; //表示最后一个有效元素下标
} seqlist_t, *seqlist_p;//1.创建空顺序 create创造 empty空的 seqlist顺序表
seqlist_p CreateEpSeqlist()
{//1.动态申请一块顺序表结构体大小的空间seqlist_p p = (seqlist_p)malloc(sizeof(seqlist_t)); //结构体空间其中包含data和last两个成员if (NULL == p){perror("CreateEpSeqlist"); //打印上一个函数的错误信息return NULL; //错误返回}//2.对last初始化为-1, 因为此时有效元素个数为0,所以下标可以赋值为0-1=-1p->last = -1;return p; //把开辟堆区空间的地址返回给函数之外
}//2.判断顺序表是否为满,满返回1, 未满返回0
int IsFullSeqlist(seqlist_p p) //full满了
{return p->last == N - 1;
}//3.判断顺序表是否为空,为空返回1,不为空返回0
int IsEpSeqlist(seqlist_p p)
{return p->last == -1;
}//4.清空顺序表 (清空是变为空顺序表,而不是销毁内存)
void ClearSeqList(seqlist_p p)
{p->last = -1;
}//5.遍历顺序表sequence顺序list表
void ShowSeqlist(seqlist_p p)
{for (int i = 0; i <= p->last; i++)printf("%d ", p->data[i]);printf("\n");
}//6.向顺序表的指定位置插入 insert插入
int InsertIntoSeqlist(seqlist_p p, int post, int data)
{//1. 容错判断if (IsFullSeqlist(p) || post < 0 || post > p->last + 1){printf("InsertIntoSeqlist err\n");return -1; //错误返回}// 2. 从最后一个元素到插入位置元素往后移动一个单位for (int i = p->last; i >= post; i--) //插入第一个元素(也就是下标为0):i=p->last =-1, post=0p->data[i + 1] = p->data[i];// 3. 将数据插入p->data[post] = data;// 4. 让last加一p->last++;return 0;
}//7.删除顺序表中指定位置的数据,post为删除位置
int DeleteIntoSeqlist(seqlist_p p, int post)
{//1. 容错判断if (IsEpSeqlist(p) || post < 0 || post > p->last){printf("DeleteIntoSeqlist err\n");return -1;}//2. 从删除位置后一个元素到最后一个元素向前移动一个单位for (int i = post + 1; i <= p->last; i++)p->data[i - 1] = p->data[i];//3. 让last减一p->last--;return 0;
}//8.修改指定位置上数据
int ChangePostSeqList(seqlist_p p, int post, int data)
{//1. 容错判断if (IsEpSeqlist(p) || post < 0 || post > p->last){printf("ChangePostSeqList err\n");return -1;}//2.修改指定位置数据p->data[post] = data;return 0;
}//9.查找指定数据出现的位置,返回下标,未找到返回-1
int SearchDataSeqList(seqlist_p p, int data)
{for (int i = 0; i <= p->last; i++){if (p->data[i] == data)return i;}return -1;
}int main(int argc, char const *argv[])
{seqlist_p p = CreateEpSeqlist();InsertIntoSeqlist(p, 0, 10);InsertIntoSeqlist(p, 0, 20);InsertIntoSeqlist(p, 0, 30);ShowSeqlist(p);DeleteIntoSeqlist(p, 1);ShowSeqlist(p);ChangePostSeqList(p, 1, 1000);ShowSeqlist(p);printf("1000 post is: %d\n", SearchDataSeqList(p, 1000));ClearSeqList(p);if (IsEpSeqlist(p))printf("empyt!\n");free(p); //用完可以释放p=NULL; return 0;
}
Python语言编程实现顺序表
P.* Python函数接口
class Seq:N = 32 # 顺序表的最大容量def __init__(self):self.last = -1 # 顺序表中最后一个有效元素的下标self.data = [0] * Seq.N # 顺序表的数据存储# 1.判断顺序表是否为满,满返回true 未满返回falsedef is_full(self):pass# 2.判断顺序表是否为空def is_empty(self):pass# 3.清空顺序表def clear(self):pass# 4.遍历顺序表sequence 顺序表def show(self):pass# 5.向顺序表的指定位置插入数据,index第几个位置,data插入的数据def insert(self, index: int, data: int):pass# 6.删除顺序表中指定位置的数据index删除位置def del_index(self, index: int):pass# 7.修改指定位置的数据,index被修改的位置,data修改成的数据def change(self, index: int, data: int):pass# 8.查找指定数据出现的位置def search(self, data: int): # data代表被查找的数据pass
P.1 创建一个空的Seq顺序表
# 1.创建一个空的顺序表
class Seq:N = 32 # 顺序表的最大容量def __init__(self):self.last = -1 # 顺序表中最后一个有效元素的下标self.data = [0] * Seq.N # 顺序表的数据存储
Python语言并不需要动态开辟,直接在定义Seq类的时候在类的内部应用构造函数建立一个固定大小的列表(同C语言的数组)以及last(最后一个有效数据的下标,空表是默认为-1)
last置为-1,可以理解为此时顺序表内部没有任何元素,当内部添加一个元素之后,last的下标会变为0,我们可以得知last实际上对应的是顺序表内部有效元素的最后一个下标。
P.2 判断顺序表是否为满
满返回true,未满返回false
# 2.判断顺序表是否为满,满返回true 未满返回falsedef is_full(self):return self.last + 1 == Seq.N
Python中代码较为简洁,可以直接将判断的结果作为返回值输出。当内部构造的last+1(有效元素的下标,从0开始)与顺序表的最大存储空间N相等的时候,代表此顺序表已经存满。
P.3 判断顺序表是否为空
# 3.判断顺序表是否为满,满返回1 未满返回0def is_empty(self):return self.last == -1
last变量指向最后一个元素,可以通过改变last变量指向的值找到最后一个有效变量的下标,当last为-1的时候,顺序表为空。
P.4 清空顺序表
# 4.清空顺序表def clear(self):self.last = -1
清空顺序表不需要返回任何实际的值,只需要将last变量置为-1,即顺序表内部无任何有效元素,但并不是将原来的数据进行删除或者清零,这是因为在访问的时候会到last停止,不会读到之后的元素。
P.5 遍历顺序表sequence
# 5.遍历顺序表sequence 顺序表def show(self):for i in range(self.last + 1):print("buf[%s] = %s" % (i, self.data[i]))
last为最后一个有效数据,遍历时访问到last为止,所以上述清空顺序表可以直接修改last的值进行逻辑清空,不会往后继续访问。
P.6 向顺序表的指定位置插入数据
# 6.向顺序表的指定位置插入数据,index第几个位置,data插入的数据def insert(self, index: int, data: int):# 容错判断if self.is_full() or index < 0 or index > self.last + 1:print("insert error")returnfor i in range(self.last, index - 1, -1):self.data[i + 1] = self.data[i]self.data[index] = dataself.last += 1
首先容错判断,因为我们需要往顺序表中插入元素,且顺序表的空间大小是一定的,如果顺序表已经满了,则不会再往顺序表中插入数据,若插入的位置小于0或者插入的位置超过顺序表中已有数目+1(因为可以往最后一个元素后面进行插入,所以last可以+1)
之后,我们需要把要插入元素之后的所有元素向后移动一个位置,给我们要插入的元素腾出一个位置,且不会影响到后面的数据,即我们的for循环从last开始遍历到post位置,倒序遍历,用[i + 1]与[i] 进行数据交换,实现所有位置后移的操作。
而在所有的元素后移结束之后,index位置的值赋值为我们当前传入的元素。最后让last+1.
P.7 删除顺序表中指定位置的数据
# 7.删除顺序表中指定位置的数据index删除位置def del_index(self, index: int):if self.is_empty() or index > self.last or index < 0:print("del error")returnfor i in range(index, self.last):self.data[i] = self.data[i + 1]self.last -= 1
删除操作首先也需要进行容错判断,首先判空,如果队列为空也不需要继续删除元素,其次判断index的输入,index不能小于0,也不能大于当前有效元素的下标last。
之后从我们删除元素的位置开始,将之后的每一个元素赋给前面一个位置,依次遍历操作到last位置,这样就将每一个元素都往前移动了一个位置,最后将我们的last减1,此时last位置的元素还是之前的元素,并未发生改变,但是因为last的改变,我们不会再遍历到此元素,所以在逻辑上我们把该元素进行删除了,物理上时post位置上的元素被后面的所有元素依次向前替换。
P.8 修改指定位置上的元素
# 8.修改指定位置的数据,index被修改的位置,data修改成的数据def change(self, index: int, data: int):if self.is_empty() or index < 0 or index > self.last:print("change error")returnself.data[index] = data
顺序表的修改,同样需要进行容错判断,首先判空,若顺序表为空则无元素可修改,其次要被判断的是传入的index值,不能小于0,不能大于有效数据last(同删除判断)
修改操作不需要循环遍历,直接将指定下标的元素进行修改即可
P.9 查找指定数据出现的位置
# 9.查找指定数据出现的位置def search(self, data: int): # data代表被查找的数据if self.is_empty():print("search error")returnfor i in range(self.last + 1):if self.data[i] == data:return ireturn -1
循环遍历整个顺序表,如果某个下标对应的元素与我们要查找的元素相同,则返回这个下标。
P.10完整代码(可运行)
class Seq:N = 32 # 顺序表的最大容量def __init__(self):self.last = -1 # 顺序表中最后一个有效元素的下标self.data = [0] * Seq.N # 顺序表的数据存储# 1.判断顺序表是否为满,满返回true 未满返回falsedef is_full(self):return self.last + 1 == Seq.N# 2.判断顺序表是否为空def is_empty(self):return self.last == -1# 3.清空顺序表def clear(self):self.last = -1# 4.遍历顺序表sequence 顺序表def show(self):for i in range(self.last + 1):print("buf[%s] = %s" % (i, self.data[i]))# 5.向顺序表的指定位置插入数据,index第几个位置,data插入的数据def insert(self, index: int, data: int):# 容错判断if self.is_full() or index < 0 or index > self.last + 1:print("insert error")returnfor i in range(self.last, index - 1, -1):self.data[i + 1] = self.data[i]self.data[index] = dataself.last += 1# 6.删除顺序表中指定位置的数据index删除位置def del_index(self, index: int):if self.is_empty() or index > self.last or index < 0:print("del error")returnfor i in range(index, self.last):self.data[i] = self.data[i + 1]self.last -= 1# 7.修改指定位置的数据,index被修改的位置,data修改成的数据def change(self, index: int, data: int):if self.is_empty() or index < 0 or index > self.last:print("change error")returnself.data[index] = data# 8.查找指定数据出现的位置def search(self, data: int): # data代表被查找的数据if self.is_empty():print("search error")returnfor i in range(self.last + 1):if self.data[i] == data:return ireturn -1if __name__ == '__main__':seq_list = Seq()seq_list.insert(0, 888)seq_list.insert(1, 777)seq_list.insert(2, 666)seq_list.show()print()seq_list.insert(0, 999)seq_list.show()print()seq_list.del_index(0)seq_list.show()print()seq_list.change(0, 999)seq_list.show()print()print(seq_list.search(999))