数据结构——顺序表

1. 顺序表的结构和概念

1.1 线性表

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

  顺序表在逻辑结构上和物理结构上都是线性的

1.2 顺序表的结构

 顺序表的底层结构是数组,对数组的封装,实现了常用的增删改查等接口。  

顺序表又分为静态顺序表动态顺序表

1.2.1 静态顺序表

结构如下:

typedef int SLDataType;
#define N 10
typedef struct SeqList
{
	SLDataType arr[N];  //定长数组
	int size;           //有效元素个数
}SL;

静态顺序表缺陷:空间给少了不够用,给多了造成空间浪费。


1.2.2 动态顺序表

结构如下:

typedef int SLDataType;
typedef struct SeqList
{
	struct SeqList* p;
	int size;       //有效数据个数
	int capacity;   //空间容量
}SL;//按需申请

 注:

  • typedef int SLDataType;用typedef 对类型进行重命名,是为了方便以后修改代码。
  • 我们一般使用动态顺序表,因为它可以按我们需求申请空间      

 2. 动态顺序表的实现

2.1 动态顺序表的创建

typedef int SLDataType;
typedef struct SeqList
{
	struct SeqList* p;  //存储数据的底层结构
	int capacity;       //记录顺序表的空间大小
	int size;           //记录顺序表当前有效的数据个数
}SL;                    //按需申请

2.2 顺序表的初始化和打印

2.2.1 初始化

SeqList.h:

void SLInit(SL* ps);

 SeqList.c:

void SLInit(SL* ps)
{
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}

2.2.3 打印

SeqList.h:

void SLPrint(SL* ps);

 SeqList.c:

void SLPrint(SL* ps)
{
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");
}

2.3 扩容

当我们申请的空间不够时,我们需要对顺序表进行扩容。

扩容的方式有三种:

  1. 一次扩容一个空间:插入一个元素还不会造成空间浪费;
  2. 一次扩容固定个大小的空间(10/100...)
  3. 成倍数的增加(1.5倍、2倍)

 我们一般使用第三种方法,因为第一种方法程序执行效率低下;第二种方法申请空间太小造成频繁扩容,太大造成空间浪费。

SeqList.h:

void SLCheckCapacity(SL* ps);

 SeqList.c:

void SLCheckCapacity(SL* ps)
{
	int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
	SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));
	if (tmp == NULL)
	{
		perror("realloc fall!");
		exit(1);
	}
	ps->arr = tmp;
	ps->capacity = newcapacity;
}

2.4 顺序表的插入

2.4.1 尾插

SeqList.h:

void SLPushBack(SL* ps, SLDataType x);

SeqList.c:

void SLPushBack(SL* ps,SLDataType x)
{
	assert(ps);
	//空间不够,扩容
	SLCheckCapacity(ps);

	//空间足够,直接插入
	ps->arr[ps->size++] = x;
}
//头插

test.c:

void SLTest() {
	SL sl;
	SLInit(&sl);
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPushBack(&sl, 4);
	SLPrint(&sl);
	SLPushBack(&sl, 5);
	SLPrint(&sl);

}

测试结果如下:

2.4.2 头插

 SeqList.h:

void SLPushFront(SL* ps, SLDataType x);

SeqList.c:

//头插
void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	//判断是否扩容
	SLCheckCapacity(ps);
	//旧数据往后挪动一位
	for (int i = ps->size; i > 0; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[0] = x;
	ps->size++;
}

test.c:

void SLTest() {
	SL sl;
	SLInit(&sl);
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPushBack(&sl, 4);
	SLPrint(&sl);
	SLPushFront(&sl, 5);
	SLPushFront(&sl, 6);
	SLPushFront(&sl, 7);
	SLPushFront(&sl, 8);
	SLPrint(&sl);

}

测试结果如下:

 2.5 顺序表的删除

2.5.1 尾删

SeqList.h:

void SLPopBack(SL* ps);

SeqList.c:

void SLPopBack(SL* ps) {
	assert(ps);
	//判断顺序表是否为空
	assert(ps->size);
	//不为空
	ps->size--;
}

test.c:

void SLTest() {
	SL sl;
	SLInit(&sl);
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPushBack(&sl, 4);
	SLPrint(&sl);
	SLPopBack(&sl);
	SLPopBack(&sl);
	SLPopBack(&sl);
	SLPrint(&sl);

}

2.5.2 头删

SeqList.h:

void SLPopFront(SL* ps);

SeqList.c:

void SLPopFront(SL* ps)
{
	assert(ps);
	//判断顺序表是否为空
	assert(ps->size);
	for (int i = 0; i < ps->size-1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];

	}
	ps->size--;
}

test.c:

void SLTest() {
	SL sl;
	SLInit(&sl);
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPushBack(&sl, 4);
	SLPrint(&sl);
	SLPopFront(&sl);
	SLPopFront(&sl);
	SLPopFront(&sl);
	SLPrint(&sl);

}

 测试结果如下:

 2.6 在指定位置插入数据

 SeqList.h:

void SLInsert(SL* ps, int pos, SLDataType x)

SeqList.c:

void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps && (pos>=0&&pos <= ps->size));
	//判断是否扩容
	SLCheckCapacity(ps);
	for (int i = ps->size; i > pos; i--)
	{
		ps->arr[i ] = ps->arr[i - 1];
	}
	ps->arr[pos] = x;
	ps->size++;
}

test.c:

void SLTest() {
	SL sl;
	SLInit(&sl);
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPushBack(&sl, 4);
	SLPrint(&sl);
	SLInsert(&sl, 3, 100);
	SLPrint(&sl);

}

2.7 在指定位置删除

SeqList.h:

void SLErase(SL* ps, int pos)

SeqList.c:

void SLErase(SL* ps, int pos)
{
	assert(ps && (pos >= 0 && pos <= ps->size));
	//判断顺序表是否为空
	assert(ps->size);
	//不为空
	for (int i = pos; i < ps->size-1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];

	}
	ps->size--;
}

test.c:

void SLTest() {
	SL sl;
	SLInit(&sl);
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPushBack(&sl, 4);
	SLPrint(&sl);
	SLErase(&sl, 2);
	SLPrint(&sl);

}

测试结果如下:

注意:删除的是对应下标的的数据。 

 2.8 查找数据

SeqList.h:

int SLFind(SL* ps, SLDataType x);

SeqList.c:

int SLFind(SL* ps, SLDataType x)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->arr[i] == x)
		{
			return i;
		}
	}
	return -1;
}

test.c:

void SLTest() {
	SL sl;
	SLInit(&sl);
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPushBack(&sl, 4);
	int ret = SLFind(&sl, 3);
	if (ret < 0)
	{
		printf("数据不存在,查找失败!");
	}
	else
	{
		printf("数据找到了,在下标%d位置\n", ret);
	}
}

测试结果如下:

 2.9 顺序表的销毁

SeqList.h:

void SLDestory(SL* ps);

SeqList.c:

void SLDestory(SL* ps)
{
	assert(ps);
	if (ps->arr)
	{
		free(ps->arr);
	}
	ps->arr = NULL;
	ps->capacity = ps->size = 0;
}

test.c:

void SLTest() {
	SL sl;
	SLInit(&sl);
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPushBack(&sl, 4);
	SLDestory(&sl);
}

测试结果如下:

3. 顺序表的完整代码

SeqList.h:

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDataType;
typedef struct SeqList
{
	SLDataType* arr;
	int capacity;
	int size;
}SL;
//初始化和打印
void SLInit(SL* ps);
void SLPrint(SL* ps);
//扩容
void SLCheckCapacity(SL* ps);
//头插和尾插
void SLPushBack(SL* ps, SLDataType x);
void SLPushFront(SL* ps, SLDataType x);
//头删和尾删
void SLPopBack(SL* ps);
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 SLDestory(SL* ps);

SeqList.c:

#include "SeqList.h"
void SLInit(SL* ps)
{
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}
void SLPrint(SL* ps)
{
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");
}
//检查内存空间,扩容
void SLCheckCapacity(SL* ps)
{
	int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
	SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));
	if (tmp == NULL)
	{
		perror("realloc fall!");
		exit(1);
	}
	ps->arr = tmp;
	ps->capacity = newcapacity;
}
//尾插
void SLPushBack(SL* ps,SLDataType x)
{
	assert(ps);
	//空间不够,扩容
	SLCheckCapacity(ps);

	//空间足够,直接插入
	ps->arr[ps->size++] = x;
}
//头插
void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	//判断是否扩容
	SLCheckCapacity(ps);
	//旧数据往后挪动一位
	for (int i = ps->size; i > 0; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[0] = x;
	ps->size++;
}
//尾删
void SLPopBack(SL* ps) {
	assert(ps);
	//判断顺序表是否为空
	assert(ps->size);
	//不为空
	ps->size--;
}
//头删
void SLPopFront(SL* ps)
{
	assert(ps);
	//判断顺序表是否为空
	assert(ps->size);

	for (int i = 0; i < ps->size-1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];

	}
	ps->size--;
}
//在指定位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps && (pos>=0&&pos <= ps->size));
	//判断是否扩容
	SLCheckCapacity(ps);
	for (int i = ps->size; i > pos; i--)
	{
		ps->arr[i ] = ps->arr[i - 1];
	}
	ps->arr[pos] = x;
	ps->size++;
}
//在指定位置删除
void SLErase(SL* ps, int pos)
{
	assert(ps && (pos >= 0 && pos <= ps->size));
	//判断顺序表是否为空
	assert(ps->size);
	//不为空
	for (int i = pos; i < ps->size-1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];

	}
	ps->size--;
}
//查找数据

int SLFind(SL* ps, SLDataType x)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->arr[i] == x)
		{
			return i;
		}
	}
	return -1;
}
void SLDestory(SL* ps)
{
	assert(ps);
	if (ps->arr)
	{
		free(ps->arr);
	}
	ps->arr = NULL;
	ps->capacity = ps->size = 0;
}

 

本文为作者学习《顺序表》后的总结,如果有什么不恰当的地方,欢迎大佬指正!!!

  • 29
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值