一、线性表的顺序存储
1、线性表的顺序存储原理
线性表的顺序存储(Sequential Mapping,简称顺序表),是指用一组地址连续的存储单元按线性表元素之间的逻辑顺序,依次存储线性表的数据元素。数据元素的逻辑顺序和物理上的存储顺序是完全一致的,物理上存放在位置 i 的元素,就是按照逻辑顺序存储时的第 i 个元素。因此在顺序存储结构下不需要另外建立空间来记录各个元素之间的关系。顺序存储的线性表是一种随机存取结构,因为只要确定了存储线性表的起始位置,就可以随机存取表中的任意一个数据元素。
2、顺序存储的线性表类型定义
由于C语言的一维数组在内存中所占的正是一个地址连续的存储区域,因此可以用C语言的一维数组来作为线性表的顺序存储结构。但是,由于在大多数高级程序设计语言中,数组的长度是不可变的,因而如果用数组类型来实现顺序表,则必须根据需要预先设置足够的长度。而在实际应用中,数组所需长度会随问题的不同而不同,并且在操作过程中长度也会发生变化,因此,在C语言中通常采用动态分配的一维数组来实现顺序表。顺序存储的线性表类型定义如下:
#define INIT_SIZE 5 /*线性表存储空间的初始分配量*/
#define INCREMENT 2 /*线性表存储空间的分配增量*/
typedef int ElemType; /*定义元素类型为int*/
/*线性表的顺序存储结构定义*/
typedef struct {
ElemType* elem; /*存储空间的基地址*/
int length; /*当前长度*/
int listsize; /*当前分配的存储容量(以sizeof(ElemType)为单位)*/
}SqList;
二、顺序表的基本操作
1、顺序表的初始化
顺序表的初始化,即构造一个空的表,就是为顺序表分配一个预定义大小的数组空间,这需要将 L 设为指针变量。首先动态分配顺序表的存储空间,然后将其当前长度设为 “0” ,表示表中没有数据元素。
/*顺序表的初始化*/
/*初始化顺序表,成功返回1,失败返回-1*/
int InitList_Sq(SqList* L)
{
L->elem = (ElemType*)malloc(INIT_SIZE * sizeof(ElemType));
if (!L->elem) return -1; /*初始化失败*/
L->length = 0;
L->listsize = INIT_SIZE;
return 1; /*初始化成功*/
}
2、顺序表的插入
一般情况下,在第 i ( 0≤ i ≤n ) 个元素之前插入一个元素时,需将第 n-1至第 i (共n-i)个元素向后移动一个位置。
(1)从后开始向后移动:将 an-1 ~ ai 顺序向后移动一个位置,即 an-1 移到 an 的位置, an-2 移到 an-1 的位置,…, ai 移到 ai+1 的位置,为待插入的新数据元素让出位置 i。
(2)将 e 放到空出的第 i 个位置上。
(3)修改线性表的当前长度 length 的值。
/*顺序表的插入*/
/*在顺序表L的第i个位置之前插入新的元素e,否成功则返回1,否则返回-1*/
int ListInsert_Sq(SqList* L, int i, ElemType e)
{
int j;
ElemType* newbase;
if (i<0 || i>L->length)
return -1; /*插入位置不合法*/
if (L->length >= L->listsize) /*当前存储空间已满,增加分配*/
{
newbase = (ElemType*)realloc(L->elem, (L->listsize + INCREMENT) * sizeof(ElemType));
if (!newbase)
return -1; /*存储分配失败*/
L->elem = newbase;
L->listsize += INCREMENT;
}
for (j = L->length - 1; j >= i; j--)
L->elem[j + 1] = L->elem[j]; /*插入位置及之后的元素后移*/
L->elem[i] = e; /*插入e*/
++L->length; /*表长增1*/
return 1;
}
3、顺序表的删除
(1)从前开始向前移动:将 ai+1 ~ an-1 顺序向前移动一个位置。
(2)修改线性表当前长度 length 的值。
/*顺序表的删除*/
/*在顺序表L中删除第i个元素,并用e返回其值,若成功则返回1,否则返回-1*/
int ListDelete_Sq(SqList* L, int i, ElemType* e)
{
int j;
if (i < 0 || i >= L->length)
return -1; /*i值不合法*/
*e = L->elem[i]; /*被删除元素的值赋给e*/
for (j = i + 1; j < L->length; j++)
L->elem[j - 1] = L->elem[j]; /*被删元素之后的元素前移*/
--L->length;
return 1;
}
4、顺序表的按值查找
从最前面一个元素 a0 开始向后依次将顺序表中的元素 ai 与 e 相比较,直到找到一个与 e 相等的数据元素,返回这个数据元素在表中的位置;若顺序表中的所有元素都与 e 不相等,即查找不到与 e 相等的数据元素,则返回 -1,表示查找失败。
/*顺序表的查找*/
/*在顺序线性表L中查找第i个值与e相等的元素的位序,若找到,则返回其在L中的位序,否则返回-1*/
int LocateElem_Sq(SqList L, ElemType e)
{
int i = 0; /*i的初值为第一个元素的位序*/
while (i < L.length && L.elem[i] != e)
i++;
if (L.elem[i] == e)
return i;
else
return -1;
}
5、读取顺序表中的元素
首先确认所查找的数据元素序号是否合法,若合法,则直接返回对应的元素值,否则操作失败。
/*读取顺序表中的元素*/
/*在顺序线性表L中取第i个元素存入e中,若成功,则返回1,否则返回-1*/
int Get_SqList(SqList L, int i, ElemType* e)
{
if (i < 0 || i >= L.length)
return -1; /*没有第i个元素,读取失败*/
*e = L.elem[i];
return 1; /*读取成功*/
}
6、顺序表的应用
例:有顺序表 LA 和 LB,其元素均按从小到大的升序排列,编写一个算法,将它们合并成一个顺序表 LC,要求 LC 的元素也按从小到大的升序排列。
(1)算法思路:
依次扫描 LA 和 LB 的元素,比较线性表 LA 和 LB 当前元素的值,将较小值的元素赋给 LC,如此直到一个线性表扫描完毕,然后将未扫描完的那个顺序表中余下的元素赋给 LC 即可。因此线性表 LC 的容量应不小于线性表 LA 和 LB 的长度之和。
(2)算法描述:
/*已知顺序线性表LA和LB的元素按值非递减排列,合并LA和LB得到新的顺序
线性表LC,LC的元素也按值非递减排列,若成功,则返回1,否则返回-1*/
/*已知顺序线性表LA和LB的元素按值非递减排列,合并LA和LB得到新的顺序
线性表LC,LC的元素也按值非递减排列,若成功,则返回1,否则返回-1*/
int MergeList_Sq(SqList LA, SqList LB, SqList* LC)
{
int i = 0, j = 0, k = 0;
LC->listsize = LA.length + LB.length;
LC->elem = (ElemType*)malloc(LC->listsize * sizeof(ElemType));
if (!LC->elem) return -1;
while (i < LA. length && j < LB.length)
{
if (LA.elem[i] <= LB.elem[j])
LC->elem[k++] = LA.elem[i++];
else
LC->elem[k++] = LB.elem[j++];
}
while (i < LA.length) LC->elem[k++] = LA.elem[i++];
while (j < LB.length) LC->elem[k++] = LB.elem[j++];
LC->length = k;
return 1;
}
三、顺序表的练习
1、完整代码
//实现顺序表常用操作的代码
#include <stdio.h>
#include <stdlib.h>
#define INIT_SIZE 5 /*线性表存储空间的初始分配量*/
#define INCREMENT 2 /*线性表存储空间的分配增量*/
typedef int ElemType; /*定义元素类型为int*/
/*线性表的顺序存储结构定义*/
typedef struct {
ElemType* elem; /*存储空间的基地址*/
int length; /*当前长度*/
int listsize; /*当前分配的存储容量(以sizeof(ElemType)为单位)*/
}SqList;
/*顺序表的初始化*/
/*初始化顺序表,成功返回1,失败返回-1*/
int InitList_Sq(SqList* L)
{
L->elem = (ElemType*)malloc(INIT_SIZE * sizeof(ElemType));
if (!L->elem)
return -1;
L->length = 0;
L->listsize = INIT_SIZE;
return 1;
}
/*输入顺序表各个元素的值*/
void InputSqList(SqList* L)
{
int i, n;
printf("请输入该线性表的元素个数:n=");
scanf("%d", &n);
while (n > L->listsize)
{
printf("超出了线性表的存储空间,请重新输入:\n");
scanf("%d", &n);
}
L->length = n;
printf("请依次输入该线性表各元素的值:\n");
for (i = 0; i < n; i++)
scanf("%d", &L->elem[i]);
}
/*打印顺序表的各个元素*/
void PrintSqList(SqList L)
{
int i;
printf("\n该线性表的元素依次为:\n");
for (i = 0; i < L.length; i++)
printf("%d ", L.elem[i]);
printf("\n");
}/*顺序表的插入*/
/*在顺序表L的第i个位置之前插入新的元素e,否成功则返回1,否则返回-1*/
int ListInsert_Sq(SqList* L, int i, ElemType e)
{
int j;
ElemType* newbase;
if (i<0 || i>L->length)
return -1; /*插入位置不合法*/
if (L->length >= L->listsize) /*当前存储空间已满,增加分配*/
{
newbase = (ElemType*)realloc(L->elem, (L->listsize + INCREMENT) * sizeof(ElemType));
if (!newbase)
return -1; /*存储分配失败*/
L->elem = newbase;
L->listsize += INCREMENT;
}
for (j = L->length - 1; j >= i; j--)
L->elem[j + 1] = L->elem[j]; /*插入位置及之后的元素后移*/
L->elem[i] = e; /*插入e*/
++L->length; /*表长增1*/
return 1;
}
/*顺序表的删除*/
/*在顺序表L中删除第i个元素,并用e返回其值,若成功则返回1,否则返回-1*/
int ListDelete_Sq(SqList* L, int i, ElemType* e)
{
int j;
if (i < 0 || i >= L->length)
return -1; /*i值不合法*/
*e = L->elem[i]; /*被删除元素的值赋给e*/
for (j = i + 1; j < L->length; j++)
L->elem[j - 1] = L->elem[j]; /*被删元素之后的元素前移*/
--L->length;
return 1;
}
/*顺序表的查找*/
/*在顺序线性表L中查找第i个值与e相等的元素的位序,若找到,则返回其在L中的位序,否则返回-1*/
int LocateElem_Sq(SqList L, ElemType e)
{
int i = 0; /*i的初值为第一个元素的位序*/
while (i < L.length && L.elem[i] != e)
i++;
if (L.elem[i] == e)
return i;
else
return -1;
}
/*读取顺序表中的元素*/
/*在顺序线性表L中取第i个元素存入e中,若成功,则返回1,否则返回-1*/
int Get_SqList(SqList L, int i, ElemType* e)
{
if (i < 0 || i >= L.length)
return -1; /*没有第i个元素,读取失败*/
*e = L.elem[i];
return 1; /*读取成功*/
}
int main(void)
{
SqList L;
int status = 0, d = 0, e = 0, f = 0, i = 0;
status = InitList_Sq(&L);
if (status == 1) printf("顺序表初始化成功!\n");
else
{
printf("顺序表初始化失败!");
return 0;
}
InputSqList(&L); /*输入顺序表的各元素值*/
PrintSqList(L);
printf("\n请输入插入位置:");
scanf("%d", &i);
printf("请输入插入的值:");
scanf("%d", &d);
status = ListInsert_Sq(&L, i, d); /*在第i个元素之前插入d*/
if (status == 1)
{
printf("\n进行插入操作后");
PrintSqList(L);
}
else
{
printf("\n插入失败!\n");
}
printf("\n请输入删除位置:");
scanf("%d", &i);
status = ListDelete_Sq(&L, i, &e); /*删除线性表的第i个元素,用e返回其值*/
if (status == 1)
{
printf("\n被删除元素的值为:%d", e);
printf("\n\n进行删除操作后");
PrintSqList(L);
}
else
{
printf("删除失败!\n");
}
printf("\n请输入待查找元素的值:");
scanf("%d", &f);
i = LocateElem_Sq(L, f);
/*在顺序表L中查找与f相等的元素的位序*/
if (i != -1)
{
printf("其值与%d相等的元素在线性表中的位序为:%d\n", f, i);
}
else
{
printf("查找%d失败!\n", f);
}
printf("\n请输入读取元素的位置:");
scanf("%d", &i);
status = Get_SqList(L, i, &e);
if (status == 1)
{
printf("\n读取相应位置的元素的值为:%d\n", e);
}
else
{
printf("读取失败!\n");
}
free(L.elem);
return 0;
}