目录
一,顺序表的概念
🌞什么是顺序表
顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系。
👍顺序表与数组的区别
上面说到顺序表是在计算机内存中以数组的形式保存的线性表,但是与C语言数组是有区别的。例如有一组数据【6 , 7 , 8】分别存入到顺序表和数组当中。
对于顺序表而言,数据的存储是连续的,相邻的。
对于数组而言,不要求在连续的空间上存储数据
二,顺序表详解
☘️顺序表结构
🖥️静态顺序表
知识点:静态顺序表
静态顺序表:使用定长数组存储
优点:操作简单,代码实现容易
缺点:定长数组很受限制,数组开小了不够用,开大了又浪费
🖥️动态顺序表
知识点:动态顺序表
动态顺序表:使用动态开辟的数组进行数据的存储
优点:数组可以根据自己的需求进行调解
缺点:代码过于麻烦
动态顺序表比静态使用范围更广,我们着重来讲述动态的
三,动态顺序表常见操作的实现
//顺序表的动态存储
typedef int SLDatatype;
typedef struct SeqList
{
SLDataType* Data; //定长数组
int size; //有效数据个数
int capacity; //空间容量的大小
}SeqList;
👏顺序表的初始化
//初始化链表
void SLInit(SeqList* ps)
{
assert(ps); //判空,如果传入的空指针,后面对它进行解引用就会报错
ps->data = NULL; //将data初始化为空指针
ps->capacity = ps->size = 0;
}
现在顺序表的初始化完成了,接下来就是顺序表的增删查改了。
在实现顺序表增加元素的函数功能前,首先我们先实现一个检查顺序表是否已经存满。如果已经存满了,那么我们就需要对它进行扩容。
👏容量判断
//容量判断
void Check_Capacity(SeqList* ps)
{
assert(ps);
if (ps->capacity == ps->size) //判断顺序表中有效数据个数是否已经达到容量大小
{
int new_capacity = ps->capacity == 0 ? 4 : (ps->capacity) * 2;
//如果容量为0的话,此时就是第一次向顺序表中添加元素,capacity就设为4
//如果容量不为0,此时就是有效数据个数达到容量,就进行扩容,新容量设置为原容量的2倍
SLDataType* tmp = (SLDataType*)realloc(ps->data, new_capacity * sizeof(SLDataType));
if (tmp == NULL) //如果扩容失败,就终止程序。
{
perror("ralloc fail");
exit(-1);
}
ps->data = tmp;
ps->capacity = new_capacity;
}
}
里扩容使用的是realloc函数,当我们进行第一次插入的时候,ps->data指针还是空指针,这时我们也可以使用realloc,realloc函数在使用的时候,如果传入的指针是空指针,它的作用就与malloc相同。
👏顺序表查找
//顺序表查找
int SL_Find(SeqList* ps, SLDataType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->data[i] == x)
{
//找到就返回下标
return i;
}
}
//找不到就返回-1
return -1;
}
👏顺序表尾插
//顺序表尾插
void SL_PushBack(SL* ps, SLDataType x)
{
assert(ps);
Check_Capacity(ps);
ps->data[ps->size] = x;
ps->size++;
}
👏顺序表尾删
//顺序表尾删
void SL_PopBack(SL* ps)
{
assert(ps);
assert(ps->size > 0); //如果顺序表中已经没有元素,那么就不用进行删除,所以
//这里需要检查顺序表中是否还有元素。
ps->size--;
}
注意:尾删时要检查顺序表中是否要有元素,下面的头删也是如此
👏顺序表头插
//顺序表头插
void SL_PushFront(SL* ps, SLDataType x)
{
assert(ps);
Check_Capacity(ps); //检查容量
//将所有数据向后移动一位
for (int i = ps->size - 1; i >= 0; i--)
{
ps->data[i + 1] = ps->data[i];
}
ps->data[0] = x;
ps->size++;
}
头插时将原有的元素向后移动一位,再将新的元素放入头部位置
👏顺序表头删
//顺序表头删
void SL_PopFront(SL* ps)
{
assert(ps);
//如果顺序表中只有一个数据,那么直接将数据个数-1
//如果对数据进行挪动,会造成越界
if (ps->size == 1)
{
ps->size--;
return;
}
//如果数据个数不为1,就将数据中第2个到最后一个都往前移动一位
for (int i = 1; i < ps->size; i++)
{
ps->data[i - 1] = ps->data[i];
}
ps->size--;
}
注意:挪动数据时防止数组的越界,顺序表只有一个元素时要考虑到位
👏顺序表指定位置插入
//顺序表指定位置插入
void SL_Insert(SeqList* ps, int pos, SLDataType x)
{
assert(ps);
//判定pos是否合法
assert(pos <= ps->size);
assert(pos >= 0);
Check_Capacity(ps); //检查容量是否够用
//将pos位置后的元素全部都向后移动一位
for (int i = ps->size-1; i >= pos; i--)
{
ps->data[i + 1] = ps->data[i];
}
ps->data[pos] = x;
ps->size++;
}
注意:插入位置要合法
👏顺序表指定位置删除
//顺序表指定位置删除
void SL_Erase(SeqList* ps, int pos)
{
assert(ps);
//检查pos位置是否合法
assert(pos >= 0);
assert(pos < ps->size);
//将pos位置后面的元素全都向前移动一位
for (int i = pos; i < ps->size; i++)
{
ps->data[i] = ps->data[i + 1];
}
ps->size--;
}
👏打印顺序表
//打印顺序表
void SLPrint(SeqList* ps)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->data[i]);
}
printf("\n");
}
👏销毁顺序表
//销毁顺序表
void SL_Destroy(SeqList* ps)
{
assert(ps);
free(ps->data);
ps->data = NULL;
ps->capacity = ps->size = 0;
}
四,顺序表完整代码
SeqList.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* data;
int size;
int capacity;
}SeqList;
//初始化顺序表
void SLInit(SeqList* ps);
//删除顺序表
void SL_Destroy(SeqList* ps);
//打印顺序表
void SLPrint(SeqList* ps);
//检查容量
void Check_Capacity(SeqList* ps);
//尾插尾删
void SL_PushBack(SeqList* ps,SLDataType x);
void SL_PopBack(SeqList* ps);
//头插头删
void SL_PushFront(SeqList* ps, SLDataType x);
void SL_PopFront(SeqList* ps);
//顺序表查找
int SL_Find(SeqList* ps, SLDataType x);
//顺序表指定位置插入
void SL_Insert(SeqList* ps, int pos, SLDataType x);
//顺序表指定位置删除
void SL_Erase(SeqList* ps, int pos);
SeqList.c
#include "SeqList.h"
void SLInit(SeqList* ps)
{
assert(ps);
ps->data = NULL;
ps->capacity = ps->size = 0;
}
void SL_Destroy(SeqList* ps)
{
assert(ps);
free(ps->data);
ps->data = NULL;
ps->capacity = ps->size = 0;
}
void SLPrint(SeqList* ps)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->data[i]);
}
printf("\n");
}
void Check_Capacity(SeqList* ps)
{
assert(ps);
if (ps->capacity == ps->size)
{
int new_capacity = ps->capacity == 0 ? 4 : (ps->capacity) * 2;
SLDataType* tmp = (SLDataType*)realloc(ps->data, new_capacity * sizeof(SLDataType));
if (tmp == NULL)
{
perror("ralloc fail");
exit(-1);
}
ps->data = tmp;
ps->capacity = new_capacity;
}
}
void SL_PushBack(SeqList* ps, SLDataType x)
{
//assert(ps);
//Check_Capacity(ps);
//ps->a[ps->size] = x;
//ps->size++;
SL_Insert(ps, ps->size, x);
}
void SL_PopBack(SeqList* ps)
{
//assert(ps);
//assert(ps->size > 0);
//ps->size--;
SL_Erase(ps, ps->size - 1);
}
void SL_PushFront(SeqList* ps, SLDataType x)
{
//assert(ps);
//Check_Capacity(ps);
//for (int i = ps->size - 1; i >= 0; i--)
//{
// ps->a[i + 1] = ps->a[i];
//}
//ps->a[0] = x;
//ps->size++;
SL_Insert(ps, 0, x);
}
void SL_PopFront(SeqList* ps)
{
//assert(ps);
//if (ps->size == 1)
//{
// ps->size--;
// return;
//}
//for (int i = 1; i < ps->size; i++)
//{
// ps->data[i - 1] = ps->data[i];
//}
//ps->size--;
SL_Erase(ps,0);
}
int SL_Find(SeqList* ps, SLDataType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->data[i] == x)
{
return i;
}
}
return -1;
}
void SL_Insert(SeqList* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos <= ps->size);
assert(pos >= 0);
Check_Capacity(ps);
for (int i = ps->size-1; i >= pos; i--)
{
ps->data[i + 1] = ps->data[i];
}
ps->data[pos] = x;
ps->size++;
}
void SL_Erase(SeqList* ps, int pos)
{
assert(ps);
assert(pos >= 0);
assert(pos < ps->size);
for (int i = pos; i < ps->size; i++)
{
ps->data[i] = ps->data[i + 1];
}
ps->size--;
}
以上就是顺序表基本操作的内容了,希望可以帮助到大家,如有不足的地方,评论区欢迎留言讨论