顺序表的构建和基本操作

目录

一.顺序表的概念和结构

线性表

二.顺序表和数组的区别

三.顺序表的分类

1.静态顺序表

2.动态顺序表

四.顺序表的实现

1,顺序表的初始化

2.顺序表的销毁

3.顺序表的扩容

4.顺序表的插入

4.1尾插

4.2头插

5.顺序表的打印

6.顺序表的删除

6.1头删

6.2尾删

测试插入删除打印

7.顺序表在指定位置插入数据

8.顺序表在指定位置删除

9.在顺序表中查找数据,返回下标

测试 

五.完整代码

SeqList.h

SeqList.c

 text.c

六.总结 


一.顺序表的概念和结构

线性表

线性表(linearlist)是n个具有相同特性的数据元素的有限序列。线性表是⼀种在实际中⼴泛使 ⽤的数据结构,常⻅的线性表:顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构,也就说是连续的⼀条直线。但是在物理结构上并不⼀定是连续的, 线性表在物理上存储时,通常以数组和链式结构的形式存储。 案例:蔬菜分为绿叶类、⽠类、菌菇类。线性表指的是具有部分相同特性的⼀类数据结构的集合

顺序表的特性:物理结构和逻辑结构是连续的

二.顺序表和数组的区别

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

三.顺序表的分类

1.静态顺序表

概念:使用定长数组存储元素

//静态顺序表

//定义顺序表的结构

typedef int SLDateType;//方便统一修改类型
#define N 100;
typedef struct SeqList {
	SLDateType a[N];
	int size;
}SL;

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

2.动态顺序表

//动态顺序表--按需申请
typedef int SLDateType;//方便统一修改类型

typedef struct SeqList
{
	SLDateType* arr;
	int size;//有效数据个数
	int capacity;//空间容量
}SL;

四.顺序表的实现

1,顺序表的初始化

头文件

//顺序表的初始化
void SLInit(SL* ps);

源文件

#include "SeqList.h"
void SLInit(SL* ps)
{
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}

测试

为了保证初始化的正确性,我们可以写一个简单的程序进行验证

#define _CRT_SECURE_NO_WARNINGS 1

#include "SeqList.h"

void SLText01()
{
	SL s1;
	SLInit(&s1);
}

int main()
{
	SLText01();
	return 0;
}

在SLText01中设置断点进行调式

通过调试我们可以发现顺序表已经成功进行了初始化,没有问题,可以进行下一步的操作.

2.顺序表的销毁

头文件

//顺序表的销毁
void SLDestroy(SL* ps);

源文件

//顺序表的销毁
void SLDestroy(SL* ps)
{
	//arr数组是否为空
	if (ps->arr)
	{
		//如果不为空,直接进行销毁
		free(ps->arr);
	}
	//同时,得重新让它指向NULL
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}

顺序表的销毁很简单,就不做调试验证了.

3.顺序表的扩容

因为我们初始化没有给数组开辟空间,所以为了后面增删改查的操作,这里需要给进行扩容

从数学上,扩容以2,到3倍最优

头文件

//顺序表的扩容
void SLCheckCapacity(SL* ps);

源文件

void SLCheckCapacity(SL* ps)
{
	//插入数据之前先看空间够不够
	if (ps->capacity == ps->size)
	{
		//申请空间
		//malloc callo realloc int arr[100]-->增容realloc
		//三目表达式
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SLDateType* tmp = (SLDateType*)realloc(ps->arr, newCapacity * 2 * sizeof(SLDateType));//要申请多大空间
		if (tmp == NULL)
		{
			perror("realloc fail!");
			exit(1);
		}
		//空间申请成功
		ps->arr = tmp;
		ps->capacity = newCapacity;
	}

这里首先我们先需要看空间够不够在进行扩容,判断条件为size==capacity(即数组有效个数是否为数组空间容量),然后判断我们的空间容量是否为0,如果是的话,初始赋值为4,然后用realloc函数进行动态开辟空间,注意开辟的是给定类型的空间.

4.顺序表的插入

4.1尾插

示意图

头文件

//顺序表的尾插
void SLPushBack(SL* ps, SLDateType x);

源文件

//顺序表的尾插
void SLPushBack(SL* ps, SLDateType x)
{
	assert(ps);
	//插入数据之前先看空间够不够
	SLCheckCapacity(ps);
	/*ps->arr[ps->size] = x;
	++ps->size;*/
	ps->arr[ps->size++] = x;
}

在插入之前先判断指针是否为空,在进行空间判断,最后在尾插,记得尾插之后size++

4.2头插

示意图

头文件

//顺序表的头插
void SLPushFront(SL* ps, SLDateType x);

源文件

//顺序表的头插
void SLPushFront(SL* ps, SLDateType 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++;
}

跟尾插步骤前面一样,不过头插需要把前面的数往后移动一位,空出首位,在进行赋值

5.顺序表的打印

头文件

//顺序表的打印
void SLPrint(SL s);

源文件

//顺序表的打印
void SLPrint(SL s)
{
	for (int i = 0; i < s.size; i++)
	{
		printf("%d ", s.arr[i]);

	}
	printf("\n");
}

6.顺序表的删除

6.1头删

示意图

头文件

//顺序表的头删
void SLPopFront(SL* ps);

源文件 

//顺序表的头删
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]; //arr[i-2] = arr[size-1]
	}
	ps->size--;
}

头删除了要判断ps是否为空外,还需要判断size,如果有效个数为空那就无法删除,最后size-- 

6.2尾删

示意图

头文件 

//顺序表尾删
void SLPopBack(SL* ps);

源文件

//顺序表尾删
void SLPopBack(SL* ps)
{
	assert(ps);
	assert(ps->size);
	//顺序表不为空
	--ps->size;
}

尾删这里只需让size--即可

测试插入删除打印

 代码

#include "SeqList.h"

	void SLText01()
{
	SL s1;
	SLInit(&s1);
	//增删查改操作
	//测试尾插
	SLPushBack(&s1, 1);
	SLPushBack(&s1, 2);
	SLPushBack(&s1, 3);
	SLPushBack(&s1, 4);
	SLPrint(s1);
	SLPushFront(&s1, 5);
	SLPushFront(&s1, 6);
	SLPushFront(&s1, 7);
	SLPrint(s1);
	SLPopBack(&s1);
	SLPopFront(&s1);
	SLPrint(s1);

	SLDestroy(&s1);
}

int main()
{
	SLText01();
	return 0;
}

从结果看我们目前的操作没有任何的问题,可以进入下一步的操作. 

7.顺序表在指定位置插入数据

示意图

头文件

//在指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDateType x);

源文件 

//在指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDateType x)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	//插入数据之前,看看空间够不够
	void SLCheckCapacity(SL * ps);
	//让pos之后的数往后移动
	for (int i = ps->size; i > pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[pos] = x;
	ps->size++;
}

首先应该判断pos是否在范围内,然后判断空间够不够,最后将pos之前的数往后移动,将空出来的位置赋值给x,size++

8.顺序表在指定位置删除

示意图

头文件

//删除指定位置的数据
void SLErase(SL* ps, int pos)

源文件 

//删除指定位置的数据
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);

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

9.在顺序表中查找数据,返回下标

头文件

//查找数据,返回下标
int SLFind(SL* ps, SLDateType x);

 源文件

//查找数据,返回下标
int SLFind(SL* ps, SLDateType x)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->arr[i] == x)
		{
			//找到啦
			return i;
		}
	}
	//没有找到
	return -1;
}

测试 

void SLText02()
{
	SL s1;
	SLInit(&s1);
	SLPushBack(&s1, 1);
	SLPushBack(&s1, 2);
	SLPushBack(&s1, 3);
	SLPushBack(&s1, 4);
	//测试在指定位置之前插入
	SLInsert(&s1, 2, 99);
	SLPrint(s1);
	//删除指定位置的数据
	SLErase(&s1, 0);
	SLPrint(s1);
	//顺序表的查找
	int find = SLFind(&s1, 3);
	if (find < 0)
	{
		printf("没有扎到\n");
	}
	else
	{
		printf("找到了!下表为:%d", find);
	}
}

int main()
{
	SLText02();
	return 0;
}

 测试结果没有问题.

以上就是所有顺序表的操作,当然你可以用一个菜单将所有操作打印出来,能是界面更加美观

五.完整代码

SeqList.h

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//静态顺序表

//定义顺序表的结构

//typedef int SLDateType;//方便统一修改类型
//#define N 100;
//typedef struct SeqList {
//	SLDateType a[N];
//	int size;
//}SL;

//动态顺序表--按需申请
typedef int SLDateType;//方便统一修改类型

typedef struct SeqList
{
	SLDateType* arr;
	int size;//有效数据个数
	int capacity;//空间容量
}SL;

//顺序表的初始化
void SLInit(SL* ps);

//顺序表的销毁
void SLDestroy(SL* ps);

//顺序表的扩容
void SLCheckCapacity(SL* ps);

//头部插⼊删除,尾部插⼊删除
void SLPushBack(SL* ps, SLDateType x);
void SLPushFront(SL* ps, SLDateType x);

void SLPopBack(SL* ps);
void SLPopFront(SL* ps);


//顺序表的打印
void SLPrint(SL s);

//指定位置之前插⼊删除数据

void SLInsert(SL * ps, int pos, SLDateType x);
void SLErase(SL * ps, int pos);
int SLFind(SL* ps, SLDateType x);

SeqList.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "SeqList.h"

//顺序表得初始化
void SLInit(SL* ps)
{
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}
//顺序表的销毁
void SLDestroy(SL* ps)
{
	//arr数组是否为空
	if (ps->arr)
	{
		//如果不为空,直接进行销毁
		free(ps->arr);
	}
	//同时,得重新让它指向NULL
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}
void SLCheckCapacity(SL* ps)
{
	//插入数据之前先看空间够不够
	if (ps->capacity == ps->size)
	{
		//申请空间
		//malloc callo realloc int arr[100]-->增容realloc
		//三目表达式
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SLDateType* tmp = (SLDateType*)realloc(ps->arr, newCapacity * 2 * sizeof(SLDateType));//要申请多大空间
		if (tmp == NULL)
		{
			perror("realloc fail!");
			exit(1);
		}
		//空间申请成功
		ps->arr = tmp;
		ps->capacity = newCapacity;
	}
}


//顺序表的尾插
void SLPushBack(SL* ps, SLDateType x)
{
	assert(ps);
	//插入数据之前先看空间够不够
	SLCheckCapacity(ps);
	/*ps->arr[ps->size] = x;
	++ps->size;*/
	ps->arr[ps->size++] = x;
}

//顺序表的头插
void SLPushFront(SL* ps, SLDateType 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 SLPrint(SL s)
{
	for (int i = 0; i < s.size; i++)
	{
		printf("%d ", s.arr[i]);

	}
	printf("\n");
}
//顺序表尾删
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]; //arr[i-2] = arr[size-1]
	}
	ps->size--;
}

//在指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDateType x)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	//插入数据之前,看看空间够不够
	void SLCheckCapacity(SL * ps);
	//让pos之后的数往后移动
	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);
	assert(pos >= 0 && pos < ps->size);

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

//查找数据,返回下标
int SLFind(SL* ps, SLDateType x)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)

 text.c

#define _CRT_SECURE_NO_WARNINGS 1


#include "SeqList.h"

	void SLText01()
{
	SL s1;
	SLInit(&s1);
	//增删查改操作
	//测试尾插
	SLPushBack(&s1, 1);
	SLPushBack(&s1, 2);
	SLPushBack(&s1, 3);
	SLPushBack(&s1, 4);
	SLPrint(s1);
	SLPushFront(&s1, 5);
	SLPushFront(&s1, 6);
	SLPushFront(&s1, 7);
	SLPrint(s1);
	SLPopBack(&s1);
	SLPopFront(&s1);
	SLPrint(s1);

	SLDestroy(&s1);
}

void SLText02()
{
	SL s1;
	SLInit(&s1);
	SLPushBack(&s1, 1);
	SLPushBack(&s1, 2);
	SLPushBack(&s1, 3);
	SLPushBack(&s1, 4);
	//测试在指定位置之前插入
	SLInsert(&s1, 2, 99);
	SLPrint(s1);
	//删除指定位置的数据
	SLErase(&s1, 0);
	SLPrint(s1);
	//顺序表的查找
	int find = SLFind(&s1, 3);
	if (find < 0)
	{
		printf("没有扎到\n");
	}
	else
	{
		printf("找到了!下表为:%d", find);
	}
}

int main()
{
	SLText02();
	return 0;
}

六.总结 

顺序表作为数据结构的第一课,难度并没有很大,重点在于理清它的结构和思路,如果你有什么问题的话可以打在评论区,大家一起互帮互助,然后是基于顺序表实现通讯录,我将会在近几天发布,想看的同学点赞关注不迷路哦!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值