【初阶数据结构】顺序表

本文详细介绍了顺序表的概念、结构,包括静态和动态顺序表的区别,以及它们的初始化、销毁、打印、增容(如尾插、头插、尾删、头删)和查找数据等操作的实现。
摘要由CSDN通过智能技术生成


请添加图片描述

线性表

  在学习顺序表之前,我们要先来了解一下线性表。线性表:是n个具有相同特性的数据元素的有限序列。它是一种在实际中广泛使用的数据结构,常见的线性表有:顺序表,链表,栈,队列,字符串等,后面我们会一一讲解学习。
  线性表具有以下特性:

  1. 逻辑结构上是线性结构,即连续的一条直线。
  2. 物理结构上不一定是连续的。

  例如:蔬菜分为了绿叶类、瓜类、菌菇类;线性表就是将具有部分相同特性的一类数据结构集合在一起。

顺序表的概念、结构以及分类

1.顺序表的概念

  顺序表指的是用一组地址连续的存储单元依次存储线性表的数据元素。 顺序表就是线性表中的一种。
  顺序表的底层结构是数组,对数组进行封装,实现了常用的增删查改等功能。

2.顺序表的结构

  由于顺序表的底层结构是数组,所以它在物理结构和逻辑结构上都是连续的

3.顺序表的分类

  顺序表一般可以分为两类:

  1. 静态顺序表:使用定长数组存储元素。
  2. 动态顺序表:按需申请动态内存存储元素。

4.静态顺序表

  静态顺序表的定义:

typedef int SLDataType;   方便用于更改数组中的数据类型
#define N 10;     方便用于修改数组长度
typedef struct SeqList
{
   SLDataType a[N];    定长数组
   int size;           有效数据个数
}SL;

定长数组的缺陷:
空间给少了不够用,容易造成数据丢失;给多了会造成空间浪费。

5.动态顺序表

  动态顺序表的定义:

typedef int SLDataType;
//动态顺序表
typedef struct SeqList
{
   SLDataType* arr;
   int size;         有效数据个数
   int capacity;     空间大小
}SL;

6.动态顺序表的实现

6.1初始化

  使用动态顺序表,可以较为简单的进行增删查改等操作,且不会存在过多的空间问题。在实现这些功能之前,我们首先要对顺序表进行初始化的操作,如下所示:

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

  注意:我们传的是结构体的地址,用一级指针来接收,这样才能对其中的数组进行增删查改。

6.2销毁

  我们是按需申请动态内存来存储元素,则需要及时销毁,释放空间且将其置为空指针,否则造成内存泄漏。
  销毁的代码如下:

void SLDestory(SL* ps)
{
  if(ps->arr)
     free(ps->arr);   如果数组arr不为空,则先释放这块空间
  ps->arr = NULL;
  ps->size = ps->capacity = 0;
}

6.3打印

  我们在进行增删查改操作后,想要知道我们写的代码是否正确,我们可以将其打印出来,方便查看其是否正确。
  打印的代码如下:

void SLPrint(SL* ps)
{
 assert(ps);     判断ps是否为空指针,若为空则直接跳出报错
 for(int i = 0; i < ps->size; i++)
    printf("%d ",ps->arr[i]);
 printf("\n");
}

6.4增容

  在增加数据即插入数据的过程中,可能会出现空间不够的情况,此时我们就需要将其扩容。
  增容代码如下:

void SLCheakCapacity(SL* ps)
{
   插入数据之前先看空间够不够
   if(ps->capacity == ps->size)  如果有效数据个数等于空间大小,则在插入数据时就没有空间给其进行插入,就要增容
   {
      int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;    用三目操作符判断空间大小是否为0,若为0,则先申请4个空间的大小,若不为0,则申请空间为原来的两倍
      SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));
      if(tmp == NULL)  为空则申请空间失败
      {
          perror("realloc fail");   输出realloc fail的错误信息
          exit(1);0值表示不正常退出函数
      }
      ps->arr = tmp;
      ps->capacity = newCapacity;
   }
}

6.5尾插

在这里插入图片描述

  由上图我们发现,我们要尾插的地方刚好就是下标为size的地方,且capacity空间大小恰好为7,够插入一个元素,插入后需要将size++,即有效数据个数加一。后面若想再插入元素,则需要进行增容操作才行,因此,在每次插入前要进行判断capacity大小是否足够。
  尾插代码如下:

void SLPushBack(SL* ps, SLDaType x)
{
   assert(ps);   判断传入的指针是否为空,为空则直接跳出
   SLCheakCapacity(ps);
   ps->arr[ps->size] = x;
   ps->size++;
}

ps->arr[ps->size] = x;
ps->size++;
可以写成:ps->arr[ps->size++] = x;
即将x插入下标为size的地方,后size++

  运行打印程序如下:
在这里插入图片描述
  由图我们分四次插入了5,6,7,8,结果正确。

6.6头插

在这里插入图片描述
  头插代码如下;

void SLPushFront(SL* ps, SLDataType x)
{
   assert(ps);
   SLCheakCapacity(ps);
   for(int i = ps->size; i > 0; i--)
   {
      ps->arr[i] = ps->arr[i - 1];
   }
   ps->arr[0] = x;
   ps->size++;
}

  在前面尾插的基础上再头插,代码运行结果如下:
在这里插入图片描述

6.7尾删

在这里插入图片描述
  尾删删完数据后,数据会少一个,因此尾删只需要将size–即可,如果删除的数据过多,会造成越界访问,因此需要判断size是否大于0。代码如下:

void SLPopBack(SL* ps)
{
   assert(ps);
   assert(ps->size > 0);  大于0为真则不会报错
   ps->size--;
}

  运行结果如下:
在这里插入图片描述

6.8头删

在这里插入图片描述

  头删时要将数据整体往前挪动一位,将第一位的数值覆盖掉即可。代码如下所示:

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[size-2] = arr[size -1];
   }
   ps->size--;
}

  运行结果如下:
在这里插入图片描述

6.9在指定位置插入数据

  我们给定一个位置pos,想要在下标为pos的位置插入一个数据。在插入数据前,按照惯例,先判断空间够不够。同样也需要判断pos这个节点是否有效,判断完成后,将pos及后面的数据整体往后挪动一位即可。
在这里插入图片描述
  代码如下所示:

void SLInsert(SL* ps, int pos, SLDataType x)
{
   assert(ps);
   assert(pos >= 0  && pos <= ps->size);
   SLCheakCapacity(ps);
   for(int i = ps->size; i > pos; i--)
   {
      ps->arr[i] = ps->arr[i - 1];
   }
   ps->arr[pos] = x;
   ps->size++;
}

  运行结果如下:
在这里插入图片描述

6.10删除指定位置的数据

  给定一个下标为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--;  
}

  运行结果如下:
在这里插入图片描述

6.11查找数据

  给定一个x,让你在顺序表中查找其是否存在。代码如下所示:

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; 
}

  运行结果如下:
在这里插入图片描述

全部代码

SeqList.h

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

typedef int SLDataType;
//动态顺序表
typedef struct SeqList
{
	SLDataType* arr;
	int size;  //有效数据个数
	int capacity;  //空间大小
}SL;


void SLInit(SL* ps);//初始化
void SLDestroy(SL* ps);//销毁

void SLCheakCapacity(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);//在下标为pos处插入数据

void SLErase(SL* ps, int pos);//删除下标为pos处的数据

int SLFind(SL* ps, SLDataType x);//查找数据

SeqList.c

#include"SeqList.h"

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

void SLDestroy(SL* ps)//销毁
{
	if (ps->arr)
	{
		free(ps->arr);
	}
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}

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

void SLCheakCapacity(SL* ps)//增容
{
	if (ps->capacity == ps->size)//有效数据个数等于空间大小时,插入数据是插不进去的,就要增容
	{
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//看原capacity是否有空间,如果没有,则先给四个字节的空间,如果有,则按原来空间的2倍增加
		SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));//要申请多大的空间
		if (tmp == NULL)//空间申请失败
		{
			perror("realloc fail");
			return;
		}
		ps->arr = tmp;//空间申请成功
		ps->capacity = newCapacity;
	}
}

void SLPushBack(SL* ps, SLDataType x)  //尾插
{
	assert(ps);
	SLCheakCapacity(ps);//检查空间是否足够
	ps->arr[ps->size++] = x;//将x插入下标为size的地方,后size++;
}

void SLPushFront(SL* ps, SLDataType x)//头插
{
	assert(ps);
	SLCheakCapacity(ps);
	for (int i = ps->size; i >0 ; i--)
	{
		ps->arr[i] = ps->arr[i - 1];//arr[1]=arr[0]
	}
	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];//arr[size-2]=arr[size-1]
	}
	ps->size--;
}

void SLInsert(SL* ps, int pos, SLDataType x)//在下标为pos处插入数据
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	SLCheakCapacity(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)  //删除下标为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, SLDataType x)//查找数据
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->arr[i] == x)
			return i;
	}
	return -1;
}

test.c

#include"SeqList.h"

void SLtest()
{
	SL sl;
	SLInit(&sl);  
	SLPushBack(&sl, 5);//尾插
	SLPushBack(&sl, 6);
	SLPushBack(&sl, 7);
	SLPushBack(&sl, 8);
	printf("尾插后:");
	SLPrint(sl);

	SLPushFront(&sl, 4);//头插
	SLPushFront(&sl, 3);
	SLPushFront(&sl, 2);
	SLPushFront(&sl, 1);
	printf("头插后:");
	SLPrint(sl);

	SLPopBack(&sl);//尾删
	SLPopBack(&sl);
	printf("尾删后:");
	SLPrint(sl);

	SLPopFront(&sl);//头删
	SLPopFront(&sl);
	printf("头删后:");
	SLPrint(sl);

	SLInsert(&sl, 1, 99);
	SLInsert(&sl, 3, 88);
	printf("指定位置插入后:");
	SLPrint(sl);

	SLErase(&sl, 1);
	SLErase(&sl, 2);
	printf("指定位置删除后:");
	SLPrint(sl);

	int find = SLFind(&sl, 4);
	printf("要找的数字为4\n");
	if (find < 0)
	{
		printf("没找到");
	}
	else
		printf("找到了,下标为:%d\n", find);
}

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

  今天的内容到此结束啦,感谢大家观看,如果大家喜欢,希望大家一键三连支持一下,如有表述不正确,也欢迎大家批评指正。

在这里插入图片描述

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值