线性表的抽象数据类型描述:
类型名称:线性表
数据对象集:线性表是具有相同数据类型的n(n >= 0)个数据元素的有限序列。
操作集:(其中整数i表示位置,ElementType表示数据元素的类型,ptrl表示指向线性表的指针)
List Make_Empty( ); //创建空表
int find( ElementType x, List ptrl ); //查找元素,返回下标,若没找到返回-1
void insert( ElementType x, int i, List ptrl ); //插入函数,在第i个位置插入新元素
void Delete( int i, List ptrl ); //删除线性表中第i个元素
int length ( List ptrl ); //返回表长(即线性表中数据元素的个数)
ElementType find_k( int i, List ptrl ); //返回线性表中第i个元素
int Empty( List ptrl ); //判断表空,如果表空,返回0;表不空,返回1
void PrintList( List ptrl, int m, int n ); //打印从位置m到位置n的所有元素
void DestoryList( ptrl ); //销毁线性表并释放空间
线性表的逻辑特性:
线性表中数据元素的个数n称为表长,当n = 0时,称为空表,其中表的第一个数据元素称为表头元素,最后一个数据元素称为表尾元素,线性表中除了表头元素之外的每一个数据元素都有前驱元,除了表尾元素之外每一个数据元素都有后驱元。
线性表的特点:(王道总结)
(1)表中数据元素个数有限;
(2)表中元素在逻辑上有先后次序;
(3)表中元素都是数据元素,每个元素都是单个元素;
(4)表中元素的数据类型都相同,每个元素占用相同的内存空间;
(5)表中元素具有抽象性,仅讨论元素之中的逻辑关系,不考虑元素究竟表示什么内容。
(6)表中元素是一对一的相邻关系;
注意:顺序表和链表指的是线性表的存储结构,而线性表本身是逻辑结构。
线性表的顺序存储(C语言:数组):
线性表的顺序存储特点是表中数据元素的逻辑顺序和物理顺序相同。
顺序表的存储密度高,每个节点上只存储数据元素。
存储密度:在计算机中是指结点数据本身所占的存储量和整个结点结构所占的存储量之比,计算公式:存储密度 = (结点数据本身所占的存储量)/(结点结构所占的存储总量)。这里的结构一般指的是数据结构,主要通过计算机中数据的存储结构来影响存储密度。(来自百度百科)
具体实现如下(数组静态分配):
//线性表的数组实现
#include <stdio.h>
#include <stdlib.h>
#define ElementType int
#define Maxsize 10
typedef struct LNODE{
ElementType data[ Maxsize ];
int len;
}node;
typedef node* List;
List Make_Empty( ); //创建空表
int find( ElementType x, List ptrl ); //查找元素,返回下标,若没找到返回-1
void insert( ElementType x, int i, List ptrl ); //插入函数,在第i个位置插入新元素
void Delete( int i, List ptrl ); //删除线性表中第i个元素
int length ( List ptrl ); //返回表长(即线性表中数据元素的个数)
ElementType find_k( int i, List ptrl ); //返回线性表中第i个元素
int Empty( List ptrl ); //判断表空,如果表空,返回0;表不空,返回1
void PrintList( List ptrl, int m, int n ); //打印从位置m到位置n的所有元素
int main ( void )
{
List ptrl;
ptrl = Make_Empty( );
int i;
for( i = 1; i < Maxsize; i++ ){
ptrl -> data[i - 1] = i;
ptrl -> len++;
}
if( Empty( ptrl ) == 0 )
printf( "表空\n" );
else
printf( "表不空\n" );
int len = length( ptrl );
printf( "表长len = %d\n", len );
int index = find( 8, ptrl );
printf( "8的下标index = %d\n", index );
ElementType m = find_k( 5, ptrl );
printf( "第5个位置上的元素m = %d\n", m );
PrintList( ptrl, 1, len );
insert( 88, 9, ptrl );
PrintList( ptrl, 1, len + 1 );
Delete( 6, ptrl );
PrintList( ptrl, 1, len );
return 0;
}
List Make_Empty( )
{
List ptrl;
ptrl = ( List )malloc( sizeof( node ) );
if( ptrl == NULL )
printf( "申请空间出错\n" );
ptrl -> len = 0;
return ptrl;
}
int find( ElementType x, List ptrl )
{
int j = 0;
while( j <= ptrl -> len - 1 && ptrl -> data[j] != x )
j++;
if( j > ptrl -> len - 1 )
return -1;
else
return j + 1;
}
void insert( ElementType x, int i, List ptrl )
{
if( ptrl -> len == Maxsize ){
printf( "表满\n" );
return ;
}
if( i < 1 || i > ptrl -> len + 1 ){
printf( "位置不合法\n" );
return ;
}
int j;
for( j = ptrl -> len - 1; j >= i - 1; j-- ){
ptrl -> data[j + 1] = ptrl -> data[j];
}
ptrl -> data[i - 1] = x;
ptrl -> len++;
return ;
}
void Delete( int i, List ptrl )
{
int j;
if( i < 1 || i > ptrl -> len ){
printf( "位置不合法\n" );
return;
}
for( j = i ; j <= ptrl -> len - 1; j++ )
ptrl -> data[j - 1] = ptrl -> data[j];
ptrl -> len--;
return ;
}
int length ( List ptrl )
{
return ptrl -> len;
}
void PrintList( List ptrl, int m, int n )
{
int i;
for( i = m; i <= n; i++ )
printf( "%d ", find_k( i, ptrl ) );
printf( "\n" );
}
ElementType find_k( int i, List ptrl )
{
return ptrl -> data[i - 1];
}
int Empty( List ptrl )
{
return !( ptrl -> len == 0 );
}
我在主函数中做了一些测试,用来检验函数的正确性,在整个代码中需要注意一个问题,就是在线性表中元素的序号使用的从1开始的自然序列,而在实现的过程中数组的下标是从0开始的。
从上面可以看出来,顺序表(数组实现)的方法在插入和删除操作上花费的时间代价是昂贵的,平均来看,这两种运算都需要移动表的一半的元素,因此需要线性时间(即O(n)),但顺序表在查找上比较方便,通过首地址和元素序号就可在O(1)的时间内找到指定的元素,只通过相继插入来建立一个表需要O(n^2)的时间。
顺序表(数组)的动态分配实现只需要把上面静态实现的相对应语句改成下面语句:
typedef struct LNODE{
ElementType *data;
int len;
}node;
List Make_Empty( )
{
List ptrl;
ptrl = ( List )malloc( sizeof( node ) );
if( ptrl == NULL )
printf( "申请空间出错\n" );
ptrl -> data = ( ElementType *)malloc( sizeof( ElementType ) * Maxsize );
if( ptrl -> data == NULL )
printf( "申请空间出错\n" );
ptrl -> len = 0;
return ptrl;
}
这样就可以实现数组分配的空间大小可以在运行时决定。