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