线性表的表示与实现-------链式结构

链表:顺序查找,插入删除操作效率高。

单链表

用一组地址任意的存储单元存放线性表中的数据元素。在物理上不一定相邻。

结点(表示数据元素或数据元素的映象)=元素(数据元素的映象) + 指针(指示后继元素存储位置)  

以“结点的序列”表示线性表 称作链表

 

线性表的地址(线性表的头指针)是以线性表中第一个数据元素的存储地址

单链表的描述定义

 
  1. Typedef  struct  LNode { 
  2.       ElemType      data;  // 数据域 
  3.       struct Lnode   *next;  // 指针域 
  4.    } LNode, *LinkList;    
  5. LNode为结点      LinkList为指向链表的指针 
  6. LinkList  L;  // L 为单链表的头指针,是一个地址 

 

要注意的是:以上定义了一个单链表,是一个地址,逻辑上的地址,而不是物理上的空间。

线性表的基本操作在链表中的实现(初始化、插入、删除、取元素、重置、创建)

注意:在单链表中通常会有一个头指针L,这个L所指的结点并不是第一个结点,头结点为21P是一个指针,用来指向结点地址的指针

初始化

 
  1. Status InitList(LinkList &L){ 
  2.      L=(LinkList)malloc(sizeof(LNode)); //分配一个结点的空间
  3.      L->next=NULL;  //将头指针指向的下一个结点(首元结点也是第一个结点)设为空
  4.      return OK; 

取元素

 
  1. Status GetElem_L(LinkList L, int i, ElemType &e) { 
  2.    // L是带头结点的链表的头指针,以 e 返回第 i 个元素 
  3.     pL->next;   j = 1;  // p指向第一个结点,j为计数器,L->next为下一个结点 
  4.   while (p && j<i) { 
  5.       pp = p->next;  ++j;     // 顺指针向后查找,直到 p 指向第 i 个元素 
  6.     }                       // 或 p 为空 
  7.     if ( !p || j>i ) 
  8.         return ERROR;      //  第 i 个元素不存在 
  9.     e = p->data;                 //  取得第 i 个元素 
  10.     return OK; 
  11. } // GetElem_L 

插入

在链表中插入结点只需要修改指针,不需要移动大量元素,因为链表是一个指向。如上图:将ai-1的后继指针指向e.

 
  1. Status ListInsert_L(LinkList L, int i, ElemType e) { 
  2.      // L 为带头结点的单链表的头指针,本算法 
  3.      // 在链表中第i 个结点之前插入新的元素 e 
  4.      p = L;    j = 0
  5.      while (p && j < i-1) { 
  6.             pp = p->next;  ++j; // 寻找第 i-1 个结点 
  7.       }         
  8.       if (!p || j > i-1) 
  9.          return ERROR;      // i 大于表长或者小于1 
  10.       s = (LinkList) malloc ( sizeof (LNode));  // 生成新结点                          
  11.       s->data = e; s->next=NULL;  //让s指向结点e 
  12.       s->next = p->next;      p->next = s;   // 插入,过程如下图。 
  13.       return OK; 
  14.   } // LinstInsert_L 

删除

找到线性表中第i-1个结点,修改其指向后继的指针。

 
  1. Status ListDelete_L(LinkList L, int i, ElemType &e) { 
  2.    // 删除以 L 为头指针(带头结点)的单链表中第 i 个结点 
  3.      p = L;    j = 0
  4.      while (p->next && j < i-1) {  
  5.            pp = p->next;   ++j; // 寻找第 i 个结点,并令 p 指向其前趋 
  6.      }                        
  7.      if  (!(p->next) || j > i-1)  
  8.          return ERROR;  // 删除位置不合理 
  9.      q = p->next;   p->next = q->next;  // 删除并释放结点,过程如下图 
  10.      e = q->data;   free(q); 
  11.      return OK; 
  12.  } // ListDelete_L 
  13.  

重置(将表置空)

 
  1. void ClearList(&L) { 
  2.    // 将单链表重新置为一个空表 
  3.     while (L->next) { 
  4.         p=L->next;    L->next=p->next; 
  5.          free(p);
  6.     } 
  7. } // ClearList 

创建

创建链表实际上就是一个插入的过程。过程如下:采用逆位序插入,然后建立头结点

 
  1. void CreateList_L(LinkList &L, int n) { 
  2.        // 逆序输入 n 个数据元素,建立带头结点的单链表 
  3.        L = (LinkList) malloc (sizeof (LNode)); 
  4.        L->next = NULL;    // 先建立一个带头结点的单链表 
  5.        for (i = n; i > 0; --i) { 
  6.          p = (LinkList) malloc (sizeof (LNode)); 
  7.          scanf(&p->data);    // 输入元素值 
  8.          p->next = L->next; L->next = p;  // 插入 
  9.        }  
  10. } // CreateList_L 
  11.                                                算法的时间复杂度为:O(Listlength(L)) 

循环链表

最后一个结点的指针域的指针又指回头结点(第一个结点)的链表,整个链表形成一个环

注意:判断时应该是后继是否为头结点,比如取元素长度时,单链表为结点后继是否为NULL,但是循环链表的判断是结点的后继是否为头结点

与单链表操作比较:

1.Initlist()  初始化时,头结点的next不为NULL,而是指向自身。

2.求表长:while中的条件改为   p=L

3.插入、删除操作的基本语句无变化

     操作基本一致,通常遇到的循环条件不是判断指针pp->next 是否为NULL,而是判断它们是否等于头结点

 向链表

定义

 
  1. typedef   struct  DuLNode  
  2.     ElemType         data;   // 数据域 
  3.     struct DuLNode   *prior;    // 指向前驱的指针域 
  4.     struct DuLNode  *next;     // 指向后继的指针域 
  5. } DuLNode, *DuLinkList; 

双向循环链表

双向循环链表的基本操作:

   其中查询和单链表相同。

初始化    

 
  1. Status Init_Dul(DulinkList  &L){ 
  2.    if(!L) return ERROR; 
  3.    L->next=L
  4.    L->prior=L
  5.    Return OK; 
  6. }//Init_Dul 

插入

不论P指向哪个结点,都要按照1234的顺序来就不会有错误

 
  1. Status ListInsert_Dul(DuLinkList  &L,int i, ElemType  e){ 
  2.   P=L-->next; j=1
  3.   while(p&&j<i){ 
  4.      pp=p-->next;  j++;        
  5.   } 
  6.   if(!p||j>i)  return ERROR; 
  7.   if(!(s=(DuLinkList)malloc(sizeof(DuLNode)))   return ERROR;)  
  8.   S-->data=e
  9.    S-->pprior=p ; S-->nextP-->next  ; P-->next=S;  S-->next-->prior=S ; 
  10.   return  OK; 
  11. }// 

删除

删除中间结点

 

 
  1. p-->next=p-->next-->next; 
  2. p-->next-->pprior=p;  注意:现在的p-->next已经是最后一个结点了,因为上一步