[数据结构]线性表(二)

来源:互联网 发布:源码屋 编辑:程序博客网 时间:2024/06/10 04:18

第二章 线性表

2.3 线性表的链式存储结构

一、单链表

    用一组任意的存储单元存储线性表的数据元素。

以元素(数据元素的映象)

             + 指针(指示后继元素存储位置)

     =  结点

       (表示数据元素  或  数据元素的映象)

 

 

 

   以线性表中第一个数据元素  的存储地址作为线性表的地址,称作线性表的头指针。

   有时为了操作方便,在第一个结点之前虚加一个“头结点”,以指向头结点的指针为链表的头指针。

二、结点和单链表的 C 语言描述

typedef struc LNode{  // 定义单链表结点

     ElemType    data;

     struct LNode   *next;   // 指向后继的指针域

}LNode, *LinkList

 

(*p)表示p所指向的结点

(*p).dataÛp->data表示p指向结点的数据域

(*p).nextÛp->next表示p指向结点的指针域

三、单链表操作的实现

GetElem(L, i, e)    // 取第i个数据元素

ListInsert(&L, i, e)    // 插入数据元素

ListDelete(&L, i, e)    // 删除数据元素

ClearList(&L)      // 重置线性表为空表

CreateList(&L, n)    // 生成含 n 个数据元素的链表

 

线性表的操作  

        GetElem(L, i, &e)

在单链表中的实现:

 

      单链表是一种顺序存取的结构,为找第 i 个数据元素,必须先找到第 i-1 个数据元素

     因此,查找第 i 个数据元素的基本操作为:移动指针,比较 j 和 i 。

     令指针 p 始终指向线性表中第 j 个数据元素。

Status GetElem_L(LinkList L, int i, ElemType &e) {

   // L是带头结点的链表的头指针,以 e 返回第 i 个元素

p = L->next;   j = 1;  // p指向第一个结点,j为计数器

while (p && j<i)  { p = p->next;  ++j;  }

        // 顺指针向后查找,直到 p 指向第 i 个元素

    // 或 p 为空

if ( !p || j>i )

    return ERROR;      //  第 i 个元素不存在

e = p->data;                 //  取得第 i 个元素

return OK;

} // GetElem_L                  算法时间复杂度为:O(ListLength(L))

 

线性表的操作ListInsert(&L, i, e)

   在单链表中的实现

有序对 <ai-1, ai>改变为 <ai-1, e> 和<e, ai>

 

   可见,在链表中插入结点只需要修改指针。但同时,若要在第 i 个结点之前插入元素,修改的是第 i-1 个结点的指针。

   因此,在单链表中第 i 个结点之前进行插入的基本操作为:找到线性表中第i-1个结点,然后修改其指向后继的指针。

Status ListInsert_L(LinkList L, int i, ElemType e) {

 // L 为带头结点的单链表的头指针,本算法

// 在链表中第i 个结点之前插入新的元素 e

p = L;    j = 0;

while (p && j < i-1)

     { p = p->next;  ++j; }   // 寻找第 i-1 个结点

if (!p || j > i-1)

      return ERROR;      // i 大于表长或者小于1

……

} // LinstInsert_L

算法的时间复杂度为:O(ListLength(L))

s = (LinkList) malloc ( sizeof (LNode));

                               // 生成新结点

s->data = e;

s->next = p->next;      p->next = s; // 插入

return OK;

 

线性表的操作ListDelete (&L, i, &e)

     在链表中的实现:

     有序对<ai-1, ai> 和 <ai, ai+1>

     改变为 <ai-1, ai+1>

 

   在单链表中删除第 i 个结点的基本操作为:找到线性表中第i-1个结点,修改其指向后继的指针。

q = p->next;   p->next = q->next;  

e = q->data;     free(q);

 

Status ListDelete_L(LinkList L, int i, ElemType &e) {

   // 删除以 L 为头指针(带头结点)的单链表中第 i 个结点

p = L;    j = 0;

while (p->next && j < i-1) {  p = p->next;   ++j; }

                           // 寻找第 i 个结点,并令 p 指向其前趋

if  (!(p->next) || j > i-1)

    return ERROR;  // 删除位置不合理

q = p->next;   p->next = q->next;  // 删除并释放结点

e = q->data;   free(q);

return OK;

} // ListDelete_L

算法的时间复杂度为:O(ListLength(L))

操作 ClearList(&L) 在链表中的实现:

void ClearList(&L) {

   // 将单链表重新置为一个空表

    while (L->next) {

        p=L->next;    L->next=p->next;

        

    }

} // ClearList

算法时间复杂度:O(ListLength(L))

如何从线性表得到单链表?

链表是一个动态的结构,它不需要予分配空间,因此生成链表的过程是一个结点“逐个插入” 的过程。

例如:逆位序输入 n 个数据元素的值,建立带头结点的单链表。

操作步骤:

一、建立一个“空表”;

二、输入数据元素an,建立结点并插入;

三、输入数据元素an-1,建立结点并插入;

四、依次类推,直至输入a1为止。

 

 

void CreateList_L(LinkList &L, int n) {

// 逆序输入 n 个数据元素,建立带头结点的单链表

L = (LinkList) malloc (sizeof (LNode));

L->next = NULL;    // 先建立一个带头结点的单链表

for (i = n; i > 0; --i) {

    p = (LinkList) malloc (sizeof (LNode));

    scanf(&p->data);    // 输入元素值

    p->next = L->next; L->next = p;  // 插入

}

} // CreateList_L

算法的时间复杂度为:O(Listlength(L))

 

单链表的优点

它是一种动态结构,整个存储空间为多个链表共用

不需预先分配空间

插入、删除操作方便

单链表的缺点

指针占用额外存储空间

不能随机存取,查找速度慢

四、其他形式的链表

循环链表:最后一个结点的指针域的指针又指回第一个结点的链表。

 

和单链表的差别仅在于,判别链表中最后一个结点的条件不再是“后继是否为空”,而是“后继是否为头结点”。

双向链表

typedef struct DuLNode {

      ElemType       data; // 数据域

      struct DuLNode  *prior;

                               // 指向前驱的指针域

      struct DuLNode  *next;

                               // 指向后继的指针域

 

双向循环链表

 

 

双向链表的操作特点:

“查询” 和单链表相同。

“插入” 和“删除”时需要同时修改两个方向上的指针。

插入

 

s->prior= p->prior;  p->prior ->next=s;

s->next = p;               p->prior = s ;

删除

 

p->prior->next = p->next;

p->next->prior = p->prior;

 

0 0
原创粉丝点击