线性表的顺序存储结构-顺序表
一.
- 在C/C++语言中,借助数组类型来实现顺序表,也就是说,用数组存放线性表的元素及其逻辑关系,数组的基本类型就是线性表中元素的的类型,数组大小(即数组上界-下界+1)要大于等于线性表的长度,否则该数组不能存放对应线性表的所有元素。所以当线性表长度小于数组大小时,该数组中会有一部分空闲空间。
注意:顺序表采用数组来实现,但不能将任何一个数组都当作是一个顺序表,二者的运算是不同的。
2.在定义一个线性表的顺序存储类型时,需要定义一个数组来存储线线表中的所有元素和定义一个整型变量来存储线性表的长度。
假定数组用data[MaxSize]表示,长度整型变量用length表示,并采用结构体类型表示,则元素类型为通用类型标识符ElemType的线性表的顺序存储类型可描述如下:
typedef struct
{ ElemType data[MaxSize];
int length;
} SqList; //顺序表类型
其中data成员存放元素,length成员存放线性表的实际长度。
二.顺序表基本运算实现
1.一旦采用顺序表存储结构,我们就可以用C/C++语言实现线性表的各种基本运算。为了方便,假设ElemType为char类型,使用如下自定义类型语句:
typedef char ElemType;
①.建立顺序表
其方法是将给定的含有n个元素的数组的每个元素依次放入到顺序表中,并将n赋给顺序表的长度成员。算法如下:
void CreateList(SqList *&L,ElemType a[],int n)
//建立顺序表
{ int i;
L=(SqList *)malloc(sizeof(SqList));
for (i=0;i<n;i++)
L->data[i]=a[i];
L->length=n;
}
②.顺序表基本运算算法
(1)初始化线性表InitList(L)
该运算的结果是构造一个空的线性表L。实际上只需将length成员设置为0即可。
void InitList(SqList *&L) //引用型指针
{ L=(SqList *)malloc(sizeof(SqList));
//分配存放线性表的空间
L->length=0;
}
本算法的时间复杂度为O(1)。
(2)销毁线性表DestroyList(L)
该运算的结果是释放线性表L占用的内存空间。
void DestroyList(SqList *&L)
{
free(L);
}
本算法的时间复杂度为O(1)。
(3)判定是否为空表ListEmpty(L)
bool ListEmpty(SqList *L)
{
return(L->length==0);
}
本算法的时间复杂度为O(1)。
(4)求线性表的长度ListLength(L)
该运算返回顺序表L的长度。实际上只需返回length成员的值即可。
int ListLength(SqList *L)
{
return(L->length);
}
本算法的时间复杂度为O(1)。
(5)输出线性表DispList(L)
该运算当线性表L不为空时,顺序显示L中各元素的值。
void DispList(SqList *L)
{ int i;
if (ListEmpty(L)) return;
for (i=0;i<L->length;i++)
printf("%c",L->data[i]);
printf("\n");
}
(6)求某个数据元素值GetElem(L,i,e)
该运算返回L中第 i(1≤i≤ListLength(L))个元素的值,存放在e中。
bool GetElem(SqList *L,int i,ElemType &e)
{ if (i<1 || i>L->length) return false;
e=L->data[i-1];
return true;
}
本算法的时间复杂度为O(1)。
(7)按元素值查找LocateElem(L,e)
该运算顺序查找第1个值域与e相等的元素的逻辑位序。若这样的元素不存在,则返回值为0。
int LocateElem(SqList *L, ElemType e)
{
int i=0;
while (i<L->length && L->data[i]!=e) i++;
if (i>=L->length)
return 0;
else
return i+1;
}
(8)插入数据元素ListInsert(L,i,e)
该运算在顺序表L的第i(1≤i≤ListLength(L)+1)个位置上插入新的元素e。如果i值不正确,则显示相应错误信息;否则将顺序表原来第i个元素及以后元素均后移一个位置,移动方向从右向左,如下图所示,腾出一个空位置插入新元素,最后顺序表长度增1。
bool ListInsert(SqList *&L,int i,ElemType e)
{ int j;
if (i<1 || i>L->length+1)
return false; //参数错误时返回false
i--; //将顺序表逻辑序号转化为物理序号
for (j=L->length;j>i;j--) //将data[i..n]元素后移一个位置
L->data[j]=L->data[j-1];
L->data[i]=e; //插入元素e
L->length++; //顺序表长度增1
return true; //成功插入返回true
}
对于本算法来说,元素移动的次数不仅与表长L.length=n有关,而且与插入位置i有关:当i=n+1时,移动次数为0;当i=1时,移动次数为n,达到最大值。
因此插入算法的平均时间复杂度为O(n)。
(9)删除数据元素ListDelete(L,i,e)
该运算删除顺序表L的第i(1≤i≤ListLength(L))个元素。如果i值不正确,则显示相应错误信息;否则将线性表第i个元素以后元素均向前移动一个位置,移动方向为从左向右,如下图所示,这样覆盖了原来的第i个元素,达到删除该元素的目的,最后顺序表长度减1。
bool ListDelete(SqList *&L,int i,ElemType &e)
{ int j;
if (i<1 || i>L->length) //参数错误时返回false
return false;
i--; //将顺序表逻辑序号转化为物理序号
e=L->data[i];
for (j=i;j<L->length-1;j++) //将data[i..n-1]元素前移
L->data[j]=L->data[j+1];
L->length--; //顺序表长度减1
return true; //成功删除返回true
}
例:已知长度为n的线性表A采用顺序存储结构,编写一个(时间复杂度为O(n)、空间复杂度为O(1)的算法)该算法删除线性表中所有值为x的数据元素。
分析:
如果每删除一个值为x的元素都进行移动,其时间复杂度为O(n2),空间复杂度为O(1)。
如果用一个新的顺序表来实现,其时间复杂度为O(n),空间复杂度为O(n)。
解法一:设删除L中所有值等于x元素后的顺序表为L1,显然L1包含在L中,为此L1重用L的空间。扫描顺序表L,重建L只包含不等于x的元素。算法如下:(算法1:类似于建顺序表
)
void delnode1(SqList *&L,ElemType x)
{ int k=0,i; //k记录值不等于x的元素个数
for (i=0;i<L->length;i++)
if (L->data[i]!=x) //若当前元素不为x,将其插入L中
{ L->data[k]=L->data[i];
k++; //不等于x的元素增1
}
L->length=k; //顺序表L的长度等于k
}
解法二:用k记录顺序表L中等于x的元素个数,一边扫描L一边统计k值,并将不为x的元素前移k个位置,最后修改L的长度。算法如下:
void delnode2(SqList *&L,ElemType x)
{ int k=0,i=0; //k记录值等于x的元素个数
while (i<L->length)
{ if (L->data[i]==x) //当前元素值为x时k增1
k++;
else //当前元素不为x时将其前移k个位置
L->data[i-k]=L->data[i];
i++;
}
L->length-=k; //顺序表L的长度递减k
}
代码:
#include "sqlist.cpp"
void swap(int &x,int &y) //交换x和y
{ int tmp=x;
x=y; y=tmp;
}
void move1(SqList *&L)
{ int i=0,j=L->length-1;
ElemType pivot=L->data[0]; //以data[0]为基准
while (i<j) //从区间两端交替向中间扫描,直至i=j为止
{ while (i<j && L->data[j]>pivot)
j--; //从右向左扫描,找一个小于等于pivot的元素
while (i<j && L->data[i]<=pivot)
i++; //从左向右扫描,找一个大于pivot的元素
if (i<j)
swap(L->data[i],L->data[j]);//将L->data[i]和L->data[j]进行交换
}
swap(L->data[0],L->data[i]); //将L->data[0]和L->data[i]进行交换
}
void move2(SqList *&L)
{ int i=0,j=L->length-1;
ElemType pivot=L->data[0]; //以data[0]为基准
while (i<j) //从顺序表两端交替向中间扫描,直至i=j为止
{ while (j>i && L->data[j]>pivot)
j--; //从右向左扫描,找一个小于等于pivot的data[j]
L->data[i]=L->data[j]; //找到这样的data[j],放入data[i]处
while (i<j && L->data[i]<=pivot)
i++; //从左向右扫描,找一个大于pivot的记录data[i]
L->data[j]=L->data[i]; //找到这样的data[i],放入data[j]处
}
L->data[i]=pivot;
printf("i=%d\n",i);
}
int main()
{
SqList *L;
ElemType a[]={1,9,8,7,6};
CreateList(L,a,5);
printf("L:");DispList(L);
printf("执行移动运算\n");
move1(L);
printf("L:");DispList(L);
DestroyList(L);
return 1;
}
例:将整数顺序表L中所有奇数移动到偶数的前面
#include "sqlist.cpp"
void swap(int &x,int &y) //交换x和y
{ int tmp=x;
x=y; y=tmp;
}
/*解法一*/
void move1(SqList *&L)
{
int i=0,j=L->length-1;
while (i<j)
{
while (i<j && L->data[j]%2==0)
j--; //从右向左扫描,找一个奇数元素
while (i<j && L->data[i]%2==1)
i++; //从左向右扫描,找一个偶数元素
if (i<j) //若i<j,将L->data[i]和L->data[j]交换
swap(L->data[i],L->data[j]);
}
}
/*解法二:*/
void move2(SqList *&L)
{ int i=-1,j;
for (j=0;j<=L->length-1;j++)
if (L->data[j]%2==1) //j指向奇数时
{
i++; //奇数区间个数增1
if (i!=j) //若i、j不相等
swap(L->data[i],L->data[j]);//L->data[i]和L->data[j]交换
}
}
int main()
{
SqList *L;
ElemType a[]={8,2,7,1,5,10,4,6,3,9};
CreateList(L,a,10);
printf("L:");DispList(L);
printf("执行移动运算\n");
move1(L);
printf("L:");DispList(L);
DestroyList(L);
return 1;
}