【数据结构与算法】顺序表精讲

2023-10-29

所属专栏:数据结构与算法

前期内容:

  1. 绪论1——抽象数据类型
  2. 绪论2——时间复杂度与空间复杂度
  3. 与时间复杂度相关的OJ面试题
  4. 抽象数据类型的实现

目录

1.1线性表的定义和特点

1.2案例引入

eg: 图书信息管理系统:

1.3线性表的类型定义

基本操作

1.4顺序表的顺序表示

顺序表的顺序表示

顺序表的特点

顺序表的实现

        概念及结构

代码实现


1.1线性表的定义和特点

        例如:

  •  12星座(双鱼、白羊、水瓶….)
    • 某单位历年拥有的计算机的数量(100、120…)
    • 26个英文字母(A,B,C,…,Z)

这些都是线性表。表中的每一项就是一个数据元素。在稍微复杂的线性表中,一个数据元素可以包含若干个数据项。

同一线性表中的元素必定具有相同特性,数据元素间的关系是线性关系。

对千非空的线性表或线性结构, 其特点是:

(1) 存在唯一的一个被称作 “第一个" 的数据元素;

(2)存在唯一的一个被称作 “最后一个" 的数据元素;

(3)除第一个之外, 结构中的每个数据元素均只有一个前驱;

(4)除最后一个之外,结构中的每个数据元素均只有一个后继。

 线性表是n个具有相同特性的数据元素的有限序列。线性表是一种实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...

线性表在逻辑上是线性结构,也就是说是连续的一条直线,但在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

1.2案例引入

eg: 图书信息管理系统:

需要的功能:查找、插入、删除、修改、排序、计数

图书表抽象为线性表

表中每本图书抽象线性表中的数据元素

可以使用:图书顺序表、图书链表

  • 选择适当的存储结构
  • 实现此存储结构上的基本操作
  • 利用基本操作完成功能

1.3线性表的类型定义

抽象数据类型线性表的定义如下:

ADT List{

数据对象:D={ai|ai属于Elemset,(i=1,2…,n,n>=0)}

数据关系:R={<ai-1,ai>|ai-1,ai属于D,(i=2,3,…,n)}

基本操作:

initList(&L);

DestroyList(&L);

….

等等

}ADT List

基本操作

  • InitList(&L)

操作结果:构造一个空的线性表L

  • DestroyList(&L)

初始条件:线性表L已经存在

操作结果:销毁线性表L

  • CharList(&L)

初始条件:线性表L已经存在

操作结果:将线性表L重置为空表

  • ListEmpty(L)

初始条件:线性表L已经存在

操作结果:若线性表为空表,则返回ture;否则返回false

  • ListLength(L)

初始条件:线性表L已经存在

操作结果:返回线性表L中的数据元素个数

  • GetElem(L,i,&e)

初始条件:线性表L已经存在,1<=i<=Listlength(L)

操作结果:用e返回线性表L中第i个数据元素的

  • LocateElem(L,e,compare())

初始条件:线性表L已经存在,compare()时数据元素判定函数

操作结果:返回L中第一个与e满足compare()的数据元素的位序,若这样的元素不存在则返回0;

  • PriorElem(L,cur_e,&pre_e)

初始条件:线性表L已经存在

操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,否则操作失败;pre_e无意义

  • NextElem(L,cur_e,&next_e)

初始条件:线性表L已经存在

操作结果:若cur_e是L的是元素,且不是最后一个,则用next_e返回它的后继,否则操作失败,则next_e无意义。

  • LinstInsert(&L,i,e)

初始条件:线性表L已经存在,1<=i<=ListLength(L)+1

操作结果:在L的第i个位置之前插入新的数据元素e,L的长度加1.

  • ListDelete(&L,i,&e)

初始条件:线性表L已经存在,1<=i<=ListLength(L)

操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减一

删除前(长度为n):(a1,a2…,ai-1,ai,ai+1,…an)

删除后(长度为n-1):(a1,a2,…ai-1,ai+1,…,an)

  • ListTraverse(&L,visited())

初始条件:线性表L已经存在

操作结果:依次对线性表中每个元素调用visited()

以上所提及的运算时逻辑结构上定义的运算。只是给出了这些运算的功能是“做什么”,至于“如何做”等实现细节,只有待确定了存储结构之后才考虑。

1.4顺序表的顺序表示

顺序表的顺序表示

       线性表的顺序表示又称为顺序存储结构或顺序映像

 线性表的第一个数据元素a1的存储位置,称作线性表的起始位置或基地址

 如果每个元素占用8个存储单元,ai存储位置是2000单元,则ai+1存储位置是?2008

由于线性表顺序存储结构占用一片连续的存储空间。因此知道某个元素的存储位置就可以计算其他元素的存储位置。

顺序表的特点

        以物理位置相邻表示逻辑关系,任一元素均可随机存取

顺序表的实现

        概念及结构

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

顺序表一般可以分为:

  1. 静态顺序表:使用长数组存储。
  2. 动态顺序表:使用动态开辟的数组存储。

顺序表的静态存储

//顺序表的静态存储
#define N 7        //定义一个宏,大小为7
typedef int SQDataType;        //自定义数据类型为int型

typedef struct SeqList
{
SQDataType array[N];    //定义数组
size_t size;            //定义数据的实际个数
}seqList;

顺序表的动态存储

//顺序表的动态存储
typedef struct SeqList
{
    SLDataType* array;    //指向动态开辟的数组
    size_t size;         //有效数据个数
    size_t capicity;    //容量空间的大小
}SeqList;

补充:C语言的内存分配函数

需加载头文件<stdlib.h>

  • malloc(m)函数:开辟m字节长度的地址空间,并返回这段空间的首地址
  • sizeof(x): 计算变量x的长度
  • free(p)函数:释放指针p所指变量的存储空间,即彻底删除一个变量

代码实现

#include <stdio.h>
#include "malloc.h"	//用到malloc函数:为结构体开辟一段内存空间
#include "assert.h"		//顺序表初始化时,用来检测通过malloc开辟的内存空间是否为空

#define maxsize 8	//该顺序表最多可容纳的长度为8


typedef int Elemtype; 
typedef struct SeqList
{
	Elemtype *base;		//顺序表的首地址 
	int capacity;		//顺序表的总容量 
	int size;			//顺序表的实际大小 
};

//函数声明

 void initSeqList(SeqList *list);					//初始化顺序表 ,构造一个空的线性表 
 void push_back(SeqList *list,Elemtype val);		//尾插:val代表要插入的数据
 void push_front(SeqList *list,Elemtype val);		//头插,val代表要插入的数据
 void delete_back(SeqList *list);					//尾删
 void delete_front(SeqList *list);					//头删
 void show_list(SeqList *list);						//5.遍历输出顺序表
 void insert_pos(SeqList *list,int pos,Elemtype val);//6.按位置插入数据,val代表要插入的数据,pos代表位置
 void delete_pos(SeqList *list,int pos);			//7.按位置删除元素
 int listlength(SeqList *list);						//8.求顺序表长度
 int find(SeqList *list,Elemtype val);			//9.寻找数据,val代表要寻找的数据
 void delete_val(SeqList *list,Elemtype val);		//10.按值删除元素,val为需要删除的元素
 void sort(SeqList *list);							//排序
 void inverse(SeqList *list);						//12.倒置
 void clearlist(SeqList *list);						//13.清除顺序表
 void destroylist(SeqList *list);					//14.销毁顺序表
 
 //函数实现
 
 //初始化顺序表
 void initSeqList(SeqList *list) 
 {
 	list->base= (Elemtype*)malloc(maxsize*sizeof (Elemtype));//动态分配内存 
 	if(list->base != NULL)
 		list->capacity = maxsize;		//总容量为顺序表初始大小 
 		list->size = 0;					//将顺序表实际大小置为0 
 }
 
//1.尾插
void push_back(SeqList *list,Elemtype val) 
{
	if(list->size == list->capacity)
	{
		printf("该顺序表已满,不能再插入元素\n");
		return;//该语句的作用:使程序不再往下进行,提高程序的效率 
	}
	else {
		list->base[list->size]=val;
		list->size++;
	}
}
 
 //2.头插 
 void push_front(SeqList *list,Elemtype val)
 {
 	if(list->size >= list->capacity)
	{
		printf("该顺序表已满,不能再插入元素\n");
		return;
	}
	else
	{
		for(int i= list->size; i>0; i--)
		{
			list->base[i] = list->base [i-1];
		}
		list->base [0] = val;
		list->size ++; 
	 } 
 }
  //3.尾删
  void delete_back(SeqList *list) 
  {
  	if(list->size==0)
  	{
  		printf("顺序表为空,不能删除元素\n");
	  }
	  else
	  {
	  	list->size --;		
	  }
  }
 
 //4.头删
  void delete_front(SeqList *list)
  {
  	if(list->size == 0)
  	{
		printf("顺序表为空,不能删除元素\n");
  		return;
   }
  	for(int i= 0;i<list->size -1 ;i++)
  	{
		list->base [i]=list->base [i+1];
	 }
		list->size --;
	 
  }
 
 //5.遍历输出顺序表 
 void show_list(SeqList *list)
 {
 	for(int i =0;i<list->size ; i++)
 		printf("%d",list->base [i]);
 	printf("\n");
 }
 
 //6.按位置插入元素
   void insert_pos(SeqList *list,int pos,Elemtype val)
   {
   	if(list->size == list->capacity )
   	{
   		printf("位置已满,不能插入元素");
   		return;
	   }
	if(pos<1||pos>list->size +1)
	{
		printf("插入的位置不正确,不能插入元素");
		return;
	}
	else
	{
		for(int i=list->size ;i>=pos;i--)
		{
			list->base [i]=list->base [i-1];
		}
		list->base [pos-1]= val;
		list->size ++; 
	}
   }
  
  //7.按位置删除元素
  void delete_pos(SeqList *list,int pos)
  {
  	if(list->size == 0){
  		printf("顺序表为空,无法删除元素");
  		return;
	  }
  	if(pos<1 ||  pos>list->size)
	{
		printf("位置不正确,无法删除元素");
		return;
	}
	for(int i= list->size+1 ;i>=pos+2;i--){
		
			list->base [i]=list->base [i+1]; 
		}
		list->size --; 
	
   } 
   
   //8.求顺序表长度
   int listlength(SeqList *list)
   {
   		return list->size ;
	} 
	
//9.查找元素
int find(SeqList *list,Elemtype val)
{
	for(int i=0; i<list->size ; i++)
	{
		if(list->base [i]==val)
			return i+1;
	 } 
	 return -1;
}

//10. 按值删除元素 
void delete_val(SeqList *list,Elemtype val)
{
	if(list->size ==0){
		printf("该顺序表为空,无法按值删除元素");
		return;
	}	
	if (find(list, val) == -1)
	{
		printf("要删除的数据%d不存在该顺序表中!\n",val);
		return;
	}
	else
		delete_pos(list, find(list, val));
}
 
 //11.排序(冒泡排序) 
 void sort(SeqList *list)
 {
 	if(list->size == 0){
 		printf("该顺序表为空,无法排序");
 		return;
	 }
	 for (int i = 0; i < list->size-1; i++)  //冒泡法 
	{
		for (int j = 0; j < list->size-1-i; j++)
		{
			if (list->base[j] > list->base[j+1])
			{
				Elemtype temp = list->base[j];
				list->base[j] = list->base[j+1];
				list->base[j+1] = temp;
			}
		}
	}
  } 
  
  //12.倒置
  void inverse(SeqList *list)
 {
  
  	if(list->size ==0)
  	{
		printf("顺序表为空,无法倒置");
  		return ;
  	}
  	if(list->size == 1)
  	{
		printf("顺序表长度为1,无法倒置");
	 	return ;
	}
	for(int i=0;i<list->size / 2;i++)
	{
		Elemtype temp;//中间量
		temp = list->base [i];
		list->base [i] = list->base [list->size -i-1]; 
		list->base [list->size -i-1] = temp;
	}
  }
   
   //13.清除顺序表 
   void clearlist(SeqList *list)
   {
   	list->size = 0;
   	printf("顺序表已清楚");
   }
   
   //14. 销毁顺序表 
   void destroylist(SeqList *list)
   {
   	free(list->base);		
	list->base == NULL;
	list->capacity = 0;
	list->size = 0;
   }
   
   //主函数
   
 int main()
{
   	SeqList mylist;//创建一个结构体变量;
	initSeqList (&mylist);//初始化顺序表
	
	push_back(&mylist, 1);     //插入数据 
    push_back(&mylist, 2);
    push_back(&mylist, 3);
    push_front(&mylist, 5);
    push_back(&mylist, 4);
    push_front(&mylist, 6);
    
	int select=1;
	int pos;//代表要插入的位置
	Elemtype  item;//代表要插入的的位置
	

		printf("\n----------------------------------------------------------------\n");
		printf("-	[1]尾插	                    [2]头插                    -\n");
		printf("-	[3]尾删                     [4]头删                    -\n"); 
		printf("-	[5]遍历输出顺序表           [6]按位置插入元素          -\n"); 
		printf("-	[7]按位置删除元素           [8]求顺序表长              -\n"); 
		printf("-	[9]查找元素                 [10]按值删除元素           -\n"); 
		printf("-	[11]排序                    [12]倒置                   -\n");
		printf("-	[13]清除顺序表              [14]销毁顺序表             -\n"); 
		printf("\n----------------------------------------------------------------\n");
		
		
	while(select)
	{
		printf("请输入你的选择:");
		scanf("%d",&select); 
		
		if(select==0)
			break;
			
		switch(select)
		{
			case 1:
				printf("请输入结尾要插入的元素:");
				scanf("%d",&item); 
				push_back(&mylist, item);
				break;
				
			case 2:
				printf("请输入头部要插入的元素:");
				scanf("%d",&item);
				push_front(&mylist,item);
				break;
				
			case 3:
				printf("尾删:\n");
				delete_back(&mylist);
				break;
				
			case 4:
			    printf("头删:\n");
				delete_front(&mylist);
				break;
				
			case 5:
			    printf("遍历输出顺序表:\n");
				show_list(&mylist);
				break;
				
			case 6:
				printf("请输入要插入的位置:");
				scanf("%d",&pos);
				printf("请输入要插入的元素:");
				scanf("%d",&item);
				insert_pos(&mylist,pos,item);
				break;
				
			case 7:
				printf("请输入要删除的位置:");
				delete_pos(&mylist,pos);
				break;
				
			case 8:
				printf("该顺序表的长度为:%d",listlength(&mylist));
				break;
			
			case 9:
				printf("请输入需要查找的元素:");
				pos = find(&mylist,item);
				if (pos == -1)
					printf("要查找的数据%d在顺序表中不存在!",item);
				else
					printf("要查找的数据%d在顺序表中的位置是: %d",item,pos);				
				break;
				
			case 10:
				printf("请输入要删除的元素:");
				scanf("%d",&item);
				delete_val(&mylist,item);
				break;
			
			case 11:
				printf("排序:");
				sort(&mylist);
				break;
					
			case 12:
				printf("倒置:");
				inverse(&mylist);
				break;
				
			case 13:
				printf("清除顺序表:\n");
				clearlist(&mylist);
				break;
				
			case 14:
				printf("顺序表已销毁\n");
				destroylist(&mylist);
				break;
				
			default:
				printf("输入的信息有误,请重新输入");
				break; 
		}
	 } 
	destroylist(&mylist);///*销毁顺序表的方法,写在while循环外面,写在main函数将要结束时,释放掉malloc开辟的空间*/
}

 今天的内容到这里就结束啦~

下期预告:顺序表相关的OJ面试题

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【数据结构与算法】顺序表精讲 的相关文章

随机推荐

  • 【4】测试用例设计-判定表法

    判定表适用于有几个原因 导致几个结果的情况 实际测试中 如果输入条件较多 再加上各种输入与输出之间相互的作用关系 画出的因果图会比较复杂 容易使人混乱 为了避免这种情况 人们往往使用决策表法代替因果图法 决策表也称为 判定表 其实质就是一种
  • 各大公司薪资

    联合利华 MKT 9500 3000元安家费 普通职位 8KX12 联合利华销售代表 底薪加提成 总体一般 一般能拿到5K以上 宝洁 本8600 硕9700 博10500发14个月 11年数据 欧莱雅 MKT 6 6K X 13 11年数据
  • ajax工作原理 网页从输入url到呈现过程(TCP ,渲染引擎) 头像上传 下拉加载 节流 防抖 常见状态码

    Ajax工作原理 1 http网络传输协议 规定 前后端交互的 数据传输格式 协议 规定 前后端交互的数据传输格式 2 http协议组成两个部分 2 1前端 必须发送 请求报文格式 2 2后端 必须响应 响应报文格式 3 请求报文格式组成
  • VueX是什么?好处?何时使用?

    VueX相关 1 VueX是什么 2 使用VueX统一管理状态的好处 3 什么样的数据适合存储到Vuex中 1 VueX是什么 VueX是实现组件全局状态 数据 管理的一种机制 可以方便的实现组件之间数据的共享 如果没有VueX实现数据间的
  • 点云配准(四) Sparse Point Registration 算法浅析

    Sparse Point Registration SPR 是一篇2017年的点云配准算法 该算法的主要目的是对稀疏点云进行配准 并且取得了不错的成果和突破 本文一方面是对SPR配准算法模型进行了简单的原理解析以及附加代码实现 另一方面是对
  • c++ 的 multiple definition of `XXX‘

    文章目录 一 为什么会有多重定义问题 二 一些错误情景 1 头文件忘记加条件编译 2 类的静态变量在头文件定义 3 全局变量在 h文件定义 4 待补充 三 参考链接 一 为什么会有多重定义问题 声明 指出存储类型 并给出存储单元指定名称 定
  • C语言博客作业--嵌套循环

    一 PTA实验作业 题目1 7 1 查询水果价格 给定四种水果 分别是苹果 apple 梨 pear 桔子 orange 葡萄 grape 单价分别对应为3 00元 公斤 2 50元 公斤 4 10元 公斤 10 20元 公斤 首先在屏幕上
  • JavaScript函数的命名方式

    函数的命名方式 JavaScript代码服用单位是函数 函数可以包含一段可执行代码 也可以接受调用者传入的参数 JavaScript定义函数主要有以下三种方式 第一种方式 命名函数 第二种 匿名函数
  • 【C++】函数的设计与使用(三)重载函数、模板函数

    重载函数 参数列表不相同 可能是参数类型不相同 或者参数个数不相同 都不相同 也可以 的两个或多个函数 可以拥有相同的函数名称 编译器会把实参和每个重载函数的形参比对 找出哪个重载函数合适 所以每个重载函数的参数列表必须和其他的重载函数的不
  • ubuntu安装bochs,nasm

    1 ubuntu上安装bochs nasm 1 1 安装缘由 最近想自己做个操作系统玩一玩巩固巩固知识 工欲善其事 必先利其器 开发操作系统首先得搭建环境 编程语言上我选择C和汇编完成 开发环境是在我装的一个虚拟机ubuntu上 ubunt
  • 【满分】【华为OD机试真题2023 JAVA&JS】统计匹配的二元组个数

    华为OD机试真题 2023年度机试题库全覆盖 刷题指南点这里 统计匹配的二元组个数 知识点数组 时间限制 1s 空间限制 32MB 限定语言 不限 题目描述 给定两个数组A和B 若数组A的某个元素A i 与数组B中的某个元素B j 满足 A
  • NodeJS - Express使用

    文章目录 1 参数 1 1 获取URL中的动态参数 2 静态资源 2 1 挂载路径前缀 3 nodemon 4 1路由 4 1 路由的匹配过程 4 2 模块化路由 4 3 为路由模块添加前缀 5 中间件 5 1 全局生效的中间件 5 2 全
  • 功能强大,但因安全隐患被企业禁用的Python内置函数

    功能强大 但却因安全隐患被企业禁用的Python内置函数 eval 函数是Python的内置函数 功能非常强大 但是存在不小的安全隐患 有些企业或项目出于安全考虑 禁止使用eval 函数 会在一些安全相关的扫描校验中进行识别和拦截 杜绝使用
  • 【嵌入式】虚拟机未能将管道连接到虚拟机: 系统找不到指定的文件

    这两天虚拟机莫名奇妙的爆出这个错误 在升级win11过后解决嘞这个问题 但是win11确实不好用最后退回win10这个问题又出现了 这里记录一下我的解决办法 设置为管理员运行程序 然后遇到新的报错了 进入控制面板选择C 2015修复环境 到
  • React性能提升

    了解react如何提升性能将有助于我们更好的编写代码 个人认为react中很多的性能优化 其实都是围绕着react的核心diff算法来展开的 通过优化 减少diff算法中一些不必要的步骤 从而来提高性能 下面是我平时开发总结出来的一些经验
  • QT控件之(TableView)中设置为不可编辑状态

    加入以下一句代码 ui gt tableView gt setEditTriggers QAbstractItemView NoEditTriggers
  • 【H.264/AVC视频编解码技术详解】二十三、帧间预测编码(1):帧间预测编码的基本原理

    H 264 AVC视频编解码技术详解 视频教程已经在 CSDN学院 上线 视频中详述了H 264的背景 标准协议和实现 并通过一个实战工程的形式对H 264的标准进行解析和实现 欢迎观看 纸上得来终觉浅 绝知此事要躬行 只有自己按照标准文档
  • STM32初始化USART后只发送了一个0x00,而无法发送其他数据的解决方法

    GPIO InitTypeDef GPIO InitStructure USART InitTypeDef USART InitStructure RCC APB2PeriphClockCmd RCC APB2Periph GPIOB EN
  • Tornado入门教程

    Overview FriendFeed是一款使用 Python 编写的 相对简单的 非阻塞式 Web 服务器 其应用程序使用的 Web 框架看起来有些像 web py 或者 Google 的 webapp 不过为了能有效利用非阻塞式服务器环
  • 【数据结构与算法】顺序表精讲

    所属专栏 数据结构与算法 前期内容 绪论1 抽象数据类型 绪论2 时间复杂度与空间复杂度 与时间复杂度相关的OJ面试题 抽象数据类型的实现 目录 1 1线性表的定义和特点 1 2案例引入 eg 图书信息管理系统 1 3线性表的类型定义 基本