线性表的顺序表示指的是用一组地址连续的存储单元(内存)依次存储线性表的数据元素。
特点:
- 逻辑相邻,物理也相邻
- 支持存放,插入,删除,修改,读取数据等操作
- 一位数组是一种特殊的顺序表,但顺序表不是数组
- 插入和删除较链表来说不太方便
固定大小的顺序表
结构设计
#define SEQ_INIT_SIZE 10
typedef int ElemType;
typedef struct
{
ElemType data[SEQ_INIT_SIZE];
int cursize;
}SeqList;
//注意区别
//无名结构体所定义的全局结构体变量
struct
{
ElemType data[SEQ_INIT_SIZE];
int cursize;
} SeqList;
缺陷:
- 不能够随着数据量的增多而增大数组大小
- 受到栈空间的限制,不能超过1M
可变顺序表
顺序表的结构设计
typedef struct SeqList
{
int* data;
int cursize;
int capacity;
}SeqList;
其他参数
typedef SqList * PSqList;//
#define INIT_SIZE 10 //初始化大小
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1//不能实行
#define OVERFLOW -2 //内存溢出
typedef int status;
顺序表操作
初始化
void InitSqList(PSqList ps)
{
assert(ps != NULL);
ps->elem = (int *)malloc(INIT_SIZE*sizeof(int));
ps->length = 0;
ps->listsize = INIT_SIZE;
}
判满,内部函数,不对外
用户不需要在意满还是没满,只是使用,因此加上“static”关键字
static bool IsFull(PSeqList ps)
{
return GetSize(ps) == GetCapacity(ps);
}
扩容,将总容量扩大到原来的2倍
static bool Inc(PSeqList ps)
{
assert(ps != NULL);
ElemType* newdata = NULL;
int total = ps->capacity * SEQ_INC_SIZE;
newdata = (ElemType*)malloc(sizeof(ElemType) * total);
//这里没有使用realloc的原因是:
//一旦数据量过大堆区空间不足,realloc将返回空指针,接收后原数据会被丢失
if (NULL == newdata)
{
return false;
}
memcpy(newdata, ps->data, sizeof(ElemType) * ps->capacity);
free(ps->data);
ps->data = newdata;
ps->capacity = total;
return true;
}
插入
status Insert(PSeqList ps, int pos, int val)
{
assert(ps != NULL);
if (pos < 0 || pos > ps->cursize)//cursize插入仍连续后面就不行了
{
return INFEASIBLE;//不能实行
}
if (IsFull(ps) && !Inc(ps))
{
return OVERFLOW;
}
for (int i = ps->cursize; i > pos; i--)
{
ps->data[i] = ps->data[i - 1];
}
ps->data[pos] = val;
ps->cursize += 1;
return OK;
}
按值查找
/按值查询
int FindValue(PSeqList ps, ElemType val)
{
assert(ps != NULL);
//顺着遍历
//int pos = -1;
// for(int i = 0;i<ps->cursize;i++)
// {
// if(val == ps->data[i]);
// {
// pos = i;
// break;
// }
// }
//倒着遍历
int pos = ps->cursize - 1;
while (pos >= 0 && ps->data[pos] != val)//减小到-1,简洁与不做判断直接退出返回-1,正确
//注意错误代码:
//while( ps->data[pos] != val && pos >= 0),减小到-1,先判断前一句会产生数组上越界
{
--pos;
}
return pos;
}
### 按值删除
```c
bool DeleteVal(PSqList ps,int key)
{
int i = Search(ps,key);
if(i < 0)//没有key
{
return false;
}
return DeletePos(ps,i);
}
按下标删除
bool DeletePos(PSqList ps,int pos)
{
if(pos<0 || pos>=ps->length)
{
return false;
}
//将后面的数据前移
for(int i=pos;i+1<ps->length;i++)
{
ps->elem[i] = ps->elem[i+1];
}
//有效数据个数--
ps->length--;
return true;
}
删除所有重复的值
两个下标,且该算法时间复杂度为O(n)
//删除所有重复的值
void Remove_all(PSeqList ps,ElemType val)
{
assert(ps != NULL);
int j = -1;
for(int i = 0;i<ps->cursize;++i)
{
if(ps->data[i] != val)
{
j++;
ps->data[j] = ps->data[i];
}
}
ps->cursize = j+1;
}
获取指定位置的值
(注意这里容易出现的问题:)
1.设计问题:很多人会设计成int GetElem(PSqList ps,int pos),返回错误值一定会和某一个正常值冲突无法区分。
2.成功返回true,失败返回false.成功的值通过rtval(输出参数)带出
bool GetElem(PSqList ps,int pos,int *rtval)
{
if(pos<0 || pos>=ps->cursize)
{
return false;
}
*rtval = ps->data[pos];
return true;
}
修改
bool SetElem(PSqList ps,int pos,int newVal)
{
if(pos<0 || pos>=ps->cursize)
{
return false;
}
ps->data[pos] = newVal;
return true;
}
排序
//冒泡排序
void BubbleSort(PSeqList ps)
{
assert(ps != NULL);
int n = ps->cursize;
for(int i = 1;i<n;++i)//控制比较的轮次,每一次都将最大的那个放到最后,并不再下次比较的名单中
{
for(int j = 0;i<n-i;j++)
{
if(ps->data[i]>ps->data[j+1])
{
Swap_val(&ps->data[i],&ps->data[j+1]);
}
}
}
}
合并两个有序顺序表
//合并两个有序顺序表
void MergeList_sq(const PSeqList la,const PSeqList lb,PSeqList lc)
{
ElemType* pa = la->data;
ElemType* pb = lb->data;
lc->capacity = la->cursize + lb->cursize;
ElemType * pc = lc->data = (ElemType*)malloc(sizeof(ElemType)*lc->capacity);
if(NULL == lc->data)exit(EXIT_FAILURE);
ElemType* pa_last = la->data + la->cursize-1;
ElemType* pb_last = lb->data + lb->cursize-1;
while(pa <= pa_last && pb <= pb_last)
{
if(*pa <= *pb)
{
*pc++ = *pa++;//*pc = *pa pc++ pa++
}
else
{
*pc++ = *pb++;
}
}
while(pa<= pa_last){*pc++ = *pa++;}//a表没插完
while(pb<= pb_last){*pc++ = *pb++;}//b表没插完
}
获取有效数据个数
int GetLength(PSqList ps)
{
return ps->cursize;
}
判空
道理跟清除一个字符串(首字符修改\0,就无法读取该字符串达到删除目的)一样
bool IsEmpty(PSqList ps)
{
return ps->cursize== 0;
}
清空
//置空
void Clear(PSeqList ps)
{
assert(ps != NULL);
ps->cursize = 0;
}
销毁,回收内存
void Destroy(PSeqList ps)
{
assert(ps != NULL);
free(ps->data);
ps->data = NULL;
ps->cursize = 0;
ps->capacity = 0;
}
输出
//打印
void Printseqlist(const PSeqList ps)
{
assert(ps != NULL);
printf("Now Capacity : %d\n", ps->capacity);
printf("Now Cursize : %d\n", ps->cursize);
for (int i = 0; i < ps->cursize; i++)
{
printf("%d ", ps->data[i]);
}
printf("\n");
}
总结
顺序存储结构
适用场景:适用于需要大量随机访问数据元素的场景,增删数据元素较少的程序
- 支持随机访问
- 尾插尾删效率高
不适用的场景
- 头插头删、中间插入、删除效率低、不适合这些位置的插入删除
- 增容代价高:申请、拷贝、释放
头文件sqlist.h
#pragma once //防止头文件被重复引用
//头文件,存放结构的定义和函数的声明
#define SEQ_INIT_SIZE 2 //初始化大小
#define SEQ_INC_SIZE 2
typedef int ElemType;
typedef int status;
typedef struct SeqList
{
int* data;
int cursize;
int capacity;
}SeqList;
typedef SeqList* PSeqList;
//初始化
void InitSeqList(PSeqList ps);
//在ps的pos位置,插入数据val
status Insert(PSeqList ps, int pos, int val);
//尾插
void Push_Back(PSeqList ps, int val);
//头插
void Push_Front(PSeqList ps, int val);
//获取有效数据个数
int GetSize(const PSeqList ps);
//获取顺序表容量
int GetCapacity(const PSeqList ps);
//判空
static bool IsEmpty(const PSeqList ps);
//判满
static bool IsFull(const PSeqList ps);
//扩容
static bool Inc(PSeqList ps);
//按值查询
int FindValue(const PSeqList ps, ElemType val);
//按下标删除
bool DeletePos(PSeqList ps,int pos);
//按值删除
bool DeleteVal(PSeqList ps,int val);
//删除所有重复的值
void Remove_all(PSeqList ps,ElemType val);
//修改数据
bool SetElem(PSeqList ps,int pos, int newval);
//清空数据
void Clear(PSeqList ps);
//销毁
void Destroy(PSeqList ps);
//置空
void Clear(PSeqList ps);
//输出
void Printseqlist(const PSeqList ps);
//交换
void Swap_val(ElemType *a,ElemType *b);
//冒泡排序
void BubbleSort(PSeqList ps);
//合并两个有序顺序表
void MergeList_sq(const PSeqList la,const PSeqList lb,PSeqList lc);
#endif