文章目录
1、顺序表介绍
顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,采用顺序存储结构的线性表通常称为顺序表。顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中。
顺序表的优点:随机访问,尾插、尾删快;
顺序表的缺点:增容的代价(效率低、浪费空间、头/中的插入和删除效率低)。
2、顺序表结构体、常用函数接口
typedef int DataType;
typedef struct SeqList
{
DataType* _array; // 指向数组的指针
size_t _size; // 大小
size_t _capacity; // 容量
}SeqList;
顺序表的结构体包括三个成员,分别是顺序表数组,顺序表大小,顺序表容量(要求动态分配)。
顺序表的函数接口:
void SeqListInit(SeqList* ps);// 初始化
void SeqListDestory(SeqList* ps);// 销毁
void SeqLiistCheckCapacity(SeqList* ps);// 检查capacity容量,不够就增容
void SeqListPushBack(SeqList* ps, DataType x);// 尾插
void SeqListPushFront(SeqList* ps, DataType x);// 头插
void SeqListPopBack(SeqList* ps);// 尾删
void SeqListPopFront(SeqList* ps);// 头删
void SeqListInsert(SeqList* ps, size_t pos, DataType x);// 插入
void SeqListErase(SeqList* ps, size_t pos);// 删除指定位置的结点
size_t SeqListSize(SeqList* ps);// 返回数据
size_t SeqListFind(SeqList* ps, DataType x);// 找值x的位置,返回下标
DataType SeqListAt(SeqList* ps, size_t pos);// 返回pos位置的值
void SeqListRemove(SeqList* ps, DataType x); // 删除指定值的结点
void SeqListModify(SeqList* ps, size_t pos, DataType x); // 删除指定位置的指定值
void SeqListPrint(SeqList* ps); // 打印
void SeqListBubbleSort(SeqList* ps);// 冒泡排序
int SeqListBinaryFind(SeqList* ps, DataType x); // 二分查找 时间复杂度O(N),空间复杂度O(1)
void SeqListRemoveAll(SeqList* ps, DataType x);// 已知值的全部结点删除
3、顺序表接口的实现
3.1 初始化
对结构体中的三个成员进行初始化,指针赋值为NULL,顺序表大小和容量初值为0。
void SeqListInit(SeqList* ps) // 初始化
{
assert(ps); // 断言 空指针报错
ps->_array = NULL;
ps->_capacity = 0;
ps->_size = 0;
}
3.2 销毁
当数组中不为空时,对数组进行销毁。首先释放掉数组的申请空间,将指向数组的指针置空,防止野指针。对顺序表的大小和容量赋值0。
void SeqListDestory(SeqList* ps) // 销毁
{
assert(ps);
if (ps->_array)
{
free(ps->_array);
ps->_array = NULL;
ps->_capacity = 0;
ps->_size = 0;
}
}
3.3 检查capacity容量,不够就增容
void SeqListCheckCapacity(SeqList* ps) // 检查capacity的容量,不够就增容
{
assert(ps);
if (ps->_capacity == ps->_size)
{
size_t newcapacity = (ps->_capacity == 0) ? 4 : ps->_capacity * 2;
ps->_array = realloc(ps->_array, sizeof(DataType) * newcapacity);
// realloc第一个参数为空,相当于malloc
ps->_capaccity = newcapacity;
}
}
3.4 尾插
先检查数组的容量够不够,然后在最后一个位置加入x,相应的数组大小自增1。
void SeqListPushBack(SeqList* ps, DataType x) // 尾插
{
assert(ps);
SeqListCheckCapacity(ps); // 检查capacity容量,不够就增容
ps->_array[ps->_size] = x;
ps->_size++;
}
3.5 头插
整体遍历,把所有元素都往后移一个位置,空出第一个位置,放入x,数组大小加1。
void SeqListPushFront(SeqList* ps, DataType x) // 头插
{
assert(ps);
SeqListCheckCapacity(ps); // 检查capacity的容量
size_t end = ps->_size; // size从0开始
while (end > 0) // end==0 时,循环停止,end不会成为-1
{
ps->_array[end] = ps->_array[end - 1];
--end;
} // 此时,所有元素都已经向后移一位
ps->_array[0] = x; // 放入值x
ps->_size++;
}
3.6 尾删
直接把数组大小调小一位,最后一位舍弃即可。
void SeqListPopBack(SeqList* ps) // 尾删
{
assert(ps && ps->_size > 0);
--ps->_size;
}
3.7 头删
整体遍历,把所有元素都往前移一个位置,覆盖掉第一个位置,数组大小减1。
void SeqListPopFront(SeqList* ps) // 头删
{
assert(ps);
size_t start = 0;
while (start < ps->_size - 1)
{
ps->_array[start] = ps->_array[start + 1];
++start;
}
--ps->_size;
}
3.8 插入
首先检查容量,定位pos的位置时,注意越界和死循环问题,找到目的下标后,直接插入x即可,最后别忘了要将数组大小加1。
void SeqListInsert(SeqList* ps, size_t pos, DataType x) // 插入
{
assert(ps && pos <= ps->_size); // pos是无符号的,大于0
SeqListCheckCapacity(ps); // 检查capacity容量,不够就增容
size_t end = ps->_size - 1; // 定义下标时,注意越界,死循环问题
while (end > pos) // 当pos是 0 时,end是整型最大值,死循环
{
ps->_array[end] = ps->_array[end - 1]; // 用减不用加
--end; // 无符号数--,一定要小心上述问题**********************
}
ps->_array[pos] = x;
++ps->_size;
}
3.9 删除
从pos的位置往后,进行循环,后一个元素向前移一位,相应的数组大小减1。
void SeqListErase(SeqList* ps, size_t pos) // 删除
{
assert(ps && pos < ps->_size); // pos是无符号的,大于0
while (pos < ps->_size - 1) // 循环覆盖
{
ps->_array[pos] = ps->_array[pos + 1];
++pos;
}
--ps->_size;
}
3.10 返回数组大小
直接返回即可。
size_t SeqListSize(SeqList* ps) // 返回数组大小,尽量不要自己操作结构体
{
assert(ps);
return ps->_size;
}
3.11 返回某值下标
循环遍历,依次与该值比较,没有找到返回-1。
size_t SeqListFind(SeqList* ps, DataType x) // 找值x的位置,并返回其下标
{
assert(ps);
size_t end = 0;
while (end < ps->_size)
{
if (ps->_array[end] == x) // 找到了
{
return end;
}
++end;
}
return -1; // 虽然是无符号,但是可以返回-1
}
3.12 返回某位置的值
直接根据下标返回该值即可。
DataType SeqListAt(SeqList* ps, size_t pos) // 返回pos位置的值
{
assert(ps && pos < ps->_size);
return ps->_array[pos];
}
3.13 删除第一个指定值结点
循环遍历,依次与x进行匹配,找到就调用删除函数,删除除掉该值。此时不用再额外将数组大小减1,因为在调用删除函数时,已经修改过数组大小了。
void SeqListRemove(SeqList* ps, DataType x) // 删除第一个指定值的结点
{
assert(ps);
size_t start = 0;
for (start = 0; start < ps->_size; ++start)
{
if (x == ps->_array[start]) // 找到值x
{
SeqListErase(ps, start); // 调用删除函数,删除该结点
return 0;
}
}
if (start == ps->_size)
{
printf("没有找到%d\n", x);
}
return 0;
}
3.14 删除指定位置指定值
循环遍历,依次比较,找到符合该下标和该值的元素,调用删除函数,此处也不用再额外将数组大小减1,删除函数中已经修改了数组大小。
void SeqListModify(SeqList* ps, size_t pos, DataType x) // 删除指定位置指定值
{
assert(ps && pos < ps->_size);
size_t start = 0;
for (start = 0; start < ps->_size; ++start)
{
if ((start == pos) && (ps->_array[start] == x))
{
SeqListErase(ps, start);
return 0;
}
}
if (start == ps->_size)
{
printf("没有找到%d\n", x);
}
return 0;
}
3.15 打印顺序表
调用函数SeqListSize(ps)得到顺序表大小,函数SeqListAt(ps, i)按照下标打印。
void SeqListPrint(SeqList* ps) // 打印
{
for (size_t i = 0; i < SeqListSize(ps); ++i)
{
printf("%d ",SeqListAt(ps, i));
}
printf("\n");
}
3.16 顺序表冒泡排序
两个循环控制,内层单趟循环,外层多趟循环。
void SeqListBubbleSort(SeqList* ps) // 冒泡排序
{
assert(ps);
size_t end = ps->_size;
// 控制多趟冒泡
while (end > 1)
{
// 控制单趟冒泡
int flag = 0;
for (size_t i = 1; i < end; ++i)
{
if (ps->_array[i - 1] > ps->_array[i])
{
DataType tmp = ps->_array[i - 1];
ps->_array[i - 1] = ps->_array[i];
ps->_array[i] = tmp;
flag = 1;
}
}
if (flag == 0)
{
break;
}
--end;
}
}
3.17 顺序表二分查找
// 左闭右开型
int SeqListBinaryFind(SeqList* ps, DataType x) // 二分查找 时间复杂度O(N),空间复杂度O(1)
{
assert(ps);
size_t begin = 0;
siez_t end = ps->_size;
while (begin < end)
{
size_t mid = begin + ((end - begin) >> 1); // >>1 相当于 /2
if (ps->_array[mid] < x)
{
begin = mid + 1;
}
else if (ps->_array[mid] > x)
{
end = mid;
}
else
{
return mid;
}
}
return -1;
}
3.18 删除已知值所有结点
循环遍历,当该值不等于已知值时,就把该值按照下标从0开始,依次存放到该数组中,覆盖掉数组中原来的值;当该值等于已知值时,直接忽略,外层循环下标不用加,继续查看下一个元素,以此类推。得到的新改进的数组就是删除过后的新数组。
void SeqListRemoveAll(SeqList* ps, DataType x) // 删除已知值的全部结点
{
assert(ps);
size_t tmp = 0;
size_t index = 0;
for (tmp = 0; tmp < ps->_size; ++tmp) // 控制整个数组
{
if (ps->_array[tmp] != x) // 当值!=已知值时,把该值直接复制到该数组
{
ps->_array[index] = ps->_array[tmp];
++index;
}
}
ps->_size = index; // 最后的下标就是数组大小
return 0;
}