数据结构 - 顺序表及其基本操作的实现

14 篇文章 0 订阅

线性表的顺序存储结构

顺序表定义
线性表的顺序存储结构, 指的是用一段地址连续的存储单元依次存储线性表的数据元素.
线性表的(a1,a2,…,an)的顺序存储示意图如下:
在这里插入图片描述
顺序表的存储方式
        线性表的顺序存储结构,就是在内存中找一块地方,通过占位的形式,把一定的空间占用,然后把相同数据类型的数据元素依次存放在这块空地中.既然线性表的每个数据元素的类型都相同,所以可以用C语言(其他语言也相同)的一维数组来实现顺序存储结构,即把第一个数据元素存到数组下标为0的位置中,接着把线性表相邻的元素存储在数组中的相邻位置.
        来看线性表的顺序存储的结构代码:

#define SIZE 10  //存储空间初始分配量
typedef struct Sqlist
{
	int elem[SIZE]; //数组存储数据元素,最大值为SIZE
	int usedsize; //顺序表当前长度
}Sqlist,*PSqlist;

这里,我们发现描述顺序存储结构需要三个属性:

  • 存储空间的起始位置 : 数组elem , 它的存储位置就是存储空间的存储位置.
  • 线性表的最大存储容量 : 数组长度SIZE.
  • 线性表的当前长度 : usedsize.
    数据长度与线性表长度的区别
    数组的长度 : 是存放线性表的存储空间的长度 , 存储分配后这个量是一般不变的.
    线性表的长度 : 是线性表中数据元素的个数,随着线性表插入和删除操作的进行,这个量是变化的.
    在任意时刻,线性表的长度 <= 数组的长度

顺序表的基本操作

1.顺序表的初始化

顺序表的初始化,就是初始化一个空的顺序表,只需要把当前长度置为0即可.

void InitSqlist(PSqlist Psq)//初始化
{
	assert(Psq != NULL);
	if(Psq == NULL)
	{
		return ;
	}
	Psq->usedsize = 0;
}

2.在 pos 位置插入 val 值

在顺序表的第pos个位置插入元素,首先将第pos个位置及其之后的元素依次向后移动一个位置,然后将值为val的元素插入到第pos个位置.在元素的移动时,要注意一个小技巧,那就是从最后一个元素开始,从后往前的移动.在插入元素之前还要判断插入的位置是否合法 , 顺序表的是否已满.在插入之后要更新顺序表的长度,即usedsize++.

bool Insert(PSqlist Psq,int pos ,int val)//在 pos 位置插入 val 值
{
	assert(Psq != NULL);
	if(pos < 0 || pos > Psq->usedsize || isFull(Psq))//pos位置必须合法
	{
		return false;
	}
	//挪数据
	for(int i = Psq->usedsize-1;i >= pos;i--)
	{
		Psq->elem[i+1] =Psq->elem[i]; 
	}
	Psq->elem[pos] = val;
	Psq->usedsize++;
	return true;
}

3.从pos位置开始查找 key 值

因为顺序表是依靠数组来实现,所以可以通过访问下标的方式来进行查找 , 如果找到与所要查询key值相等的元素 , 则返回其在表中的位置 , 如果没有找到 , 则返回 -1.

int Search(PSqlist Psq,int pos,int key)
{
	assert(Psq != NULL);
	if(pos < 0 || pos >= Psq->usedsize || isEmpty(Psq))
	{
		return -1;
	}
	for(int i = pos; i < Psq->usedsize;i++)
	{
		if(Psq->elem[i] == key)
		{
			return i;
		}
	}
	return -1;
}

4.删除pos位置的值

删除表中第pos个位置的值 , 因为顺序表是依靠数组来实现 , 而数组没有删除单个某一个元素的功能 , 所以只需用后面的元素覆盖pos位置的元素即可.值得注意的是,在进行删除操作之前 , 需要判断表是否为空 , 如果为空 , 则返回false;在删除元素之后 , 要将顺序表长度,即suedsize- -;

bool DeletePos(PSqlist Psq,int pos,int *rtv)//删除 pos 位置的值
{
	assert(Psq != NULL );
	if(pos < 0 || pos >= Psq->usedsize || isEmpty(Psq))
	{
		return false;
	}
	if(rtv != NULL)
	{
		*rtv = Psq->elem[pos];
	}
	for(int i = pos ; i < Psq->usedsize-1;i++)
	{
		Psq->elem[i] = Psq->elem[i+1];
	}
	Psq->usedsize--;
	return true;
}

5.删除一个 key 值

首先调用 Search() 函数找到元素的位置index , 若未找到则返回 -1 , 若找到 , 则调用 DeletePos() 函数删除 index位置的元素(即 key 值所在位置)

bool Delete(PSqlist Psq,int pos,int key)//删除一个 key 值
{
	assert(Psq != NULL);
	if(pos < 0 || pos >= Psq->usedsize)
	{
		return false;
	}
	int index = Search(Psq,pos,key);
	if(index == -1)
	{
		return false;
	}
	return DeletePos(Psq,index,NULL);
}

6.得到pos位置的值

这个pos位置 , 指的是数组的下标

bool GetElem(PSqlist Psq,int pos,int *rtv)//得到pos位置的值
{
	assert(Psq != NULL );
	if(pos < 0 || pos >= Psq->usedsize || isEmpty(Psq))
	{
		return false;
	}
	*rtv = Psq->elem[pos];
	return true;
}

7.得到顺序表的长度

int GetLength(PSqlist Psq)
{
	assert(Psq != NULL );
	return Psq->usedsize;
}

8.清空顺序表

就是将顺序表的长度suedsize置为 0 .

void Clear(PSqlist Psq)
{
	assert(Psq != NULL );
	Psq->usedsize = 0;
}

9.打印顺序表

void Show(PSqlist Psq)
{
	assert(Psq != NULL );
	for(int i = 0;i< Psq->usedsize;i++)
	{
		printf("%d ",Psq->elem[i]);
	}
	printf("\n");
}

以下是顺序表的完整代码

sqlist.h

//函数的定义  结构体的定义
#pragma once//预防头文件重复引用
#define SIZE 10

typedef struct Sqlist
{
	int elem[SIZE];
	int usedsize;
}Sqlist,*PSqlist;

void InitSqlist(PSqlist Psq);//初始化

bool Insert(PSqlist Psq,int pos ,int val);//在 pos 位置插入 val 值

int Search(PSqlist Psq,int pos,int key);//查找 key 值

bool DeletePos(PSqlist Psq,int pos,int *rtv);//删除 pos 位置的值

bool Delete(PSqlist Psq,int pos,int key);//删除一个 key 值

bool GetElem(PSqlist Psq,int pos,int *rtv);

int GetLength(PSqlist Psq);

void Clear(PSqlist Psq);

void Show(PSqlist Psq);

sqlist.cpp

#include<stdio.h>
#include"sqlist.h"
#include<assert.h>


void InitSqlist(PSqlist Psq)//初始化
{
	assert(Psq != NULL);
	if(Psq == NULL)
	{
		return ;
	}
	Psq->usedsize = 0;
}

static bool isFull(PSqlist Psq)
{
	return Psq->usedsize == SIZE;
}

bool Insert(PSqlist Psq,int pos ,int val)//在 pos 位置插入 val 值
{
	assert(Psq != NULL);
	if(pos < 0 || pos > Psq->usedsize || isFull(Psq))//pos位置必须合法
	{
		return false;
	}
	//挪数据
	for(int i = Psq->usedsize-1;i >= pos;i--)
	{
		Psq->elem[i+1] =Psq->elem[i]; 
	}
	Psq->elem[pos] = val;
	Psq->usedsize++;
	return true;
}

//从pos位置开始查找 key 值==>第一次出现==>返回下标 -1
static bool isEmpty(PSqlist Psq)
{
	return Psq->usedsize == 0;
}

int Search(PSqlist Psq,int pos,int key)
{
	assert(Psq != NULL);
	if(pos < 0 || pos >= Psq->usedsize || isEmpty(Psq))
	{
		return -1;
	}
	for(int i = pos; i < Psq->usedsize;i++)
	{
		if(Psq->elem[i] == key)
		{
			return i;
		}
	}
	return -1;
}

bool DeletePos(PSqlist Psq,int pos,int *rtv)//删除 pos 位置的值
{
	assert(Psq != NULL );
	if(pos < 0 || pos >= Psq->usedsize || isEmpty(Psq))
	{
		return false;
	}
	if(rtv != NULL)
	{
		*rtv = Psq->elem[pos];
	}
	for(int i = pos ; i < Psq->usedsize-1;i++)
	{
		Psq->elem[i] = Psq->elem[i+1];
	}
	Psq->usedsize--;
	return true;
}

bool Delete(PSqlist Psq,int pos,int key)//删除一个 key 值
{
	assert(Psq != NULL);
	if(pos < 0 || pos >= Psq->usedsize)
	{
		return false;
	}
	int index = Search(Psq,pos,key);
	if(index == -1)
	{
		return false;
	}
	return DeletePos(Psq,index,NULL);
}

bool GetElem(PSqlist Psq,int pos,int *rtv)//得到pos位置的值
{
	assert(Psq != NULL );
	if(pos < 0 || pos >= Psq->usedsize || isEmpty(Psq))
	{
		return false;
	}
	*rtv = Psq->elem[pos];
	return true;
}

int GetLength(PSqlist Psq)
{
	assert(Psq != NULL );
	return Psq->usedsize;
}

void Clear(PSqlist Psq)
{
	assert(Psq != NULL );
	Psq->usedsize = 0;
}

void Show(PSqlist Psq)
{
	assert(Psq != NULL );
	for(int i = 0;i< Psq->usedsize;i++)
	{
		printf("%d ",Psq->elem[i]);
	}
	printf("\n");
}

源.cpp

#include<stdio.h>
#include<assert.h>
#include<string.h>
#include"sqlist.h"
//#include<vld.h> //测内存泄漏 , 没有插件的可以屏蔽

int main()
{
	DSqlist ds;
	InitList(&ds);
	for (int i = 0; i < 70; i++)
	{
		Insert(&ds,i,i);
	}
	Show(&ds);
	Delete(&ds,0,2);
	Delete(&ds,0,9);
	Delete(&ds,0,0);
	Show(&ds);
	Destroy(&ds);
	Show(&ds);
	return 0;
}

具有动态扩容的顺序表的建立

为什么要引入动态扩容? 原因是在实际问题中 , 我们不知道到底需要开辟多大SIZE的内存 , 开的太大 , 会造成资源浪费 , 开的太小 , 则又不够使用. 所以为了合理利用资源 , 需要引进动态扩容.

1.顺序表的初始化

就是把顺序表初始化为空的顺序表 , 为数组动态开辟INIT_SIZE大小的数组.并将其usersize置为0
来看动态扩容版顺序表的结构代码:

#define INIT_SIZE 10 //最大数组长度
typedef struct DSqlist
{
	int *elem;//存储数据的内存
	int usedsize;//有效数据个数
	int size;//总单元数
}DSqlist,*PDSqlist;
void InitList(PDSqlist psq)
{
	assert(psq != NULL);
	psq->elem = (int *)malloc(sizeof(int)*INIT_SIZE);
	assert(psq->elem != NULL);
	psq->size = INIT_SIZE;
	psq ->usedsize = 0;
}

这里为什么要引进变量 size , 原因是用 size 来记录当前数组的总单元数 , 如果有效数据个数 == 总单元数 , 那么就说明需要动态扩容 .

2.为顺序表动态扩容

当顺序表被判为满的时候 , 需要对其进行二倍扩容.

static void Inc(PDSqlist psq)
{
	assert(psq != NULL);
	int *p = (int *)realloc(psq->elem,psq->size*sizeof(int)*2);
	if(p != NULL)
	{
		psq->elem = p;
	}
	psq->size *=2;	
}

在这个代码中存在一个优化 , 就是扩容时使用的是realloc()函数 , 而非malloc()或者calloc()函数.这样写的目的就是能够优化内存.

3.在 pos 位置插入 val == 插入操作

与之前Insert()函数不同的是 , 当顺序表被判为满的时候 , 需要调用动态扩容函数来为顺序表进行扩容.

bool Insert(PDSqlist psq,int pos,int val)
{
	assert(psq != NULL);
	if(isFull(psq))
	{
		Inc(psq);
	}
	for(int i = psq->usedsize-1;i >= pos;i--)
	{
		psq->elem[i+1] = psq->elem[i];
	}
	psq->elem[pos] = val;
	psq->usedsize++;
	return true;
}

4.释放动态开辟的存储空间

因为是在堆上开辟的内存 , 所以需要手动进行资源的释放 , 否则就会遭成资源泄漏.

void Clear(PDSqlist psq)
{
	psq->usedsize = 0;
	psq->size = 0;
}

void Destroy(PDSqlist psq)
{
	free(psq->elem);
	psq->elem = NULL;
	Clear(psq);
}

以下是动态扩容版的顺序表完整代码

dsqlist.h

#pragma once
#define INIT_SIZE 10
typedef struct DSqlist
{
	int *elem;//存储数据的内存
	int usedsize;//有效数据个数
	int size;//总单元数
}DSqlist,*PDSqlist;
void InitList(PDSqlist psq);

bool Insert(PDSqlist psq,int pos,int val);

int Search(PDSqlist Psq,int pos,int key);//查找 key 值

bool DeletePos(PDSqlist Psq,int pos,int *rtv);//删除 pos 位置的值

bool Delete(PDSqlist Psq,int pos,int key);//删除一个 key 值

void Clear(PDSqlist psq);

void Destroy(PDSqlist psq);

void Show(PDSqlist psq);

dsqlist.cpp

#include"dsqlist.h"
#include<assert.h>
#include<stdlib.h>
#include<stdio.h>

void InitList(PDSqlist psq)
{
	assert(psq != NULL);
	psq->elem = (int *)malloc(sizeof(int)*INIT_SIZE);
	assert(psq->elem != NULL);
	psq->size = INIT_SIZE;
	psq ->usedsize = 0;
}

static bool isFull(PDSqlist psq)
{
	if(psq->usedsize == psq->size)
	{
		return true;
	}
	return false;
}

static void Inc(PDSqlist psq)
{
	assert(psq != NULL);
	int *p = (int *)realloc(psq->elem,psq->size*sizeof(int)*2);
	if(p != NULL)
	{
		psq->elem = p;
	}
	psq->size *=2;	
}

bool Insert(PDSqlist psq,int pos,int val)
{
	assert(psq != NULL);
	if(isFull(psq))
	{
		Inc(psq);
	}
	for(int i = psq->usedsize-1;i >= pos;i--)
	{
		psq->elem[i+1] = psq->elem[i];
	}
	psq->elem[pos] = val;
	psq->usedsize++;
	return true;
}

static bool isEmpty(PDSqlist Psq) 
{
	return Psq->usedsize == 0;
}

int Search(PDSqlist Psq,int pos,int key)//查找 key 值
{
	assert(Psq != NULL);
	if(pos < 0 || pos >= Psq->usedsize || isEmpty(Psq))
	{
		return -1;
	}
	for(int i = pos;i < Psq->usedsize;i++)
	{
		if(Psq->elem[i] == key)
		{
			return i;
		}
	}
	return -1;
}


bool DeletePos(PDSqlist Psq,int pos,int *rtv)//删除 pos 位置的值
{
	assert(Psq != NULL);
	if(pos < 0 || pos >= Psq->usedsize || isEmpty(Psq))
	{
		return false;
	}
	if(rtv != NULL)
	{
		*rtv = Psq->elem[pos];
	}
	for(int i = pos;i < Psq->usedsize-1;i++)
	{
		Psq->elem[i] = Psq->elem[i+1];
	}
	Psq->usedsize--;
	return true;
}

bool Delete(PDSqlist Psq,int pos,int key)//删除一个 key 值
{
	assert(Psq != NULL);
	int index = Search(Psq,pos,key);
	if(index == -1)
	{
		return false;
	}
	return DeletePos(Psq,index,NULL);
}

void Clear(PDSqlist psq)
{
	psq->usedsize = 0;
	psq->size = 0;
}

void Destroy(PDSqlist psq)
{
	free(psq->elem);
	psq->elem = NULL;
	Clear(psq);
}

void Show(PDSqlist psq)
{
	if(psq->usedsize == 0)
	{
		return;
	}
	for (int i = 0; i < psq->usedsize; i++)
	{
		printf("%d ",psq->elem[i]);
	}
	printf("\n");
}

源.cpp

#include<stdio.h>
#include<assert.h>
#include<string.h>
#include"dsqlist.h"
//#include<vld.h>//测内存泄漏

int main()
{
	DSqlist ds;
	InitList(&ds);
	for (int i = 0; i < 70; i++)
	{
		Insert(&ds,i,i);
	}
	Show(&ds);
	Delete(&ds,0,2);
	Delete(&ds,0,9);
	Delete(&ds,0,0);
	Show(&ds);
	Destroy(&ds);
	Show(&ds);
	return 0;
}

求两个顺序表的交集

/*
求两个顺序表的交集
**思路:
**新开辟一个顺序表存放交集
**如果遍历两个顺序表,查找相同元素写入交集中,并记录长度
*/
void Intersection(PDSqlist sq1,PDSqlist sq2,PDSqlist sq3)
{
	int k = 0;  // 计数器
	for (int i = 0; i < sq1->usedsize; i++)
	{
		int j = 0;
		while (j < sq2->usedsize && sq2->elem[j] != sq1->elem[i])
		{
			j++;
		}
		if (j < sq2->usedsize)
		{
			Insert(sq3,k++,sq1->elem[i]);
		}
	}
}

求两个顺序表的并集

/*
求两个顺序表的并集
**思路:
**新开辟一个顺序表存放并集
**由于是并集,所以结果中至少包含了一个顺序表中的所有元素
**因此首先将第一个顺序表写入并集,然后遍历第二个的顺序表
**第二个顺序表中的该元素与第一个顺序表的所有元素均不相同,则写入新表
*/
void Union(PDSqlist sq1,PDSqlist sq2,PDSqlist sq4)
{
	for (int i = 0; i < sq1->usedsize; i++)
	{
		Insert(sq4,i,sq1->elem[i]);
	}
 
	int k = sq4->usedsize;
 
	for (int i = 0; i < sq2->usedsize; i++)
	{
		int j = 0;
		while (j < sq1->usedsize && sq1->elem[j] != sq2->elem[i])
		{
			j++;
		}		
		if (j >= sq1->usedsize)
		{
			Insert(sq4,k++,sq2->elem[i]);
		}
	}
}

源.cpp

#include<stdio.h>
#include<assert.h>
#include<string.h>
#include"dsqlist.h"
//#include<vld.h>//测内存泄漏

int main()
{
	DSqlist ds1;
	DSqlist ds2;
	DSqlist ds3;
	DSqlist ds4;
	InitList(&ds1);
	InitList(&ds2);
	InitList(&ds3);
	InitList(&ds4);
	for (int i = 0; i < 10; i++)
	{
		Insert(&ds1,i,i);
		Insert(&ds2,i,i+3);
	}
	Show(&ds1);
	Show(&ds2);
	Intersection(&ds1,&ds2,&ds3);
	Union(&ds1,&ds2,&ds4);
	Show(&ds3);
	Show(&ds4);
	Destroy(&ds1);
	Destroy(&ds2);
	Destroy(&ds3);
	Destroy(&ds4);
	return 0;
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值