深入认识数据结构(二)---顺序表的实现

顺序表

顺序表是线性结构中的一种----顺序储存结构

该种结构的占用的是内存中的一块连续的空间(和数组相似)

顺序表可以有两种实现方式:

  • 一种是用静态数组来实现,
#define NUM 100
typedef struct SeqList{
    
    
    int a[NUM];
    int size;//表长
}SeqList;

NUM是数组的大小,也就是顺序表中可以放置的元素个数。

我们可以用size来记录某个时刻数组中已经放入了多少个元素。

  • 一种是用动态开辟的方式实现
typedef struct SeqList {
    
    
	SeqListType* list;
	int size;//表长
	int capacity;
}SeqList;

利用指针以及动态内存开辟可以灵活的控制空间的大小

同样用size来记录已经存放了多少个元素

capacity和静态数组中的NUM作用相似,不同的是Capacity可以按照需求扩大缩小。

两者比较:我们在使用前并不能确定实际上需要用到多大的空间,所以我们采用了动态开辟的方法,这样可以灵活的控制空间的大小。

顺序表中需要实现的功能:

顺序表可以实现以下功能:

//函数的声明
//顺序表的初始化----SeqListInit

//尾插一个元素----SeqListPushBack
//尾删一个元素----SeqListPopBack

//头插一个元素----SeqListPushFront
//头删一个元素----SeqListPopFront

//任何位置的元素插入----SeqListInsert
//任何位置的元素删除----SeqListErase

//检查容量----SeqListCheckCapacity

//销毁顺序表----SeqListDeStroy

接下来我们依次来实现这些函数

顺序表的初始化

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

我们可以一开始让capacity 有一个初始值,也可以像这样将初始值赋为0

顺序表的容量

注意:我们在操作一个顺序表的时候,需要注意的是顺序表的容量是否已满,如果顺序表的容量满了,我们就需要扩容,这样我们才能继续向顺序表中添加元素。

void SeqListCheckCapacity(SeqList* psl)
{
    
    
	assert(psl);//判断psl是否为空指针

	if (psl->size == psl->capacity)
	{
    
    
		int newcapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
        //如果capacity的值为0(只有初始化的时候才可能为0),那么就将capacity 的值赋为4。
        //如果caoacity的值不为0,那么就让容量翻倍。

		SeqListType* tmp = (SeqListType*)realloc(psl->list, sizeof(SeqListType) * newcapacity);
		if (tmp == NULL)
		{
    
    
			printf("realloc:fail\n");
			exit(-1);
		}
		else
		{
    
    
			psl->list = tmp;
			psl->capacity = newcapacity;
		}

	}

}

当size 与capacity的值相同时,说明顺序表的容量已经满了,我们需要扩容

注意:我们需要判断是否扩容成功(判断tmp是否为空指针)。

向顺序表中添加元素

在顺序表的末尾添加元素

注意:1. 每一次向顺序表中添加元素都需要检查顺序表的容量。

2. 每一次添加一个元素后都需要将size的值加1。

void SeqListPushBack(SeqList* psl, SeqListType x)
{
    
    
	assert(psl);
	SeqListCheckCapacity(psl);
	psl->list[psl->size] = x;
	psl->size++;
}

向尾部添加元素时,psl->list[psl->size]就是我们要赋值的地址,由于size 的值很容易得到,所以向尾部添加元素得操作十分简单。

在顺序表的首部添加元素

void SeqListPushFront(SeqList* psl, SeqListType x)
{
    
    
	assert(psl);

	SeqListCheckCapacity(psl);
	
	int begin = psl->size;
	while (begin > 0)
	{
    
    
		psl->list[begin] = psl->list[begin - 1];
		begin--;
	}
	psl->list[0] = x;
	psl->size++;
	
}

在这里插入图片描述

在顺序表中的指定位置添加元素

void SeqListInsert(SeqList* psl, size_t pos, SeqListType x)
{
    
    
	assert(psl);
	if (pos > psl->size)
	{
    
    
		printf("越界");
	}
	SeqListCheckCapacity(psl);//检查容量
	size_t begin = psl->size;
	while (begin > pos)
	{
    
    
		psl->list[begin] = psl->list[begin - 1];
		begin--;
		}
	psl->list[pos] = x;
	psl->size++;
	
}

在指定位置添加元素的操作和在顺序表的头部添加元素的操作十分相似。

在这里插入图片描述

不过在插入元素之前我们需要判断传过来的参数是否符合条件

在这里插入图片描述

不同的插入位置会有不同的结果

在这里插入图片描述

向顺序表中删除元素

需要注意的是:每一次在顺序表中删除一个元素size的值就需要减1

删除顺序表末尾的元素

//尾删一个元素
void SeqListPopBack(SeqList* psl)
{
    
    
	assert(psl);

	if (psl->size > 0)
	{
    
    
		psl->size--;
	}

}

删除尾部的元素,只需要将表长(size)减1,因为我们是通过size来记录顺序表中有几个元素,如果size减1 ,就相当于顺序表中的元素个数减1,也就相当于删除了最后一个元素。

删除顺序表首部的元素

//头删一个元素
void SeqListPopFront(SeqList* psl)
{
    
    
	assert(psl);

	int begin = 1;
	while (begin < psl->size)
	{
    
    
		psl->list[begin-1] = psl->list[begin];
		begin++;
	}
	psl->size--;
}

删除元素表头部的元素,只需要从顺序表中的第二个元素开始,依次向前覆盖,直到最后一个元素的值,覆盖到倒数第二个元素的位置上,然后让表长(size)减1,就完成了头部元素的删除。

在这里插入图片描述

删除顺序表指定位置的元素

void SeqListErase(SeqList* psl, size_t pos)
{
    
    

	assert(psl);
	assert(pos < psl->size);
	
		size_t begin = pos+1;
		while (begin < psl->size)
		{
    
    
			psl->list[begin-1] = psl->list[begin];
			begin++;
		}
		psl->size--;
	

}

删除指定位置的元素方法和删除头部元素的方法类似:

在这里插入图片描述

销毁顺序表

由于我们使用的是动态内存开辟的方法实现的顺序表,所以在最后我们需要将动态开辟出来的空间释放。

void SeqListDeStroy(SeqList* psl)
{
	assert(psl);

	free(psl->list);
	psl->capacity = 0;
	psl->size = 0;
	psl->list = NULL;
}

顺序表的完整实现

头文件SeqList.h

#pragma once
//顺序表

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//结构的声明
//有两种顺寻标的声明,一种是静态的---以数组的形式
//另一种是动态的----动态内存开辟,第一种方式开辟出来的空间可能会不足,,也可能会浪费,所以我们采用动态内存开辟的方式

typedef int SeqListType;

typedef struct SeqList {
    
    
	SeqListType* list;
	int size;
	int capacity;
}SeqList;

//一个顺序表要实现的功能有:在任何一个位置插入一个元素,
//在任何一个位置删除一个元素,修改元素,查找元素


//函数的声明

//顺序表的初始化
void SeqListInit(SeqList* psl);

//尾插一个元素
void SeqListPushBack(SeqList* psl, SeqListType x);
//尾删一个元素
void SeqListPopBack(SeqList* psl);
//头插一个元素
void SeqListPushFront(SeqList* psl, SeqListType x);

//头删一个元素
void SeqListPopFront(SeqList* psl);

//任何位置的元素插入
void SeqListInsert(SeqList* psl, size_t pos, SeqListType x);
//任何位置的元素删除
void SeqListErase(SeqList* psl, size_t pos);
//检查容量
void SeqListCheckCapacity(SeqList* psl);

//销毁顺序表
void SeqListDeStroy(SeqList* pal);

//打印顺序表的内容
void SeqListPrint(SeqList* psl);

源文件SeqList.c

#define _CRT_SECURE_NO_WARNINGS 1
//函数实现
#include"SeqList.h"

//顺序表的初始化
void SeqListInit(SeqList* psl)
{
	assert(psl);
	psl->capacity = 0;
	psl->size = 0;
	psl->list = NULL;
}



//检查容量
void SeqListCheckCapacity(SeqList* psl)
{
	assert(psl);

	if (psl->size == psl->capacity)
	{


		int newcapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;

		SeqListType* tmp = (SeqListType*)realloc(psl->list, sizeof(SeqListType) * newcapacity);
		if (tmp == NULL)
		{
			printf("realloc:fail\n");
			exit(-1);
		}
		else
		{
			psl->list = tmp;
			psl->capacity = newcapacity;

		}

	}

}
//尾插一个元素
void SeqListPushBack(SeqList* psl, SeqListType x)
{
	assert(psl);


	SeqListCheckCapacity(psl);

	psl->list[psl->size] = x;
	psl->size++;

}


//尾删一个元素
void SeqListPopBack(SeqList* psl)
{
	assert(psl);

	if (psl->size > 0)
	{
		psl->size--;
	}

}


//头插一个元素
void SeqListPushFront(SeqList* psl, SeqListType x)
{
	assert(psl);

	SeqListCheckCapacity(psl);
	
	int begin = psl->size;
	while (begin > 0)
	{
		psl->list[begin] = psl->list[begin - 1];
		begin--;
	}
	psl->list[0] = x;
	psl->size++;
	

}

//头删一个元素
void SeqListPopFront(SeqList* psl)
{
	assert(psl);

	int begin = 1;
	while (begin < psl->size)
	{
		psl->list[begin-1] = psl->list[begin];
		begin++;
	}
	psl->size--;

}

//任何位置的元素插入
void SeqListInsert(SeqList* psl, size_t pos, SeqListType x)
{
	assert(psl);
	if (pos > psl->size)
	{

		printf("越界");

	}

	SeqListCheckCapacity(psl);
	
	
	
		size_t begin = psl->size;
		while (begin > pos)
		{
			psl->list[begin] = psl->list[begin - 1];
			begin--;
		}
		psl->list[pos] = x;
		psl->size++;
	
}

//任何位置的元素删除
void SeqListErase(SeqList* psl, size_t pos)
{

	assert(psl);
	assert(pos < psl->size);
	
		size_t begin = pos+1;
		while (begin < psl->size)
		{
			psl->list[begin-1] = psl->list[begin];
			begin++;
		}
		psl->size--;
	

}

//销毁顺序表
void SeqListDeStroy(SeqList* psl)
{
	assert(psl);

	free(psl->list);
	psl->capacity = 0;
	psl->size = 0;
	psl->list = NULL;
}

//打印顺序表的内容
void SeqListPrint(SeqList* psl)
{
	assert(psl);

	int i = 0;
	for (i = 0; i < psl->size; i++)
	{
		printf("%d ", psl->list[i]);
	}
	printf("\n");
}

源文件test.c

void menu()
{
    
    
	printf("********************************\n");
	printf("***  1.尾插      2.尾删      ***\n");
	printf("***  3.头插      4.头删      ***\n");
	printf("***  5.指定插入  6.指定删除  ***\n");
	printf("***  7.查看     -1.退出      ***\n");
	printf("********************************\n");
}


int main()
{
    
    

	SeqList data;
	InitSeqList(&data);
	int option = -1;
	do {
    
    
		menu();
		printf("请选择您想要进行的操作:>");
		scanf("%d", &option);
		SeqListType val = 0;
		size_t pos = 0;

		switch (option)
		{
    
    
		case 1:
		
			printf("请输入您想要插入的数据:>");
			scanf("%d", &val);
			SeqListPushBack(&data, val);
		

			break;
		case 2:

			SeqListPopBack(&data);
			break;
		case 3:
			printf("请输入您想要插入的数据:>");
			scanf("%d", &val);

			SeqListPushFront(&data, val);

			break;

		case 4:

			SeqListPopFront(&data);
			break;

		case 5:
			printf("请输入你想要插入的数据:>");
			scanf("%d", &val);
			printf("请输入你想要插入的位置:>");
			scanf("%d", &pos);
			SeqListInsert(&data, pos, val);

			break;

		case 6:
			printf("请输入您想要删除的位置:>");
			scanf("%d", &pos);
			SeqListErase(&data, pos);
			break;

		case 7:
	
			SeqListPrint(&data);
			break;
		case -1:
			printf("退出程序\n");
			return 0;
			break;

		default:
			printf("请重新选择\n");
			break;
		}


	} while (option);


	test2(&data);


	return 0;
}

小结

  • 在操作顺序表的时候需要清楚顺序表的这三个属性:
  1. 存储空间的起始位置
  2. 顺序表的最大容量
  3. 顺序表的当前长度
  • 我们可以通过利用顺序表的当前长度和起始位置来对顺序表实现添加元素和删除元素
    不过需要注意的是:每一次添加元素后表长会加1,每一次删除元素后表长会减1。

猜你喜欢

转载自blog.csdn.net/cainiaochufa2021/article/details/123469139