线性表和顺序表的区别

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线性表和顺序表的区别相关的知识,希望对你有一定的参考价值。

如题

线性表仅仅是逻辑上的定义而已,即具有相同特性数据元素的有限序列,(比如一个字符数组或者一个整型数组 再或者链表 )并不是说,线性表就一定是链表或者顺序表(链表和顺序表都满足线性表的定义,只是实现方式不一样,顺序表采用顺序存储方式,内存中开辟的地址是连续的,而链表采用链式存储的方式,地址是可以不连续的)两者存储方式各有优缺点,看情况而定。

下面是数据结构中对于线性表的一段定义与操作

代码来源线性表--顺序表的定义与操作示例

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAXSIZE 20      /* 存储空间初始分配量 */
typedef int ElemType;   /* ElemType类型根据实际情况而定,这里假设为int */
typedef struct

ElemType data[MAXSIZE]; /* 数组,存储数据元素,最大值为MAXSIZE */
int length;             /* 线性表当前长度 */
SqList;
//顺序表的初始化
SqList Init()
   //构造一个空的线性表L,时间复杂度O(1)
SqList L;       //定义一个顺序表
L.length = 0;   //顺序表的长度为0
return L;       //返回空顺序表

//顺序表的建立
SqList Create(SqList L)

int i;
srand((unsigned)time(NULL));
for(i=0; i < 10; i++)

L.data[i] = rand()%100;
L.length++;

return L;

/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */
/* 操作结果:用e返回L中第i个数据元素的值,注意i是指位置,第1个位置的数组是从0开始 */
ElemType GetElem(SqList L,int i)
//
if(i < 1 || i > L.length)

printf("查找位置错误!\\n");//检查查询位置是否合法
return 0;

else
return L.data[i-1];

//顺序表的插入
SqList SqListInsert(SqList L, int i, ElemType x)
   //在顺序表中的第i个位置插入元素x
if(L.length == MAXSIZE)
printf("表已经满了\\n");//插入时,必须检查表是否已经满了。否则会出现溢出错误
else if(i < 1 || i > L.length)
printf("插入位置错误\\n");//判断插入位置的有效性
int j;
for(j = L.length-1; j >= i - 1; j--)//第i个位置元素逐个后移
L.data[j+1] = L.data[j];
L.data[i-1] = x;                        //插入元素x
L.length++;                         //顺序表长度增1
return L;

SqList SqListDelete(SqList L,int i)
//删除顺序表中的第i个位置的元素
if(i < 1 || i > L.length)
printf("删除位置错误\\n");     //检查删除位置是否合法
int j;
for(j = i-1; j < L.length; j++)
L.data[j] = L.data[j+1];        //将第i个位置之后的元素前移
L.length--;                         //顺序表长度-1
return L;

int main()

SqList nmList;
nmList = Init();
nmList = Create(nmList);
int find;
int found;
int pos;
ElemType value;
char opp;
int i;
printf("顺序表初始化为:");
for(i=0; i < nmList.length; i++)

printf("%d ", nmList.data[i]);

printf("\\n1.查看顺序表 \\n2.查找 \\n3.插入 \\n4.删除 \\n0.退出 \\n请选择你的操作:\\n");
while(opp != '0')
scanf("%c",&opp);
//printf("\\n 1.查找 \\n 2.插入 \\n 3.排序 \\n 0.退出 \\n 请选择你的操作:\\n");
switch(opp)
case '1':
printf("\\n查看顺序表:");
for(i=0; i < nmList.length; i++)

printf("%d ", nmList.data[i]);

printf("\\n");
break;
case '2':
printf("\\n进入查找功能,请问需要查找第几个元素:");
scanf("%d",&find);
printf("%d",find);
found = GetElem(nmList, find);
printf("第%d个值为%d ", find, found);
printf("\\n");
break;
case '3':
printf("进入插入功能,请输入插入元素位置:");
scanf("%d",&pos);
printf("请输入插入元素的值:");
scanf("%d",&value);
nmList = SqListInsert(nmList,pos,value);
printf("\\n插入元素后顺序表为:");
for(i=0; i < nmList.length; i++)

printf("%d ", nmList.data[i]);

printf("\\n");
break;
case '4':
printf("进入删除功能,请输入删除元素位置:");
scanf("%d",&pos);
nmList = SqListDelete(nmList,pos);
printf("\\n删除元素后顺序表为:");
for(i=0; i < nmList.length; i++)

printf("%d ", nmList.data[i]);

printf("\\n");
break;
case '0':
exit(0);


参考技术A 线性表是链式存储结构,用链表实现,使用空间多,且合理。而顺序表基本上是用数组实现的,使用空间有限,会造成浪费。 参考技术B 顺序表 静态分配。程序执行之前必须明确规定存储规模。
随机存取结构,主要是进行查找,很少做插入和删除操作时顺序表。
线性表 动态分配。只要内存空间尚有空闲,就不会产生溢出。
从头指针起顺着扫描才能取得。

看完这篇文章还不会顺序表和链表,请寄刀片给我

一、线性表

线性表(linear list )是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

二、顺序表

💦 顺序表的概念和结构

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

静态顺序表:使用定长数组存储
缺点就是小了不够用,大了浪费

动态顺序表:使用动态开辟的数组存储
可根据自己的需要调整大小

💦 顺序表各接口实现 (动图分析)

静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N固定了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表以及增删查改等功能

这里新建三个文件:
1️⃣ SeqList.h文件,用于函数声明

2️⃣ SeqList.c文件,用于函数的定义

3️⃣ Test.c文件,用于测试函数


接口1:定义结构体SLT

typedef int SQDataType;
typedef struct SeqList
{
	SQDataType* a;//指向动态开辟的空间
	int size;//有效数据个数
	int capacity;//当前容量
}SLT;

接口2:初始化结构体 (SeqListInit)

函数原型:

函数实现:

void SeqListInit(SLT* psl)
{
	assert(psl);
	psl->a = NULL;
	psl->size = psl->capacity = 0;
}

📐 测试

接口3:检查容量 (SeqListCheckCapcity)

函数原型:

函数实现:

void SeqListCheckCapcity(SLT* psl)
{
	//一般情况为了避免频繁插入数据而增容,或者一下开辟很大的空间,我们一般是每次增容2倍
	assert(psl);
	if (psl->size == psl->capacity)
	{
		//第一次是增容4*sizeof(SQDataType)
		size_t newcapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
		int* temp = (SQDataType*)realloc(psl->a, newcapacity * sizeof(SQDataType));
		if (temp != NULL)
			psl->a = temp;
		else
			return;
		psl->capacity = newcapacity;
	}
}

📐 测试

接口4:指定位置插入数据 (SeqListInser)

函数原型

函数实现

void SeqListInser(SLT* psl, size_t pos, SQDataType x)
{
	assert(psl);
	//因为pos是size_t类型的,所以不用断言它是否小于0了,不过还是建议加上
	assert(pos > 0 && pos <= psl->size + 1);
	//判断是否要增容
	SeqListCheckCapcity(psl);
	int start = pos - 1;
	int end = psl->size - 1;
	while (start <= end)
	{
		psl->a[end + 1] = psl->a[end];
		end--;
	}
	psl->a[pos - 1] = x;
	psl->size++;
}

📐 测试

接口5:输出数据 (SeqListPrint)

函数原型

函数实现

void SeqListPrint(SLT* psl)
{
	assert(psl);
	int i = 0;
	for (i = 0; i < psl->size; i++)
	{
		printf("%d ", psl->a[i]);
	}
	printf("\\n");
}

📐 测试

接口6:尾插 (SeqListPushBack)

函数原型

函数实现

void SeqListPushBack(SLT* psl, SQDataType x)
{
	assert(psl);
	//根据分析尾部插入的话,传size+1吻合SeqListInser函数
	SeqListInser(psl, psl->size + 1, x);
}

📐 测试

接口7:头插 (SeqListPushFront)

函数原型

函数实现

void SeqListPushFront(SLT* psl, SQDataType x)
{
	assert(psl);
	SeqListInser(psl, 1, x);//根据分析头部插入的话,传1吻合SeqListInser函数
}

📐 测试

接口8:指定位置删除数据 (SeqListErase)

函数原型

函数实现

void SeqListErase(SLT* psl, size_t pos)
{
	assert(psl);
	//因为pos是size_t类型的,所以不用断言它是否小于0了,不过还是建议加上
	assert(pos > 0 && pos <= psl->size);
	size_t start = pos - 1;
	while (start < psl->size - 1)	
	{
		psl->a[start] = psl->a[start + 1];	
		start++;
	}
	psl->size--;
}

📐 测试

接口9:尾删 (SeqListPopBack)

函数原型

函数实现

void SeqListPopBack(SLT* psl)
{
	assert(psl);
	SeqListErase(psl, psl->size);
}

📐 测试

接口10:头删 (SeqListPopFront)

函数原型

函数实现

void SeqListPopFront(SLT* psl)
{
	assert(psl);
	SeqListErase(psl, 1);
}

📐 测试

接口11:查找 (SeqListFind)

函数原型

函数实现

//找到返回下标,找不到返回-1
int SeqListFind(SLT* psl, SQDataType x)
{
	assert(psl);
	int i = 0;
	for (i = 0; i < psl->size; i++)
	{
		if (psl->a[i] == x)
		{
			return i;
		}
	}
	return -1;
}

📐 测试

接口12:统计 (SeqListSize)

函数原型

函数信息

size_t SeqListSize(SLT* psl)
{
	assert(psl);
	return psl->size;
}

📐 测试

接口13:修改 (SeqListAt)

函数原型

函数实现

size_t SeqListAt(SLT* psl, size_t pos, SQDataType x)
{
	assert(psl);
	assert(pos > 0 && pos <= psl->size);
	psl->a[pos - 1] = x;	
}

📐 测试

接口14:销毁 (SeqListDestory)

函数原型

函数实现

void SeqListDestory(SLT* psl)	
{
	assert(psl);
	if (psl->a)
	{
		free(psl->a);
		psl->a = NULL;
	}
	psl->size = psl->capacity = 0;
}

📐 测试


📝 完整代码

SeqList.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int SQDataType;
typedef struct SeqList
{
	SQDataType* a;//指向动态开辟的空间
	int size;//有效数据个数
	int capacity;//当前容量
}SLT;

//初始化
void SeqListInit(SLT* psl);
//检查容量
void SeqListCheckCapcity(SLT* psl);
//尾部插入
void SeqListPushBack(SLT* psl, SQDataType x);
//头部插入
void SeqListPushFront(SLT* psl, SQDataType x);
//尾部删除
void SeqListPopBack(SLT* psl);
//头部删除
void SeqListPopFront(SLT* psl);
//显示
void SeqListPrint(SLT* psl); 
//查找
int SeqListFind(SLT* psl, SQDataType x);	
//从某个值的位置插入
void SeqListInser(SLT* psl, size_t pos, SQDataType x);
//从某个值的位置删除	
void SeqListErase(SLT* psl, size_t pos);
//统计当前有多少数据 - 不要认为这样没必要,这是规范的操作
size_t SeqListSize(SLT* psl);
//修改
size_t SeqListAt(SLT* psl, size_t pos, SQDataType x);
//销毁
void SeqListDestory(SLT* psl);

SeqList.c

#include"SeqList.h"
//初始化
void SeqListInit(SLT* psl)
{
	assert(psl);
	psl->a = NULL;
	psl->size = psl->capacity = 0;
}
//检查容量
void SeqListCheckCapcity(SLT* psl)
{
	assert(psl);
	//检查容量以决定要不要增容 - 2倍增容(一般情况为了避免频繁插入数据增容,或者一下开辟很大的空间,我们一般是每次增容2倍
	if (psl->size == psl->capacity)
	{
		size_t newcapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
		int* temp = (SQDataType*)realloc(psl->a, newcapacity * sizeof(SQDataType));
		if (temp != NULL)
			psl->a = temp;
		else
			return;
		psl->capacity = newcapacity;
	}
}
//尾部插入
void SeqListPushBack(SLT* psl, SQDataType x)
{
	/*assert(psl);
	SeqListCheckCapcity(psl);
	psl->a[psl->size] = x;
	psl->size++;*/
	assert(psl);
	SeqListInser(psl, psl->size + 1, x);//根据分析尾部插入的话,传size+1吻合SeqListInser函数
}
//头部插入
void SeqListPushFront(SLT* psl, SQDataType x)
{
	//assert(psl);
	//SeqListCheckCapcity(psl);
	挪动数据
	//int end = psl->size - 1;
	//while (end >= 0)
	//{
	//	psl->a[end + 1] = psl->a[end];
	//	--end;
	//}
	//psl->a[0] = x;
	//psl->size++;

	assert(psl);
	SeqListInser(psl, 1, x);//根据分析头部插入的话,传1吻合SeqListInser函数

}
//尾部删除
void SeqListPopBack(SLT* psl)
{
	//assert(psl);
	//assert(psl->size > 0);//如果没有数据就报错
	//psl->size--;

	assert(psl);
	SeqListErase(psl, psl->size);
}
//头部删除
void SeqListPopFront(SLT* psl)
{
	//assert(psl);
	//assert(psl->size > 0);//如果没有数据就报错
	//int start = 0;
	//while (start < psl->size - 1)
	//{
	//	psl->a[start] = psl->a[start + 1];
	//	start++;
	//}
	//psl->size--;	 

	assert(psl);
	SeqListErase(psl, 1);
}
//显示数据
void SeqListPrint(SLT* psl)
{
	assert(psl);
	int i = 0;
	for (i = 0; i < psl->size; i++)
	{
		printf("%d ", psl->a[i]);
	}
	printf("\\n");
}
//销毁
void SeqListDestory(SLT* psl)	
{
	assert(psl);
	if (psl->a)
	{
		free(psl->a);
		psl->a = NULL;
	}
	psl->size = psl->capacity = 0;
}
//查找 - 找到返回下标,找不到返回-1
int SeqListFind(SLT* psl, SQDataType x)
{
	assert(psl);
	int i = 0;
	for (i = 0; i < psl->size; i++)
	{
		if (psl->a[i] == x)
		{
			return i;
		}
	}
	return -1;
}
//从某个位置插入,但是区间只能在第1个元素之前1个元素到最后1个元素之后1个元素
void SeqListInser(SLT* psl, size_t pos, SQDataType x)
{
	assert(psl);
	assert(pos > 0 && pos <= psl->size + 1);//因为pos是size_t类型的,所以不用断言它是否小于0了,不过还是建议加上
	SeqListCheckCapcity(psl);
	int start = pos - 1;
	int end = psl->size - 1;
	while (start <= end)
	{
		psl->a[end + 1] = psl->a[end];
		end--;
	}
	psl->a[pos - 1] = x;
	psl->size++;
}
//从某个位置的值删除	
void SeqListErase(SLT* psl, size_t pos)
{
	assert(psl);
	assert(pos > 0 && pos <= psl->size);//因为pos是size_t类型的,所以不用断言它是否小于0了,不过还是建议加上
	size_t start = pos - 1;
	while (start < psl->size - 1)	
	{
		psl->a[start] = psl->a[start + 1];	
		start++;
	}
	psl->size--;
}
//统计当前有多少数据 - 其实不要认为这样没必要,这是一些规范的基本操作
size_t SeqListSize(SLT* psl)
{
	assert(psl);
	return psl->size;
}
//修改
size_t SeqListAt(SLT* psl, size_t pos, SQDataType x)
{
	assert(psl);
	assert(pos > 0 && pos <= psl->size);
	psl->a[pos - 1] = x;	
}

Test.c

#include"SeqList.h"
void TestSeqList1()
{
	SLT s;
	//初始化
	SeqListInit(&s);
	//尾插
	SeqListPushBack(&s, 1);
	SeqListPushBack(&s, 2);
	SeqListPushBack(&s, 3);
	SeqListPushBack(&s, 4);
	SeqListPushBack(&s, 5);
	SeqListPushBack(&s, 6);
	SeqListPrint(&s);//显示数据
	//头插
	SeqListPushFront(&s, -1);
	SeqListPushFront(&s, -2);
	SeqListPushFront(&s, -3);
	SeqListPrint(&s);//显示数据
	//从某个值的位置插入
	SeqListInser(&s, 1, -4);
	SeqListPrint(&s);//显示数据
	//尾删
	SeqListPopBack(&s);
	SeqListPrint(&s);//显示数据
	//头删
	SeqListPopFront(&s);
	SeqListPrint(&s);//显示数据
	从某个值的位置删除	
	SeqListErase(&s, 8);
	SeqListPrint(&s);//显示数据
	//修改
	SeqListAt(以上是关于线性表和顺序表的区别的主要内容,如果未能解决你的问题,请参考以下文章

C中线性表和链表的区别

链表和顺序表的一些区别

线性表——顺序表和链表

链表是顺序表吗?

数据结构之顺序表和链表的区别

《数据结构》复习之线性表(顺序表和链表)