数据结构复习(一)线性表

学习目标:

  • 复习线性表

学习内容:

  1. 线性表的类型定义
  2. 线性表的顺序表示和实现
  3. 线性表的链式表示和实现

开始学习:

1.线性表的类型定义

  线性表是是最常用且最简单的一种数据结构。线性表示一个相当灵活的数据结构,它的长度可根据需要增长或缩短,即对数据元素不仅可以进行访问,还可进行插入和删除等操作。

  抽象数据类型定义如下:

ADT 线性表(List)
Data
    数据对象为{a1, a2, ... , an}的集合,每个元素的类型相同。
Operation
    InitList(*L)
        操作结果:构造一个空的线性表 L。
    DestroyList(*L)
        初始条件:线性表 L 已经存在。
        操作结果:销毁线性表 L。
    ClearList(*L)
        初始条件:线性表 L 已经存在。
        操作结果:将 L 重置为空表。
    ListEmpty(L)
        初始条件:线性表 L 已经存在。
        操作结果:若 L 为空,则返回 true,否则返回 false。
    ListLength(L)
        初始条件:线性表 L 已经存在。
        操作结果:返回 L 中数据元素的个数。
    GetElem(L, i, *e)
        初始条件:线性表 L 已经存在,且 1 ≤ i ≤ ListLength(L)。
        操作结果:返回 L 中第 i 个数据元素。
    LocateElem(L, e)
        初始条件:线性表 L 已经存在。
        操作结果:返回 L 中第一个出现数据元素 e 的位置,若数据元素 e 不存在,则返回 0。
    PriorElem(L, cur_e, *pre_e)
        初始条件:线性表 L 已经存在。
        操作结果:若 cur_e 不是 L 的第一个元素,则返回 true 和 cur_e 的前驱(赋值给 pre_e),否则返回 false,这时 pre_e 为空。
    NextElem(L, cur_e, *next_e)
        初始条件:线性表 L 已经存在。
        操作结果:若 cur_e 不是 L 的最后一个元素,则返回 true 和 cur_e 的后继(赋值给 next_e),否则返回 false,这时 pre_e 为空。
    ListInsert(*L, i, e)
        初始条件:线性表 L 已经存在,且 1 ≤ i ≤ ListLength(L)+1。
        操作结果:在 L 中第 i 个位置之前插入新的数据元素 e,L 的长度加 1。
    ListDelete(*L, i, *e)
        初始条件:线性表 L 已经存在,且 1 ≤ i ≤ ListLength(L)。
        操作结果:删除 L 中第 i 个位置的数据元素,并用 e 返回其值,L 的长度减 1。
endADT

2.线性表的顺序表示和实现

  线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素。

  假设线性表的每个元素需占用l个存储单元,并以所占的第一个单元的存储地址作为数据元素的存储位置。则线性表中第i+1个数据元素的存储位置LOC(a__{i+1})和第i个数据元素的存储位置LOC(a_i)之间满足下列关系:

LOC(a_{i+1})=LOC(a_i)+l

  一般来说,线性表的第i个数据元素a_i的存储位置为:

LOC(a_i)=LOC(a_1)+(i-1)\times l

  由于高级程序设计语言中的数组类型也有随机存取的特性,因此,通常都使用数组来描述数据结构中的顺序存储结构。在此,由于线性表的长度可变,且所需最大存储空间随着问题的不同而不同,则在C语言中可用动态分配的一维数组,如下描述:

#define LIST_INIT_SIZE 100 //线性表存储空间的初始分配量
#define LISTINCREMENT 10 //线性表存储空间的分配增量
typedef int ElemType ;//数据类型定义
typedef struct {
    ElemType *elem; //存储空间基址
    ElemType length; //当前长度
    ElemType listsize; //当前分配的存储容量(以sizeof(ElemType)为单位)
}SqList;

(1) 顺序表的初始化

在上述定义中,数组指针elem指示线性表的基础地址,length表示当前线性表的长度,Listsize指示顺序表当前分配的存储空间的大小,一旦因为插入元素而空间不足时,可以进行再分配,即为顺序表增加一个大小为存储LISTINCREMENT个数据元素的空间。

Status InitList_Sq(SqList &L){
    L.elem=(ElemType *) malloc(LIST_INIT_SIZE*sizeof (ElemType));
    if(!L.elem)exit(OVERFLOW); //存储分配失败
    L.length=0;//空表长度为0
    L.listsize=LIST_INIT_SIZE;//初始存储容量
    return OK;
}//InitList_Sq

(2)顺序表的插入操作 

 在这种存储结构中,容易实现线性表的某些操作,如随机存取第i个数据元素等,只是要特别注意的是,C语言中数组的下标从“0”开始,因此,若L是SqList类型的顺序表,则表中的第i个数据元素是L.elem[i-1]。下面讨论线性表的插入和删除两种操作在顺序存储表示时的实现方法。

  其大致的原理是,在第i个元素处插入元素,如果i=n+1,则不需要移动元素,如若不然,需要移动元素才能反应这个逻辑关系的变化。一般情况下,在第(1<=i<=n)个元素之前插入一个元素时,需要将第n至第i(共n-i+1)个元素向后移动一个位置,算法如下:

Status ListInsert_Sq(SqList &L,int i,ElemType e){
    //在顺序线性表L中第i个位置之前插入新的元素e。
    //i的合法值为1<=i<=ListLength_Sq(L)+1
    if (i<1||i>L.length+1) return ERROR; //i值不合法
    if(L.length>=L.listsize){ //当前存储空间已满,增加分配
        int * newbase=(ElemType*) realloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(ElemType));
        if(!newbase)exit(OVERFLOW);//内存分配失败
        L.elem=newbase;//引入新的基址
        L.listsize+=LISTINCREMENT;//分配增量
    }
    int *q=&(L.elem[L.length - 1]);//确定插入位置
    for(int *p=&(L.elem[L.length - 1]); p >= q; --p) *(p + 1)=*p;//将元素向后移动
    *q=e;//插入元素
    ++L.length;//表长增1
    return OK;
}

(3)顺序表的删除操作

一般情况下,删除 第i个元素时,需将从第i+1至第n个元素依次向前移动一个位置,代码实现如下:

Status ListDelete_Sq(SqList &L,int i,ElemType &e){
    if((i<1)||(i>L.length))return ERROR;
    int *p=&(L.elem[i-1]);
    e=*p;
    int *q=L.elem+L.length-1;
    for(++p;p<=q;++p)*(p-1)=*p;
    --L.length;
    return OK;
}

3.线性表的链式表示和实现

(1)线性链表的定义

  线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的。)因此,为了表示每个数据元素a_i与其直接后继数据元素a_{i+1}之间的逻辑关系,对于数据元素a_i来说,除了存储本身的信息之外,还需要存储一个指示其直接后继的信息(即直接后继的存储位置)。这两部分信息组成数据元素a_i的存储映像,称为结点。它包括两个域:其中存储数据元素信息的域成为数据域;存储直接后继的域成为指针域。指针域中存储的信息称作指针。n个结点链结成一个链表,为线性表的链式存储结构。又由于此链表的每个节点中只包含一个指针域,则又称为线性链表或者单链表

(2)线性链表方法实现

  线性链表定义如下:

typedef struct LNode{
    ElemType data;
    struct LNode *next;

}LNode,*LinkList;

初始化如下:

Status InitList(LinkList *L) {
    *L = (LinkList) malloc(sizeof(Node));/*新建新结点作为头结点,用头指针L指向头结点*/
    (*L)->next = NULL;/*头结点的指针域置空*/
    return OK;
}

 后插法:

//后插法
void CreateList_Tail(LinkList &L,LinkList &p,int n)
{
	LinkList r;//尾结点 
	L = new LNode;
	L->next = NULL;
	r =L;//之所以用尾结点,是因为L是指向头节点的 过L来找到这条单链表的,r是一个代替L移动操作的指针
	for ( int i=0; i<n; i++ )
	{
		p = new LNode;
		cin>>p->data;
		p->next = NULL;
		r->next = p;
		r = p;
	}
}

 前插法:

void CreateList_Head(LinkList &L,LinkList &p,int n)
{
	L = new LNode;
	L->next =  NULL;
	
	for( int i=0; i<n; i++ )
	{
		p = new LNode;
		cin>>p->data;
		p->next = L->next;
		L->next = p;
	}
}

猜你喜欢

转载自blog.csdn.net/m0_49684834/article/details/128215631
今日推荐