首先讲下顺序表的定义和结构,顺序表是在计算机内存中以数组的形式保存的线性表,是指用一组地址连续的存储单元依次存储数据元素的线性结构。线性表采用顺序存储的方式存储就称之为顺序表。顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中。
图表如下:
了解了顺序表是什么之后,我们来比较一下是顺序表和单链表之间的区别:
1.单链表访问时不支持随机访问,比如直接输入下标查找该下标的元素;但是顺序表是一个数组,可以通过下标进行随机访问。
2.单链表在插入/删除时,则比顺序表的效率高很多,在中间元素插入或者删除时,单链表只需要将上个元素指向下个元素的指针跳过需要删除或者插入的元素指向下一个,就可以实现插入/删除操作;顺序表则不同,在插入或删除时,需要将删除的元素后面所有元素向前移动,这就是顺序表的效率低了很多。
3.在CPU缓存效率上,它们也有区别,因为顺序表是数组存放,所以它们每个元素的地址都是连续的,所以在CPU缓存时,可能一次取到多个顺序表中的元素,但是单链表则不然,它们每个元素的地址都是不连续的,所以在CPU缓存时,每次取到的数据会比顺序表少,效率也不会比顺序表高。
在以后的项目中,可以结合顺序表和单链表的优劣势进行选择数据的存储结构,提高程序的运行速度。
下面是顺序表的实现,大概如下:
动态顺序表用一个头文件SeqList.h(声明函数及其他变量),还有两个源文件test.c(测试函数功能)与fun.c(各个函数的实现)组成
SeqList.h:
#ifndef __SEQLIST_H__
#define __SEQLIST_H__
#include<stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
typedef int DataType; //将p_data类型在此定义,为以后如果改变顺序表元素类型
//只需在这里改变一次
#define DEFAULT_SZ 3 //每次开辟的内存个数
typedef struct SeqList
{
DataType *p_data; //数据区的地址
int sz; //有效元素的个数
int capacity; //开辟内存的总容量
}SeqList,*pSeqList;
//接口
void InitSeqlist(pSeqList ps); //初始化顺序表(开辟内存空间)
void PushBack(pSeqList ps, DataType x); //尾插,在最后插入一个新元素
void PopBack(pSeqList ps); //尾删,删除尾部最后一个元素
void PushFront(pSeqList ps, DataType x); //头插,在最前面插入一个新元素
void PopFront(pSeqList ps); //头删,删除第一个元素
//排序
void Sort(pSeqList ps); //冒泡排序顺序表
//逆序顺序表
void Reserve(pSeqList ps); //将现有的顺序表倒序排列
//打印
void Display(const pSeqList ps);
//查找指定元素
int Find(const pSeqList ps, DataType x);
//指定位置删除
void Erase(pSeqList ps, int pos);
//对已排序顺序表二分查找
int BinarySearch(pSeqList ps,int left, int right, DataType x);
//指定位置插入
void Insert(pSeqList ps, int pos, DataType x);
//指定元素删除
void Remove(pSeqList ps, DataType x);
//删除指定位置的所有元素
void RemoveAll(pSeqList ps, DataType x);
#endif //__SEQLIST_H__
fun.c:
#define _CRT_SECURE_NO_WARNINGS 1
#include "SeqList.h"
/**************************************************/
/* 在有指针的传参的函数中, */
/* */
/* 首先应断言assert,保证不是野指针 */
/**************************************************/
void InitSeqlist(pSeqList ps)
{
assert(ps);
ps->sz = 0;
//第一次开辟内存空间,用到malloc函数
//开辟大小应该是(元素个数*每个元素大小)
ps->p_data = (DataType *)malloc(DEFAULT_SZ*sizeof(DataType));
//判断内存开辟是否成功
if (NULL == ps->p_data)
{
perror("InitSeqList for data");
exit(EXIT_FAILURE);
}
//开辟成功后的内存空间初始化为0
memset(ps->p_data,0,DEFAULT_SZ*sizeof(DataType));
ps->capacity += DEFAULT_SZ;
}
//在有些功能函数使用前,应该先判断开辟内存是否已满
//如果已满,则需要用realloc再开辟一个更大的内存空间
int CheckSeqList(pSeqList ps)
{
//先将p_data的地址用临时变量保存
//因为可能realloc开辟失败
//不保存p_data的话开辟失败后将找不到原始地址
DataType *tmp = ps->p_data;
assert(ps);
if (ps->sz >= ps->capacity)
{
ps->capacity += DEFAULT_SZ;
ps->p_data = (DataType *)realloc(ps->p_data,ps->capacity*sizeof(DataType));
if (NULL ==ps->p_data) //判断开辟是否成功
{
ps->p_data = tmp;
return -1;
} //如果开辟失败则返回-1
} //其他情况都是返回1
return 1;
}
void PushBack(pSeqList ps, DataType x) //尾插
{
int ret = 0;
assert(ps);
ret = CheckSeqList(ps); //用ret临时变量接收CheckSeqList()返回值
if (ret == 1) //判断开辟是否成功
{
ps->p_data[ps->sz] = x; //直接在尾部插入新元素
ps->sz ++;
}
}
void Display(const pSeqList ps) //打印顺序表
{
int i = 0;
assert(ps);
for (i=0; i<ps->sz; i++)
{
printf("%d ",ps->p_data[i]);
}
printf("\n");
}
void PopBack(pSeqList ps) //尾删
{
assert(ps);
if (ps->sz == 0) //如果顺序表中元素为0
{ //则不需要删除直接结束
return;
}
ps->p_data[ps->sz] = 0;
ps->sz --;
}
void PushFront(pSeqList ps, DataType x) //头插
{
int i = 0;
int ret = 0;
assert(ps);
ret = CheckSeqList(ps); //用ret临时变量接收CheckSeqList()返回值
if (ret == 1) //判断开辟是否成功
{
for (i=ps->sz; i>0; i--)
{
ps->p_data[i] = ps->p_data[i-1]; //头插要将原顺序表整体向后移动一位
} //给将要插入的元素挪出位置
ps->p_data[i] = x; //将新元素插入到头部
ps->sz++; //并且有效元素加一
}
}
void PopFront(pSeqList ps) //头删
{
int i = 0;
assert(ps);
if (ps->sz == 0) //判断顺序表中的元素是否为空
{
return;
}
for (; i<ps->sz-1; i++) //如果不为空,则执行此循环
{
ps->p_data[i] = ps->p_data[i+1]; //从第二个元素开始从前到后依次将元素向前移动
}
ps->sz--; //最后有效元素个数减一
}
void Sort(pSeqList ps) //排序(冒泡)
{
int i = 0;
int j = 0;
int flag = 1; //这里做一个冒泡优化,建立临时变量flag
DataType tmp = 0;
assert(ps);
//如果其中有一趟循环之后元素位置没有改变
for (i=0; i<ps->sz-1; i++) //则表明该序列已有序,可以跳出循环
{
flag = 1;
for (j=0; j<ps->sz-i-1; j++)
{
if (ps->p_data[j]>ps->p_data[j+1])
{
flag = 0; //此趟循环元素有改变
//flag置为0
tmp = ps->p_data[j];
ps->p_data[j] = ps->p_data[j+1];
ps->p_data[j+1] = tmp;
}
}
if (flag==1) //如果某一趟排序之后flag为1
{ //这说明该序列已有序,从if中跳出循环
break;
}
}
}
void Reserve(pSeqList ps) //逆序
{
int left = 0; //第一个元素下标
int right = ps->sz-1; //最后一个元素下标
assert(ps);
for (; left<(ps->sz/2); left++,right--) //从两边分别向中间增加(减少)
{
DataType tmp = ps->p_data[left];
ps->p_data[left] = ps->p_data[right];
ps->p_data[right] = tmp;
}
}
int Find(const pSeqList ps, DataType x) //查找指定元素
{
int i = 0;
assert(ps);
if (ps->sz == 0) //如果有效元素为空
{
return -1; //则直接返回1并结束函数
}
for (; i<ps->sz; i++) //遍历整个元素
{
if (x == ps->p_data[i]) //找到指定元素
{
return i; //返回指定元素位置
}
}
return -1; //遍历之后未找到返回-1
}
void Erase(pSeqList ps, int pos) //指定位置删除
{
int i = 0;
DataType tmp = 0;
assert(ps);
if ((pos>ps->sz)&&(ps->sz==0)) //如果有效元素为空或者指定位置输入大于有效元素个数
{
return ; //直接结束函数
}
for (i=pos; i<ps->sz; i++) //直接从指定位置元素开始
{
ps->p_data[i] = ps->p_data[i+1]; //依次从前向后向前移动一位
}
ps->p_data[ps->sz] = 0; //先将原来最后一位置零
ps->sz--; //最后有效元素个数减一
}
int BinarySearch(pSeqList ps,int left, int right, DataType x) //已有序的顺序表二分查找,并返回查找元素位置
{
int tmp = 0;
assert(ps);
if (ps->sz == 0) //如果有效元素个数为零
{
return -1; //直接结束函数并返回-1
}
while(right>=left) //right必须要">="left,存在两边指向同一个元素
{ //如果没有‘=’这种条件会跳过
int mid = left+((right-left)>>1);
if (ps->p_data[mid] == x) //下面就是二分查找了
{
return mid;
}
else if (ps->p_data[mid] > x)
{
right = mid - 1;
}
else
{
left = mid + 1;
}
}
return -1;
}
void Insert(pSeqList ps, int pos, DataType x) //指定位置插入
{
int i = 0; //与指定删除差不多
int ret = 0; //区别就是数组依次向后移动一位
assert(ps);
if (pos>ps->sz) //插入位置在顺序表外
{
return; //错误位置直接结束函数
}
ret = CheckSeqList(&ps); //插入前判断内存空间是否已满
if(ret == 1) //内存空间足够
{
for (i=ps->sz; i>pos; i--) //从pos位置从后向前依次向后移动一位
{
ps->p_data[i] = ps->p_data[i-1];
}
ps->p_data[i] = x;
ps->sz++;
}
}
void Remove(pSeqList ps, DataType x) //删除指定元素
{
int i = 0;
assert(ps);
for (; i<ps->sz;i++) //遍历顺序表
{
if (ps->p_data[i] == x) //寻找指定删除元素
{
for (;i<ps->sz-1; i++) //移动顺序表操作
{
ps->p_data[i] = ps->p_data[i+1]; //从指定元素位置开始,从前向后依次向前移动一位
}
ps->p_data[ps->sz] = 0; //将原顺序表最后一个元素置零
ps->sz--; //有效元素个数减一
}
}
}
void RemoveAll(pSeqList ps, DataType x) //删除指定元素的所有元素
{
int i = 0;
int tmp = 0; //第二变量
assert(ps); //将i与tmp区分开
for (; i<ps->sz;i++)
{
if (ps->p_data[i] == x) //当在顺序表中找到了该数时
{
for (tmp=i; tmp<ps->sz-1; tmp++)
{
ps->p_data[tmp] = ps->p_data[tmp+1];//从tmp位从前向后依次向前移动一位
}
i--;
//移动一位之后i+1位元素移到i位
//可能存在第i+1位也是需要删除的元素
//因为外层循环有i++,这里为了使i仍指向原来位置,则需要有i--
//指向移动之后的第一个元素
ps->p_data[ps->sz] = 0;
ps->sz--;
} //移动之后需要继续遍历余下顺序表寻找是否存在指定删除的元素
}
}