线性表是n个具有相同特性的数据元素的有限序列,包括顺序表、链表、栈、队列等。
线性表在逻辑结构上是连续的,但是在物理结构上不一定连续。
顺序表
顺序表的特点是:物理地址连续
通常采用数组存储,可分为:
- 静态顺序表:使用定长数组存储
- 动态顺序表:使用动态数组存储
既然顺序表的物理地址是连续的,那么就可以计算出某个元素的物理地址.
下图给出的即为计算物理地址的公式
顺序表的优点
- 存储密度大,空间利用率高
- 存储空间连续,允许元素随机访问(即查找元素方便)
顺序表的缺点
- 长度固定,实现插入和删除操作不方便
顺序表的基本操作接口
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* array; // 指向动态开辟的数组
size_t size; // 有效数据个数
size_t capicity; // 容量空间的大小
}SeqList;
void SeqListInit(SeqList* psl, size_t capacity);//初始化
void SeqListDestory(SeqList* psl);//销毁释放
void CheckCapacity(SeqList* psl);//检查存储元素是否达到上限
void SeqListPushBack(SeqList* psl, SLDataType x);//尾插法
void SeqListPopBack(SeqList* psl);//尾删法
void SeqListPushFront(SeqList* psl, SLDataType x);//头插法
void SeqListPopFront(SeqList* psl);//头删法
int SeqListFind(SeqList* psl, SLDataType x);//查找某元素所在位置
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);//在某个位置插入一个元素
void SeqListErase(SeqList* psl, size_t pos);//删除某位置的元素
void SeqListRemove(SeqList* psl, SLDataType x);//删除某个值的元素
void SeqListModify(SeqList* psl, size_t pos, SLDataType x);//修个某个位置元素的值
void SeqListPrint(SeqList* psl);//打印顺序表中的元素
void SeqListBubbleSort(SeqList* psl);//冒泡排序
int SeqListBinaryFind(SeqList* psl, SLDataType x);//二分查找
各接口的具体实现
#include"SeqList.h"
//初始化
void SeqListInit(SeqList* psl, size_t capacity)
{
assert(psl);//判空
psl->capicity = capacity;
psl->array = (SLDataType *)malloc(capacity * sizeof(SLDataType));//开辟动态空间
psl->size = 0;
}
void SeqListDestory(SeqList* psl)
{
assert(psl);
if (psl->array)
{
free(psl->array);//将一个数组free后,需要将其置空(NULL)
psl->array = NULL;
psl->capicity = 0;//并将其开辟的空间均置为0
psl->size = 0;
}
}
//检查是否到达上限
void CheckCapacity(SeqList* psl)
{
assert(psl);
if (psl->size == psl->capicity)
{
psl->capicity *= 2;
psl->array = (SLDataType *)realloc(psl->array, psl->capicity * sizeof(SLDataType));
}
}
//尾插法
void SeqListPushBack(SeqList* psl, SLDataType x)
{
assert(psl);
//插入一个元素需要检查是否需要进行扩容,
//若psl->size == psl->capacity,则需要扩容
CheckCapacity(psl);
//尾插法即将要插入放入元素直接插到顺序表的末端,再将顺序表的大小加一
psl->array[psl->size] = x;
psl->size++;
}
//尾删法
void SeqListPopBack(SeqList* psl)
{
assert(psl || psl->size);
//直接将顺序表中有效元素的个数减一,即可删掉表中最后一个元素
psl->size--;
}
//头插法
void SeqListPushFront(SeqList* psl, SLDataType x)
{
assert(psl);
//头插法是将每个元素向后移动一位,即将后面的那个元素覆盖
//再将要插入的元素赋值给表中的第一个元素,表的长度加一
for (int i = psl->size - 1; i >= 0; i++)
{
psl->array[i + 1] = psl->array[i];
}
psl->array[0] = x;
psl->size++;
}
//头删法
void SeqListPopFront(SeqList* psl)
{
assert(psl || psl->size);
psl->size--;//先将表的长度减一
//头删法是将后面的一个元素向前移动,即后面的元素将前面的元素覆盖
for (int i = 0; i < psl->size; i++)
{
psl->array[i] = psl->array[i + 1];
}
}
//查找该元素是否在顺序表中存在
int SeqListFind(SeqList* psl, SLDataType x)
{
assert(psl);
for (int i = 0; i < psl->size; i++)
{
if (psl->array[i] == x)//判断顺序表中元素的值与该值相等,返回顺序表中的位置
{
return i;
}
}
}
//在pos后插入一个元素
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x)
{
assert(psl);
CheckCapacity(psl);
//与头插法思想一致
for (int i = psl->size -1; i >= pos; i--)
{
psl->array[i + 1] = psl->array[i];
}
psl->array[pos] = x;
psl->size++;
}
//删除指定位置的元素
void SeqListErase(SeqList* psl, size_t pos)
{
assert(psl);
psl->size--;
for(int i =pos; i< psl->size;i++)
{
psl->array[i] = psl->array[i + 1];
}
}
//删除给定值的元素
void SeqListRemove(SeqList* psl, SLDataType x)
{
assert(psl);
int pos = SeqListFind(psl, x);//利用find函数找到该元素的位置
if (pos >= 0)
{
SeqListErase(psl, pos);//利用删除函数删掉该位置的值即可
}
}
//修改一个指定位置元素的值
void SeqListModify(SeqList* psl, size_t pos, SLDataType x)
{
assert(psl || (pos < psl->size));
psl->array[pos - 1] = x;
}
//打印函数
void SeqListPrint(SeqList* psl)
{
assert(psl);
for (int i = 0; i < psl->size; i++)
{
cout << psl->array[i] << " ";
}
cout << endl;
}
//冒泡排序
void SeqListBubbleSort(SeqList* psl)
{
assert(psl);
SLDataType tmp;
for (int i = 0; i < psl->size - 1; i++)
{
for (int j = 0; j < psl->size - 1 - i; j++)
{
if (psl->array[j] > psl->array[j + 1])
{
tmp = psl->array[j];
psl->array[j] = psl->array[j + 1];
psl->array[j + 1] = tmp;
}
}
}
}
//二分法查找
int SeqListBinaryFind(SeqList* psl, SLDataType x)
{
assert(psl);
int left = 0;
int right = psl->size - 1;
int mid;
while (left <= right)//不要在代码块内部定义变量
{
mid = (left + right) / 2;
if (psl->array[mid] < x)
{
left = mid + 1;
}
else if (psl->array[mid]>x)
{
right = mid - 1;
}
else
{
return mid;
}
}
}
测试
#include"SeqList.h"
int main()
{
SeqList test;
SeqListInit(&test, 10);//必须传地址
SeqListPushBack(&test, 1);
SeqListPushBack(&test, 2);
SeqListPushBack(&test, 3);
SeqListPushBack(&test, 4);
SeqListPushBack(&test, 5);
SeqListPushBack(&test, 6);
SeqListPushBack(&test, 7);
SeqListPushBack(&test, 8);
SeqListPushBack(&test, 9);
SeqListPrint(&test);
SeqListPopFront(&test);
SeqListPrint(&test);
SeqListPopBack(&test);
SeqListPrint(&test);
SeqListInsert(&test, 1, 10);
SeqListPrint(&test);
SeqListErase(&test, 6);
SeqListPrint(&test);
SeqListRemove(&test, 8);
SeqListPrint(&test);
SeqListModify(&test, 2, 12);
SeqListPrint(&test);
SeqListBubbleSort(&test);
SeqListPrint(&test);
cout << SeqListBinaryFind(&test, 6) << endl;
SeqListDestory(&test);
system("pause");
return 0;
}
运行结果
总结
顺序表插入和删除元素的时间复杂度为O(n),其余操作的时间复杂度均与n无关,故时间复杂度为O(1)。