【数据结构】--顺序表的详细实现

前言
数据结构 这里包含了博主很多的数据结构学习上的总结,每一篇都是超级用心编写的,有兴趣的伙伴们都支持一下。

顺序表的结构及概念

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

顺序表数据节点

顺序表可以分为两种结构:
1.静态顺序表:使用定长数组存储元素
在这里插入图片描述
2.动态顺序表:使用动态开辟的数组存储
在这里插入图片描述

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

3. 顺序表结构

typedef int SLDataType;
typedef struct SeqList
{
	SLDataType* a;//指向动态数组指针
	int size;//数据个数
	int capacity;//容量-空间大小
}SL;

接口实现

//顺序表实现增删查改

void SeqListInit(SL* ps);//顺序表初始化
 
void SLPrint(SL* ps);//打印数据
 
void SLCheckCapacity(SL* ps);//检查容量
 
//时间复杂度O(1)
void SLPushBack(SL* ps, SLDataType x);//尾插
void SLPopBack(SL* ps);//尾删
 
//时间复杂度O(n)
void SLPushFront(SL* ps, SLDataType x);//头插
void SLPopFront(SL* ps);//头删
 
void SLInsert(SL* ps, int pos, SLDataType x);//任意位置插
 
void SLErase(SL* ps, int pos);//任意位置删
 
int SLFind(SL* ps, SLDataType x);//查找
 
void SLModify(SL* ps, int pos, SLDataType x);//修改
 
void SLDestory(SL* ps);//销毁
顺序表初始化
void _SeqListInit(SL* ps) {
	assert(ps);
	ps->a = NULL;
	ps->capacity = ps->size = 0;
}

注意:这里传参的时候一定要传址,形参的改变不会影响实参的变化, 这里a,size,capacity都发生了变化,所以要传址

检查容量空间并扩容

当我们初始化好,或者后续我们将现有分配给我们的内存空间用完之后,我们肯定是要进行增容的,增容我们使用realloc函数就行了。

void SLCheckCapacity(SL* ps)
{
    assert(ps);
	//检查容量空间,满了就扩容
	if (ps->size == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);//结束程序
		}
		ps->a = tmp;
		ps->capacity = newCapacity;
		
	}
}
顺序表的尾插

尾插: 这里要注意尾插的时候要先检查容量够不够,再进行尾插。
这里的size是作为下一个数据的下标的。

void SLPushBack(SL* ps, SLDdataType x) {
	assert(ps);
	//扩容
	SLCheckCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;
}
顺序表的尾删

尾删的时候要有一个检查顺序表为不为空,有数据就删,没数据就不用删,我这里提供了两种检查方式,一个是比较温柔的检查,另一个是暴力的检查

void SLPopBack(SL* ps)
{
	assert(ps);
	 温柔检查
	if (ps->size == 0)
	{
		printf("SepList is empty\n");
		return;
	}
	// 暴力检查
	assert(ps->size>0);
	ps->size--;
}
顺序表的打印
void SLPrint(SL* ps)
{
    assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}
顺序表的头插

头插: 这里挪动数据要从后往前挪,你从前往后挪的话会造成数据被覆盖了,就出错了。
不要忘了检查容量哦。

   void SLPushFront(SL* ps, SLDataType x)
{
    assert(ps);
	SLCheckCapacity(ps);
	//挪动数据
	int end=ps->size-1;
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	ps->a[0] = x;
	ps->size++;
}
顺序表的头删

和头插刚好相反,这里是从前往后挪将数据覆盖,并且也要进行检查,有数据就删,没数据了就不要删。

void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size > 0);
	int begin = 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}
任意位置的插入

**任意位置插:**这里的pos要进行断言,原因是你插入的数据必须是紧挨着的,要在当前数据范围内插入。
检查容量大小也是必不可少的。插入数据还是从后往前挪。

void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	SLCheckCapacity(ps);
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[pos] = x;
	ps->size++;
}

实现了这个函数,那前面的头插和尾插直接就可以调用这个函数就行了。

尾插:

void SLPushBack(SL* ps, SLDataType x)
{
	SLInsert(ps, ps->size, x);
}

头插:

void SLPushFront(SL* ps, SLDataType x)
{
	SLInsert(ps, 0, x);
}
任意位置的删除

这里就不用再写对顺序表为空的检查了,因为检查pos的时候顺便检查了为空的情况。挪动数据的时候也是从前往后挪,我这里写了两种写法。挪动数据时要注意越界的问题。

void SLErase(SL* ps, int pos)//任意位置删
{
    assert(ps);
    assert(pos >= 0 && pos < ps->size);//不用等于size
 
    /*int begin = pos;
    while (begin < ps->size - 1)
    {
        ps->a[begin] = ps->a[begin + 1];
        begin++;
    }
    ps->size--;*/
}

当然头删和尾删也可以直接调用这个函数

头删:

void SLPopFront(SL* ps)
{
	SLErase(ps, 0);
}

尾删:

void SLPopBack(SL* ps)
{
	SLErase(ps, ps->size - 1);
}
顺序表的查找

查找和修改是配合起来一起使用的。
查找:

int SLFind(SL* ps, SLDataType x)//查找
{
	assert(ps);
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		if (ps->a[i] == x)
		{
			return i;//返回下标
		}
	}
	return -1;
}

修改:

void SLModify(SL* ps, int pos, SLDataType x)//修改
{
	assert(ps);
	assert(pos > 0 && pos < ps->size);
 
	ps->a[pos] = x;
}
顺序表的销毁
void SLDestory(SL* ps)//销毁
{
	assert(ps);
	if (ps->a)
	{
		free(ps->a);
		ps->a = NULL;
		ps->capacity = ps->size = 0;
	}
}

源码

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

SeqList.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
 
typedef int SLDataType;
 
typedef struct SeqList
{
	SLDataType* a;//指向动态数组指针
	int size;//数据个数
	int capacity;//容量-空间大小
}SL;
 
//顺序表实现增删查改
 
void SeqListInit(SL* ps);//顺序表初始化
 
void SLPrint(SL* ps);//打印数据
 
void SLCheckCapacity(SL* ps);//检查容量
 
//时间复杂度O(1)
void SLPushBack(SL* ps, SLDataType x);//尾插
void SLPopBack(SL* ps);//尾删
 
//时间复杂度O(n)
void SLPushFront(SL* ps, SLDataType x);//头插
void SLPopFront(SL* ps);//头删
 
void SLInsert(SL* ps, int pos, SLDataType x);//任意位置插
 
void SLErase(SL* ps, int pos);//任意位置删
 
int SLFind(SL* ps, SLDataType x);//查找
 
void SLModify(SL* ps, int pos, SLDataType x);//修改
 
void SLDestory(SL* ps);//销毁

SeqList.c

#include "SeqList.h"
void SeqListInit(SL* ps)
{
	ps->a = NULL;
	ps->capacity = ps->size = 0;
}
 
void SLPrint(SL* ps)
{
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}
 
void SLCheckCapacity(SL* ps)
{
	//检查容量空间,满了就扩容
	if (ps->size == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);//结束程序
		}
		ps->a = tmp;
		ps->capacity = newCapacity;
		//printf("扩容成功!\n");
	}
}
 
void SLErase(SL* ps, int pos)//任意位置删
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);//不用等于size
 
	/*int begin = pos;
	while (begin < ps->size - 1)
	{
		ps->a[begin] = ps->a[begin + 1];
		begin++;
	}
	ps->size--;*/
 
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}
 
void SLInsert(SL* ps, int pos, SLDataType x)//任意位置插
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	SLCheckCapacity(ps);
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[pos] = x;
	ps->size++;
}
 
//尾插
void SLPushBack(SL* ps, SLDataType x)
{
	//1
	/*SLCheckCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;*/
	//2
	//直接调用
	SLInsert(ps, ps->size, x);
}
 
//尾删
void SLPopBack(SL* ps)
{
	//assert(ps);
	//ps->a[ps->size - 1] = 0;
	// 温柔检查
	//if (ps->size == 0)
	//{
	//	printf("SepList is empty\n");
	//	//exit(-1);
	//	return;
	//}
	// 暴力检查
	/*assert(ps->size>0);
	ps->size--;*/
	SLErase(ps, ps->size - 1);
}
 
//头插
void SLPushFront(SL* ps, SLDataType x)
{
	//SLCheckCapacity(ps);
	挪动数据
	//int end=ps->size-1;
	//while (end >= 0)
	//{
	//	ps->a[end + 1] = ps->a[end];
	//	--end;
	//}
	//ps->a[0] = x;
	//ps->size++;
	SLInsert(ps, 0, x);
}
 
//头删
void SLPopFront(SL* ps)
{
	/*assert(ps);
	assert(ps->size > 0);
	int begin = 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;*/
	SLErase(ps, 0);
}
 
 
int SLFind(SL* ps, SLDataType x)//查找
{
	assert(ps);
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		if (ps->a[i] == x)
		{
			return i;//返回下标
		}
	}
	return -1;
}
 
void SLModify(SL* ps, int pos, SLDataType x)//修改
{
	assert(ps);
	assert(pos > 0 && pos < ps->size);
 
	ps->a[pos] = x;
}
 
void SLDestory(SL* ps)//销毁
{
	assert(ps);
	if (ps->a)
	{
		free(ps->a);
		ps->a = NULL;
		ps->capacity = ps->size = 0;
	}
}

test.c

#include "SeqList.h"
void menu()
{
	printf("******************************\n");
	printf("*****  1.尾插    2.头插  *****\n");
	printf("*****  3.尾删    4.头删  *****\n");
	printf("*****  5.删除    6.插入  *****\n");
	printf("*****  7.修改    8.查找  *****\n");
	printf("*****  9.打印    0.退出  *****\n");
	printf("******************************\n");
}
int main()
{
	SL sl;
	SeqListInit(&sl);
	SLPushFront(&sl, 1);
	SLPushFront(&sl, 2);
	SLPushFront(&sl, 3);
	SLPushFront(&sl, 2);
	SLPushFront(&sl, 2);
	SLPushFront(&sl, 4);
	SLPushFront(&sl, 5);
	int option = 0;
	do
	{
		menu();
		scanf("%d", &option);
		
		int x, y, z;
		int val, pos;
		switch (option)
		{
		case 1:
			printf("请连续输入你要尾插的数据,以-1结束:>");
			scanf("%d", &val);
			while (val != -1)
			{
				SLPushBack(&sl, val);
				scanf("%d", &val);
			}
			break;
		case 2:
			printf("请输入要头插的数据:>");
			scanf("%d", &val);
			SLPushFront(&sl, val);
			break;
		case 3:
			SLPopBack(&sl);
			printf("尾删成功\n");
			break;
		case 4:
			SLPopFront(&sl);
			printf("头删成功\n");
			break;
		case 5:
			printf("请输入你要删除的值:");
			scanf("%d", &x);
			int pos = SLFind(&sl, x);
			while (pos != -1)
			{
				SLErase(&sl, pos);
				pos = SLFind(&sl, x);
			}
			printf("删除成功\n");
			break;
		case 6:
			printf("请连续输入你要插入的数据和位置:>");
			scanf("%d%d", &val,&pos);
			SLInsert(&sl, pos, val);
			SLPrint(&sl);
			break;
		case 7:
			printf("请输入你要修改的值和修改后的值:");
			scanf("%d%d", &y, &z);
			pos = SLFind(&sl, y);
			if (pos != -1)
			{
				SLModify(&sl, pos, z);
			}
			else
			{
				printf("没找到:%d\n", y);
			}
			break;
		case 8:
			printf("请输入你要查找的值:");
			scanf("%d", &y);
			pos = SLFind(&sl, y);
			if (pos == -1)
			{
				printf("找不到\n");
			}
			else
			{
				printf("%d\n", pos);
			}
			break;
		case 9:
			SLPrint(&sl);
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (option != 0);
	SLDestory(&sl);
	return 0;
}

尾声
看到这里,相信大家对堆这个数据结构有了了解了。
如果你感觉这篇博客对你有帮助,不要忘了一键三连哦
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值