数据结构与算法(二)

目录

线性表(一)

1.定义

2.数据类型

3.抽象

4.抽象数据类型

5.线性表的顺序存储结构

6.地址计算方法

7.插入操作


线性表(一)

1.定义

线性表(List):由零个或多个数据元素组成的有限序列。
这里需要强调几个关键的地方:
  1)首先它是一个序列,也就是说元素之间是有个先来后到的。
  2)若元素存在多个,则第一个元素无前驱,最后一个元素无后继,其他元素都有且只有一个前驱       和后继。
  3)另外,线性表强调是有限的,事实上无论计算机发展到多强大,它所处理的元素都是有限的。如果用数学语言来进行定义,可如下:
若将线性表记为(a1,.,ai-1,ai,ai+1...an)则表中ai-1领先于ai,ai领先于ai+1,称ai-1是ai的直接前驱元素,ai+1是ai的直接后继元素。


所以线性表元素的个数n(n>=0)定义为线性表的长度,当n=0时,称为空表

2.数据类型

是指一组性质相同的值的集合及定义在此集合上的一些操作的总称。例如很多编程语言的整型,浮点型,字符型这些指的就是数据类型。

例如在C语言中,按照取值的不同,数据类型可以分为两类:
 1)原子类型:不可以再分解的基本类型,例如整型、浮点型、字符型等。
 2)结构类型:由若干个类型组合而成,是可以再分解的,例如整型数组是由若干整型数据组成的.

3.抽象

     是指抽取出事物具有的普遍性的本质。它要求抽出问题的特征而忽略非本质的细节,是对具体事物的一个概括。抽象是一种思考问题的方式,它隐藏了繁杂的细节。
    我们对已有的数据类型进行抽象,就有了抽象数据类型。

4.抽象数据类型

1)抽象数据类型(Abstract Data Type, ADT)是指一个数学模型及定义在该模型上的一组操作。抽象数据类型的定义仅取决于它的一组逻辑特性而与其在计算机内部如何表示和实现无关。
2)抽象的意义在于数据类型的数学抽象特性。
3)而且,抽象数据类型不仅仅指那些已经定义并实现的数据类型,还可以是计算机编程者在设计软件程序时自己定义的数据类型。

4)抽象数据类型的标准格式:

ADT抽象数据类型名
Data
数据元素之间逻辑关系的定义
Operation
操作
endADT

5)线性表的抽象数据类型定义:

ADT线性表(List)
Data
线性表的数据对象集合为{a1,a....,an},每个元素的类型均为DataType。其中,除第一个元素a1外,每一个元素有且只有一个直接前驱元素,除了最后一个元素an外,每一个元素有且只有一个直接后继元素。数据元素之间的关系是一对一的关系。

Operation
InitList(*L):初始化操作,建立一个空的线性表L。

ListEmpty(L):判断线性表是否为空表,若线性表为空返回true,否则返回false。
ClearList(*L):将线性表清空。
GetElem(L,i,*e):将线性表L中的第i个位置元素值返回给e。
LocateElem(L,e):在线性表L中查找与给定值e相等的元素,如果查找成功,返回该元素在表中序号表示成功。否则,返回0表示失败。

ListInsert(*L,i,e):在线性表L中第i个位置插入新元素e。
ListDelete(*L,i,*e):删除线性表L中第i个位置元素并用e返回其值。
ListLength(L):返回线性表L的元素个数。
endADT
对于不同的应用,线性表的基本操作是不同的,上述操作是最基本的,对于实际问题中涉及的关于线性表的更复杂操作,完全可以用这些基本操作的组合来实现。

// La表示A集合,Lb表示B集合。
void unionL(List *La, list L) 
{
   int La_len,Lb_len,i;
   Elemype e;
   La_len = Listlength(*La);
   Lb_len = Listlength(Lb);
   for( i=1; i <= Ib_len; i++ )
   {
     GetElem(Lb, i, &e);
     if( !LocateElem(*La, e) )
        {
          ListInsert(La, ++La_ len, e);
        }
   }
}

5.线性表的顺序存储结构

我们可以想象,线性表有两种物理存储结构:顺序存储结构链式存储结构
线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表的数据元素。
线性表(a1,a2...,an)的顺序存储如下:

#define MAXSIZE 20

typedef int ElemType;

typedef struct

{

   ElemType data [MAXSIZE];

   int length; //线性表当前长度

} SqList;

总结下,顺序存储结构封装需要三个属性:
1)存储空间的起始位置,数组data,它的存储位置就是线性表存储空间的存储位置

2)线性表的最大存储容量:数组的长度MaxSize。

3)线性表的当前长度:length。
注意,数组的长度与线性表的当前长度需要区分一下:数组的长度是存放线性表的存储空间的总长度,一般初始化后不变。而线性表的当前长度是线性表中元素的个数,是会变化的。

6.地址计算方法

所以对于第i个数据元素ai的存储位置可以由a1推算得出:LOC(ai) = LOC(a1) + (i-1) *c结合下图来理解:


通过这个公式,我们可以随时计算出线性表中任意位置的地址,不管它是第一个还是最后一个,都是相同的时间。那么它的存储时间性能当然就为0(1),我们通常称为随机存储结构。

typedef int Status;

// status 是函数的类型,其值是函数结果状态代码,如oK等。
// 初始条件:顺序线性表1已存在,1 <= i <= ListIength (L)
// 操作结果:用e返回L中第i个数据元素的值。

Status GetElem(SqList L, int i, ElemType *e)

{
   if( L.length==0 || i<1 || i>L.length )
    {
       return ERROR;
    }
    *e = L.data[i-1];
    return OK;
}

7.插入操作

刚才我们也谈到,线性表的顺序存储结构具有随机存储结构的特点,时间复杂度为0(1)。
大家现在来考虑下,如果我们要实现
ListInsert(*L, i, e),即在线性表L中的第i个位置插入新元素e,代码应该如何写?

/*初始条件: 顺序线性表L已存在,1<=i<=ListLength(L). */

/*操作结果:在I中第i个位置之前插入新的数据元素e,L长度+1。*/

Status ListLnsert (SqList *L, int i, ElemType e)
{
    int k;

    if( L->length =MAXSIZE ) // 顺序线性表已经满了
    {
         return ERROR;
    }
    if( i<1 || i>L->length+1) // 当i不在范围内时
    {
         return ERROR;
    }
    if( i <= L->length )//若插入数据位置不在表尾
    {
         /*将要插入位置后数据元素向后移动一位*/
         for( k=L->length-1; k >= i-1; k-- )
         {
             L->data[k+1] = L->data[k];
         }
    }
    L->data[i-1] = e; // 将新元素插入
    L->length++;
    return OK;
}

所以插入算法的思路:
1)如果插入位置不合理,抛出异常;
2)如果线性表长度大于等于数组长度,则抛出异常或动态增加数组容量;
3)从最后一个元素开始向前遍历到第i个位置,分别将它们都向后移动一个位置;
4)将要插入元素填入位置i处;
5)线性表长+1。
实现代码: ListInsert.c
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值