本篇博客给大家带来的是用C++语言来实现数据结构的顺序表
🐟🐟文章专栏:C++
🚀🚀若有问题评论区下讨论,我会及时回答
❤❤欢迎大家点赞、收藏、分享
你们的支持就是我创造的动力
今日思想:如果你不能承受孤独,就不要追逐梦想!
一、线性表
在说顺序表之前我们先聊聊线性表,为什么呢??举个例子:苹果和菠萝统称水果。而线性表就是就是顺序表和单链表等的统称。
线性表在逻辑结构(想象出来的数据在内存存储的地址是连续的)是线性的,物理结构(数据在内存存储的地址)不一定是线性的。
二、顺序表
顺序表是线性表的一种,它在逻辑结构是线性的,物理结构也是线性的。
顺序表的底层是数组,说到底跟数组差不多,只是加了一点东西罢了。
顺序表也分为静态顺序表(内存的大小固定)和动态顺序表(内存的大小可以变化)。
1、静态顺序表
静态顺序表是使用数组来存储数据,但是数组的大小固定了,如果存储用户的数据满了,无法及时调整数组的大小,导致丢失用户数据,给企业带来巨大的损失。
代码实例:
cpp
typedef int SLDatatype;//用户的数据的类型不一样,所以重命名方便程序员根据用户数据的类型来修改
#define M 20 //数组的大小固定
struct SqeList
{
SLDatatype arr[M];//使用数组来存储用户的数据
int size;//有效数据个数(有多少个用户的数据,不是每个内存都存储着用户的数据)
};
缺陷:空间给少了不够用,给多了浪费空间。
2、动态顺序表
实现动态实现我们要在VS上编辑三个文件(SeqList.h、SeqList.c、test.c)。
我们在头文件上声明动态顺序表的基本形式:
cpp
//SeqList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//顺序表的结构
typedef int SLDataType;
typedef struct SqeList
{
SLDataType* arr;
int size;//有效数据个数
int capacity;//空间大小
}SL;
那么怎么存储用户的数据呢??
在这里有6种存储用户数据的方法:尾插、头插、尾删、头删、指定位置之前插入数据、指定位置删除数据。
在实现这些方法之前我们要对这个进行结构体初始化。
①动态顺序表的初始化
代码实例:
cpp
//SeqList.h
//初始化
void SLInit(SL* ps);
cpp
//SqeList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
//初始化
void SLInit(SL* ps)
{
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
②尾插
无论是什么方法只要关于插入数据的都要坚查是否存满了数据,如果存满了那就在原来数组大小乘以2倍。
代码实例:
cpp
//SeqList.h
void SLcheckCapacity(SL* ps);
怎么判断用户的数据存满了呢??
如果有效数据个数等于空间大小,那么用户的数据存满了
满了之后我们要扩容
但是由于realloc有可能申请空间失败返回NULL,所以要用临时变量来接受一下,再来判断是否申请空间失败。
cpp
//SeqList.c
void SLcheckCapacity(SL* ps)
{
if (ps->size == ps->capacity)//空间满了
{
int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
ps->arr = tmp;
ps->capacity = newCapacity;
}
}
尾插的实现
代码实例:
cpp
//SeqList.h
void SLPushBack(SL* ps, SLDataType x);
图片解疑: 假设我们存储了6个用户数据,空间大小为8,那么尾插就是在size这个位置插入数据,然后size++就行。
cpp
//SeqList.c
//尾插
void SLPushBack(SL* ps, SLDataType x)
{
SLcheckCapacity(ps);
ps->arr[ps->size++] = x;
}
③头插
代码实例:
cpp
//SeqList.h
//头插
void SLPushFront(SL* ps, SLDataType x);
图片解疑:
头插就是在下标为0的位置插入数据,这时我们在令i=size,把 i 和 i-1的位置的数据用个循环来前面的数据整体移动,这时候下标为0的位置没有数据,再把数据插入进去就行,这时size要加一。
cpp
//SqeList.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;
}
④尾删
代码实例:
cpp
//SeqList.h
//尾删
void SLPopBack(SL* ps);
图片解疑:
尾删不是让下标为5的位置的数据置为0(假如用户存储的数据就是0),我们只要size--指向下标为5的数据就行了。用尾插例子,尾插这时就是在下标为5的位置插入数据就行(插入的数据会覆盖原来的数据)。
cpp
//SeqList.c
//尾删
void SLPopBack(SL* ps)
{
assert(ps->size);
--ps->size;
}
⑤头删
代码实例:
cpp
//SeqList.h
//头删
void SLPopFront(SL* ps);
图片解疑:
头删我们只要用个循环把下标为0的后面的数据整体移动覆盖下标为0的数据就行,然后size--。
cpp
//SeqList.c
//头删
void SLPopFront(SL* ps)
{
assert(ps && ps->size);
for (int i = 0; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
--ps->size;
}
⑥指定之前位置插入数据
代码实例:
cpp
//SeqList.h
//指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType x);
图片解疑:
指定位置之前插入数据是:举个例子我们要在下标为2的位置插入数据,那么我们用个循环把下标为2的数据和后面的数据整体移动,把下标为2的位置的数据空出来然后再在要插入的数据放到下标为2的空间上,然后size要加一。
cpp
//SqeList.c
//指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(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;
}
⑦指定位置删除数据
代码实例:
cpp
//SeqList.h
//指定位置删除数据
void SLErase(SL* ps, int pos);
图片解疑:
假设我们要把下标为3的数据删除掉,那么我们可以让i = 3,然后用个循环把i = 3的数据覆盖掉,然后size要减一。
cpp
//SeqList.c
//指定位置删除数据
void SLErase(SL* ps, int pos)
{
assert(ps && ps->size);
assert(pos >= 0 && pos <= ps->size);
for (int i = pos; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
--ps->size;
}
三、福利:
1、用户查找数据的方法
代码实例:
cpp
//SeqList.h
//查找
//找到了,就返回下标,未找到,返回-1;
int SLFind(SL* ps, SLDataType x);
遍历数组内容就行。
cpp
//SeqList.c
int SLFind(SL* ps, SLDataType x)
{
for (int i = 0; i < ps->size; i++)
{
if (ps->arr[i] == x)
{
return i;
}
}
//未找到
return -1;
}
2、动态顺序表的销毁
代码实例:
cpp
//SeqList.h
void SLDesTroy(SL* ps);
cpp
//SeqList.c
//销毁
void SLDesTroy(SL* ps)
{
if (ps->arr)
free(ps->arr);
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
完!!!