0 前言
线性表(亦作顺序表)是最基本、最简单、也是最常用的一种数据结构。线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的。线性表的逻辑结构简单,便于实现和操作。因此,线性表这种数据结构在实际应用中是广泛采用的一种数据结构。
1 结构
线性表是一种常用的数据结构,以下介绍线性表及其顺序存储,并对栈和队列及它们的顺序实现给出了详细的设计描述。
在实际应用中,线性表都是以栈、队列、字符串等特殊线性表的形式来使用的。由于这些特殊线性表都具有各自的特性,因此,掌握这些特殊线性表的特性,对于数据运算的可靠性和提高操作效率都是至关重要的。
线性表是一个线性结构,它是一个含有n≥0个结点的有限序列,对于其中的结点,有且仅有一个开始结点没有前驱但有一个后继结点,有且仅有一个终端结点没有后继但有一个前驱结点,其它的结点都有且仅有一个前驱和一个后继结点。一般地,一个线性表可以表示成一个线性序列:k1,k2,…,kn,其中k1是开始结点,kn是终端结点。
是一个数据元素的有序(次序)集
2特征
线性结构的基本特征为:
1.集合中必存在唯一的一个“第一元素”;
2.集合中必存在唯一的一个 “最后元素” ;
3.除最后一个元素之外,均有 唯一的后继(后件);
4.除第一个元素之外,均有 唯一的前驱(前件)。
由n(n≥0)个数据元素(结点)a1,a2,…,an组成的有限序列。
数据元素的个数n定义为表的长度。
当n=0时称为空表。
常常将非空的线性表(n>0)记作:
(a1,a2,…an)
数据元素ai(1≦i≦n)只是一个抽象的符号,其具体含义在不同的情况下可以不同。
线性表的基本操作
1)MakeEmpty(L) 这是一个将L变为空表的方法
2)Length(L) 返回表L的长度,即表中元素个数
3)Get(L,i) 这是一个函数,函数值为L中位置i处的元素(1≤i≤n)
4)Prev(L,i) 取i的前驱元素
5)Next(L,i) 取i的后继元素
6)Locate(L,x) 这是一个函数,函数值为元素x在L中的位置
7)Insert(L,i,x)在表L的位置i处插入元素x,将原占据位置i的元素及后面的元素都向后推一个位置
8)Delete(L,p) 从表L中删除位置p处的元素
9)IsEmpty(L) 如果表L为空表(长度为0)则返回true,否则返回false
10)Clear(L)清除所有元素
11)Init(L)同第一个,初始化线性表为空
12)Traverse(L)遍历输出所有元素
13)Find(L,x)查找并返回元素
14)Update(L,x)修改元素
15)Sort(L)对所有元素重新按给定的条件排序
16) strstr(string1,string2)用于字符数组的求string1中出现string2的首地址
3结构特点
线性表具有如下的结构特点:
1.均匀性:虽然不同数据表的数据元素可以是各种各样的,但对于同一线性表的各数据元素必定具有相同的数据类型和长度。
2.有序性:各数据元素在线性表中的位置只取决于它们的序号,数据元素之前的相对位置是线性的,即存在唯一的“第一个“和“最后一个”的数据元素,除了第一个和最后一个外,其它元素前面均只有一个数据元素直接前驱和后面均只有一个数据元素(直接后继)。
在实现线性表数据元素的存储方面,一般可用顺序存储结构和链式存储结构两种方法。链式存储结构将在本网站线性链表中介绍,本章主要介绍用数组实现线性表数据元素的顺序存储及其应用。另外栈、队列和串也是线性表的特殊情况,又称为受限的线性结构。
4 顺序存储
顺序存储就是采用一段连续的内存单元依次存储线性表的数据元素,顺序存储的线性表也称为顺序表,在顺序存储的方式中,逻辑上相邻的两个数据元素在物理位置上也是相等的,如表所示:
顺序表可以使用一维数组表示,其表示如下
#define MAXSIZE 100
DataType data[MAXSIZE];
int length
其中MAXSIZE决定了顺序表的最大内存空间,length为当前数据元素的个数,也就是顺序表元素的长度,length应小于或等于MAXSIZE,顺序表中的数据元素从data[0]开始一次存储,因此最后一个元素的下标为length-1.我们也可以将data和length进行封装,即定义一个结构体作为顺序表的类型,定义如下
typedef struct node
{
DataType data[MAXSIZE];
int length;
}sqlList;
这样我们就可以用sqlList来定义顺序存储的线性表了。
接下来我们写一个程序实现创建并初始化一个顺序表,然后将各个数据元素输出到屏幕。实现代码如下:
/*
* =====================================================================================
*
* Filename: sqlList.c
*
* Description:
*
* Version: 1.0
* Created: 2014年08月23日
* Revision: none
* Compiler: gcc
*
* Author: xinyi61
* Company:
*
* =====================================================================================
*/
#include
#include
#include
#define MAXSIZE 100
typedef struct node
{
int data[MAXSIZE];
int length;
}sqlList;
void initSqlList(sqlList *p, int n)
{
int i;
if(n > MAXSIZE)
{
printf("\033[47;31m error !\033[0m");
return ;
}
srand((unsigned)time(NULL));
for(i = 0; i < n; ++i)
{
p->data[i] = rand()%100;
}
p->length = n;
}
int main(int argc, char *argv[])
{
sqlList sl;
int i;
int n;
printf("\033[42;35m please input a number less than %4d\033[0m",MAXSIZE);
scanf("%4d",&n);
initSqlList(&sl,n);
printf("\033[44;32mElement (1 ~~%2d) is:\n\033[0m",sl.length);
for(i = 0; i < sl.length; ++i)
{
printf("%4d",sl.data[i]);
}
printf("\n");
return 0;
}
实验结果如下:
至此,我们对顺序表的创建有了相当的掌握了。接下来看看顺序表的一些操作。
5 插入数据元素
在线性表的第i个位置插入一个新的数据元素,将会使原来编号为i,i+1,i+2,...,n的数据元素变为编号为i+1,i+2,i+3,...,n+i,顺序表的插入前后如图所示:
插入的代码实现是:
void insertSqlList(sqlList *p, int n, int x)
{
int *p1, *p2;
if(n < 1 || n > p->length)
{
printf("\033[47;31m insert position error !\033[0m");
return ;
}
if(p->length >= MAXSIZE)
{
printf("\033[47;31mno enough space to insert an element\033[0m");
return ;
}
p1 = &(p->data[n]);
p2 = &(p->data[p->length]);
for(; p1 <= p2; --p2)
{
*p2 = *(p2 -1);
}
*p1 = x;
++p->length;
}
实验结果是:
6 删除数据元素
删除顺序表的第i个元素, 将会使原来编号为i+1,i+2,i+3,...,n+i的数据元素变为编号为i,i+1,i+2,...,n,顺序表的删除前后如图所示:
删除的代码实现为:
void deletSqlList(sqlList *p, int n, int *x)
{
int *p1, *p2;
if(n < 1 || n >= p->length)
{
printf("\033[47;31m delet position error !\033[0m");
return ;
}
p1 = &(p->data[n]);
p2 = &(p->data[p->length]);
*x = *p1;
for(; p1 < p2; ++p1)
{
*p1 = *(p1 + 1);
}
--p->length;
}
实验结果为:
至此关于线性表的顺序存储及简单实现就介绍完了,感兴趣的读者可以尝试其他方法实现。
备注1:
在最开始的代码中我使用了srand()、和rand()函数来实现随机数的产生,这里将解析下这两个函数的具体使用。
原型:
void
srand(unsigned seed);
用法:
srand和
rand()配合使用产生伪
随机数序列。
rand函数在产生随机数前,需要系统提供的生成伪随机数序列的种子,rand根据这个种子的值产生一系列随机数。如果系统提供的种子没有变化,每次调用rand函数生成的伪随机数序列都是一样的。srand(unsigned seed)通过参数seed改变系统提供的种子值,从而可以使得每次调用rand函数生成的伪随机数序列不同,从而实现真正意义上的“随机”。通常可以利用系统时间来改变系统的种子值,即srand(time(NULL)),可以为rand函数提供不同的种子值,进而产生不同的随机数序列 函数rand()是真正的随机数生成器,而srand()会设置供rand()使用的随机数种子。如果你在第一次调用rand()之前没有调用srand(),那么系统会为你自动调用srand()。而使用同种子相同的数调用 rand()会导致相同的随机数序列被生成。
原型为:int rand(void);
返回一个[0,
RAND_MAX]间的随机整数。
例如随机输出0~100的整数
#include/*用到了srand函数,所以要有这个头文件*/
#include
#define MAX 10
int main(int argc, char *argv[])
{
int number[MAX]={0};
int i;
unsigned int seed;
scanf("%d",&seed);/*手动输入种子*/
srand(seed);
for(i = 0;i < MAX; ++i)
{
number[i]=rand()%100;/*产生100以内的随机整数*/
printf("%d",number[i]);
}
printf("\n");
return0;
}或者是
#include
#include
#include /*用到了time函数,所以要有这个头文件*/
#define MAX 10
int main(int argc, char *argv[])
{
int number[MAX]={0};
int i;
srand((unsigned)time(NULL));/*播种子*/
for(i = 0;i < MAX; ++i)
{
number[i]=rand()%100;/*产生100以内的随机整数*/
printf("%d",number[i]);
}
printf("\n");
return 0;
}