数据结构——C语言实现顺序表
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构。一般情况下顺序表是以数组为基础,结构体为框架来实现的。数组存储。在数组上完成数据的增删查改,而结构体中包含了存储数据的数组、所存储数据的实际长度以及顺序表的容量。
顺序表一般可分为:
1.静态顺序表:使用定长数组存储
#define N 100
typedef int SLDataType;
typedef struct SeqList
{
SLDataType array[N]; // 定长数组
size_t size; // 有效数据的个数
}SeqList;
- 动态顺序表:使用动态开辟的数组存储
typedef struct SeqList
{
SLDataType* array; // 指向动态开辟的数组
size_t size ; // 有效数据个数
size_t capicity ; // 容量空间的大小
}SeqList;
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们以实现动态顺序表为例进行描述:
其动态顺序表的定义以及函数接口的声明如下:
typedef struct SeqList
{
SLDataType* array;
size_t size;
size_t capacity;
}SeqList;
void SeqListInit(SeqList* psl);//初始化顺序表
void SeqListDestory(SeqList *psl);//摧毁顺序表
void SeqListCheckCapacity(SeqList *psl);//检查容量
void SeqListPushBack(SeqList *psl, SLDataType x);//尾插
void SeqListPopBack(SeqList *psl);//尾删
void SeqListPushFront(SeqList *psl, SLDataType x);//头插
void SeqListPopFront(SeqList *psl);//头删
void SeqListInsert(SeqList *psl, size_t pos, SLDataType x);//向第pos位插入数据
void SeqListRErase(SeqList *psl, size_t pos);//删除第pos位数据
int SeqListFind(SeqList *psl, SLDataType x);//查找数据
void SeqListMdfidy(SeqList *psl, size_t pos, SLDataType x);//修改第pos位的元素
void SeqListPrint(SeqList *psl);//打印顺序表
size_t SeqListSize(SeqList *psl);//顺序表的长度
void SeqListRemoveAll(SeqList *psl, SLDataType x);//按内容全部删除
void SeqListBubbleSort(SeqList *psl);//冒泡法对顺序表排序
int SeqListBinaryFind(SeqList *psl, SLDataType x);//顺序表二分查找
函数实现以及功能解释将在以下代码体现:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdlib.h>
#include"SeqList.h"
#include<assert.h>
#include<malloc.h>
//初始化
void SeqListInit(SeqList* psl)
{
assert(psl);//检验sql是否为空
psl->array = NULL;//将前地址置为空
psl->size = 0;//顺序表当前长度为0
psl->capacity = 0;//顺序表当前容量为0
}
//摧毁顺序表
void SeqListDestory(SeqList *psl)
{
assert(psl);//检验psl是否为空
if (psl->array)
{
free(psl->array);//释放psl空间
psl->array = NULL;//将前地址置为空
psl->capacity = 0;//将其容量置为0
psl->size = 0;//将其实际长度置为0
}
}
//检查容量:此处可以用malloc进行内存空间申请,亦可用realloc进行申请空间,不过切记两者都必须在申请后对空间是否申请成功进行判断以及malloc申请后使用完毕的释放空间。
void CheckCapacity(SeqList* psl)
{
assert(psl);
//检查容量
if (psl->size == psl->capacity)
{
//建立新容量
size_t newcapacity = psl->capacity == 0 ? 5 : 2 * psl->capacity;
//分配空间
psl->array = (SLDataType*)realloc(psl->array, newcapacity * sizeof(SLDataType));
//检查是否分配成功
assert(psl->array);
//更新容量
psl->capacity = newcapacity;
}
}
//尾插
void SeqListPushBack(SeqList* psl, SLDataType x)
{
assert(psl);//检验sql是否为空
SeqListCheckCapacity(psl);
psl->array[psl->size] = x;//为尾部插入需要插入的值
psl->size++;//顺序表当前长度加一
}
//尾删
void SeqListPopBack(SeqList *psl)
{
assert(psl);//检验sql是否为空
if (psl->size == 0)//判断顺序表是否为空
{
printf("SeqList has been empty\n");
return;
}
psl->size--;//顺序表不为空时,顺序表长度减一
}
//头插
void SeqListPushFront(SeqList *psl, SLDataType x)
{
size_t end = 0;
assert(psl);//检验sql是否为空
SeqListCheckCapacity(psl);//检查容量
for (end = psl->size; end > 0; end--)
{
psl->array[end] = psl->array[end - 1];//将当前顺序表的数据向后移一位
}
psl->array[0] = x;//为头部插入要插入的值
psl->size++;//当前顺序表长度加1
}
//头删
void SeqListPopFront(SeqList *psl)
{
size_t start = 0;
assert(psl);//检验psl是否为空
if (psl->size == 0)//判断顺序表是否为空
{
printf("SeqList has been empty!\n");
return;
}
for (start = 0; start < psl->size - 1; start++)
{
psl->array[start] = psl->array[start + 1];//将当前顺序表的数据向后移一位
}
psl->size--;//当前顺序表长度减一
}
//第pos位插入数据
void SeqListInsert(SeqList *psl, size_t pos, SLDataType x)
{
//pos==0-->头插
//pso==szie-->尾插
size_t end = 0;
assert(psl && (pos >= 0) && (pos <= psl->size));//检查psl是否为空并判断插入位置的合法性
SeqListCheckCapacity(psl);
for (end = psl->size; end > pos; end--)
{
psl->array[end] = psl->array[end - 1];//将当前顺序表pos位以后的数据向后移一位
}
psl->array[pos] = x;//将第pos位更新为x
psl->size++;//当前顺序表长度加1
}
//删除第pos位数据
void SeqListErase(SeqList *psl, size_t pos)
{
size_t start = 0;
assert(psl && (pos >= 0) && (pos <= psl->size));//检查psl是否为空并判断插入位置的合法性
if (psl->size == 0)//判断顺序表是否为空
{
printf("SeqList has been empty\n");
return;
}
for (start = pos; start < psl->size - 1; start++)
{
psl->array[start] = psl->array[start + 1];//将第pos位后的数据向前移一位
}
psl->size--;//顺序表当前长度减一
}
//查找数据
int SeqListFind(SeqList *psl, SLDataType x)
{
size_t i = 0;
assert(psl);//检验psl是否为空
if (psl->size == 0)//判断顺序表是否为空
{
printf("SeqList has been empty\n");
return -1;
}
for (i = 0; i < psl->size; i++)
{
if (psl->array[i] == x)//遍历顺序表查找数据
{
return i;//找到返回下标
}
}
return -1;//未找到返回-1
}
//修改第pos位数据
void SeqListModify(SeqList *psl, size_t pos, SLDataType x)
{
assert(psl && (pos >= 0) && (pos <= psl->size));//检查psl是否为空并判断修改位置的合法性
if (psl->size == 0)//判断顺序表是否为空
{
printf("SeqList has been empty\n");
return;
}
psl->array[pos] = x;//修改pos位的数据
}
//打印顺序表
void SeqListPrint(SeqList *psl)
{
size_t i = 0;
assert(psl);//检查psl是否为空
for (i = 0; i < psl->size; i++)
{
printf("%d ", psl->array[i]);//打印
}
printf("\n");
}
//顺序表的长度
size_t SeqListSize(SeqList *psl)
{
assert(psl);//检查psl是否为空
return psl->size;//返回长度
}
//按内容全部删除
void SeqListRemoveAll(SeqList *psl, SLDataType x)
{
size_t i = 0;
size_t index = 0;
assert(psl);//检验psl是否为空
if (psl->size == 0)//判断顺序表是否为空
{
printf("SeqList has been empty\n");
return;
}
for (i = 0; i < psl->size; i++)
{
//两种思维实现删除操作
//惯性思维
//if (psl->array[i] == x)//找到数据
//{
//psl->size--;//当前长度减一
// for (index=i; index < psl->size ; index++)
// {
// psl->array[index] = psl->array[index + 1];//删除数据,将该数据后的数据向前移一位
// }
// return;
//}
//逆向思维:不相等时保存,相等时跳过
if (psl->array[i] != x)//当前数据不等于x时
{
psl->array[index] = psl->array[i];//将其保存
index++;
}
}
if (index < psl->size)//新长度小于原长度时,删除成功
{
psl->size = index;//将新长度赋给顺序表的长度
return;
}
printf("SeqList does not the data\n");//未找到该数据
}
//冒泡法对顺序表排序
void SeqListBubbleSort(SeqList *psl)
{
assert(psl);
//升序
size_t end = psl->size;
while (end > 1)
{
//单趟冒泡排序
int flag = 0;
for (size_t i = 0; i < end; i++)
{
if (psl->array[i - 1]>psl->array[i])
{
flag = 1;
//交换
(psl->array[i - 1]) ^= (psl->array[i]);
(psl->array[i]) ^= (psl->array[i - 1]);
(psl->array[i - 1]) ^= (psl->array[i]);
}
}
if (flag == 0)
{
break;
}
--end;
}
}
//顺序表二分查找:必须在顺序表已排好序的前提下进行查找,否则查找是无效的。
int SeqListBinaryFind(SeqList *psl, SLDataType x)
{
assert(psl);
if (psl->size == 0)
{
printf("SeqList has been empt\n");
return -1;
}
size_t start = 0;
size_t end = psl->size - 1;
while (start <= end)
{
size_t mid = start+(end-start) / 2;
if (psl->array[mid] == x)
{
return mid;
}
else if (psl->array[mid] > x)
{
end = mid - 1;
}
else
{
start = mid + 1;
}
}
return -1;
}
在实现顺序表接口函数时需要注意,函数体中一定要对接口函数参数的有效性、顺序表的容量以及顺序表是否为空进行判断与检查,防止产生溢出等错误。在进行循环遍历时,切记对边界值的判断,否则极易造成边界的超出或遗漏进而导致内存空间的溢出。