顺序表的概念和特点
顺序表是一种线性数据结构,它由一组数据元素构成,这些元素具有相同的特性,并按照一定的顺序排列。在顺序表中,数据元素通常存储在连续的内存空间中,这使得通过索引可以直接访问到表中的任意元素。
顺序表的特点如下:
-
连续的存储空间:顺序表中的元素在内存中占据一段连续的空间,这使得访问任何一个元素的时间复杂度都为O(1),即访问速度快。
-
动态性:顺序表可以是静态的,也可以是动态的。静态顺序表一旦初始化后,其空间大小就不能更改;而动态顺序表则可以根据需要动态地调整空间大小,即在空间不足时可以自动扩容,空间过多时可以缩容。
-
随机访问:由于元素存储是连续的,顺序表支持随机访问,即可以直接通过索引来访问元素,这一点与链表不同,链表需要遍历才能访问到指定位置的元素。
-
插入和删除操作:顺序表的插入和删除操作可能需要移动大量的元素,尤其是在插入或删除中间的元素时,时间复杂度为O(N),其中N是表中元素的数量。因此,顺序表在执行插入和删除操作时效率较低,这是它的一个缺点。
-
内存预分配与动态调整:在动态顺序表中,内存可以根据需要预先分配或动态调整。预分配指的是在创建顺序表时指定一个初始大小,后续根据实际情况可能进行扩容;动态调整则是在运行时根据需要增大或减小内存空间。
顺序表的实现
对比静态顺序表,动态顺序表更有优势,同样在项目中,动态顺序表的应用远远大于静态顺序表,下面我们就来实现动态顺序表。
首先创建三个文件:
- SeqList.h —— 用于声明函数的头文件
- SeqList.c —— 顺序表主要函数的实现
- test.c——测试顺序表。
创建顺序表
typedef int SLDataType;//方便后续使用更改类型
typedef struct SeqList
{
SLDataType* arr;//数组指针
int size;//记录当前有效的数组大小
int capacity;//记录当前总空间大小
}SL;
对顺序表初始化
void InitSeqList(SL* ps)//初始化顺序表
{
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
顺序表的销毁
void SLDestroy(SL* ps)//销毁顺序表
{
free(ps->arr);
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
打印顺序表
void SLPrint(const SL* ps)//打印顺序表的数据
{
assert(ps);
assert(ps->size);
for (int i = 0; i < ps->size; i++)
{
printf("%d", ps->arr[i]);
}
}
在给数据表增加数据前,我们先封装一个函数,用来判断顺序表是否已满,如果满则需要开辟新空间。
判断扩容
void SLCheckCapacity(SL* ps)//判断是否需要增容
{
assert(ps);
if (ps->capacity == ps->size)//数组已满
{
int newcapacity = (ps->capacity == 0) ? 4 : 2 * ps->capacity;//如果空间为0则开辟4个空间,否则开辟原空间二倍
SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity*sizeof(SLDataType));
if (tmp == NULL)
{
perror("realloc");
exit(1);
}
ps->arr = tmp;
ps->capacity = newcapacity;
}
}
头插
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++;
}
尾插
void SLPushBack(SL* ps, SLDataType x)//在顺序表的末尾插入数据
{
assert(ps);
SLCheckCapacity(ps);
ps->arr[ps->size] = x;
ps->size++;
}
指定位置插入
void SLInsert(SL* ps, int pos, SLDataType x)//在指定位置插入数据
{
assert(ps);
SLCheckCapacity(ps);
for (int i = ps->size; i > pos; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[pos] = x;
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];
}
ps->size--;
}
尾删
直接size–就可以了
void SLPopBack(SL* ps)//删除顺序表尾部的数据
{
assert(ps);
assert(ps->size);
ps->size--;
}
删除指定位置
void SLErase(SL* ps, int pos)//删除指定位置的数据
{
assert(ps);
assert(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)//在顺序表中查找数据,返回数组下标,没找到返回-1
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->arr[i] == x)
{
return i;//找到了,返回x在顺序表中的下标
}
}
return -1;//没找到
}
顺序表源码
SeqList.c
#pragma once
#include"SeqList.h"
void InitSeqList(SL* ps)//初始化顺序表
{
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
void SLDestroy(SL* ps)//销毁顺序表
{
free(ps->arr);
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
void SLPrint(const SL* ps)//打印顺序表的数据
{
assert(ps);
assert(ps->size);
for (int i = 0; i < ps->size; i++)
{
printf("%d", ps->arr[i]);
}
}
void SLCheckCapacity(SL* ps)//判断是否需要增容
{
assert(ps);
if (ps->capacity == ps->size)
{
int newcapacity = (ps->capacity == 0) ? 4 : 2 * ps->capacity;
SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity*sizeof(SLDataType));
if (tmp == NULL)
{
perror("realloc");
exit(1);
}
ps->arr = tmp;
ps->capacity = newcapacity;
}
}
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++;
}
void SLPushBack(SL* ps, SLDataType x)//在顺序表的末尾插入数据
{
assert(ps);
SLCheckCapacity(ps);
ps->arr[ps->size] = x;
ps->size++;
}
void SLInsert(SL* ps, int pos, SLDataType x)//在指定位置插入数据
{
assert(ps);
SLCheckCapacity(ps);
for (int i = ps->size; i > pos; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[pos] = x;
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];
}
ps->size--;
}
void SLPopBack(SL* ps)//删除顺序表尾部的数据
{
assert(ps);
assert(ps->size);
ps->size--;
}
void SLErase(SL* ps, int pos)//删除指定位置的数据
{
assert(ps);
assert(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)//在顺序表中查找数据,返回数组下标,没找到返回-1
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if (ps->arr[i] == x)
{
return i;//找到了,返回x在顺序表中的下标
}
}
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 InitSeqList(SL* ps);//初始化顺序表
void SLDestroy(SL* ps);//销毁顺序表
void SLPrint(const SL* ps);//打印顺序表的数据
void SLPushFront(SL* ps, SLDataType x);//在顺序表的头部插入数据
void SLPushBack(SL* ps, SLDataType x);//在顺序表的末尾插入数据
void SLInsert(SL* ps, int pos, SLDataType x);//在指定位置插入数据
void SLPopFront(SL* ps);//删除顺序表头部的数据
void SLPopBack(SL* ps);//删除顺序表尾部的数据
void SLErase(SL* ps, int pos);//删除指定位置的数据
int SLFind(SL* ps, SLDataType x);//在顺序表中查找数据,返回数组下标,没找到返回-1