線性表


線性表的基本概念

線性表的定義

線性表是由\(n(n>=0)\) 個相同類型的數據元素組成的有限序列,標記為:

\(L = (a_{1},a_{2},...,a_{i},...,a_{n})\)

  • 線性表中元素的個數n定義為線性表的長度,當\(n=0\)時為空表。
  • \(n>0\)時,線性表的邏輯結構如圖所示:

線性表的幾個概念:

邏輯特征:

  • 若至少含有一個元素,則只有唯一的起始元素
  • 若至少含有一個元素,則只有唯一的尾元素
  • 除了起始元素外,其他元素有且僅有一個前驅元素
  • 除了尾元素外,其余元素有且只有一個后繼元素

線性表中的每個元素有唯一的序號(邏輯序號),同一個線性表中可以有多個相同的元素,但他們的序號是不同的。

線性表的基本運算

1、初始化InitList(L)。其作用是建立一個空表L(即建立線性表的構架,但不含任何數據元素)。
2、銷毀線性表DestroyList(L)。其作用是釋放線性表L的內存空間。
3、求線性表的長度ListLength(L)。其作用是返回線性表L的長度。
4、求線性表中第i個元素GetElem(L,i,e)。其作用是返回線性表L的第i個數據元素。
5、按值查找LocateElem(L,x)。若L中存在一個或多個值與x相等的元素,則其作用是返回第一個值為x的元素的邏輯序號。
6、插入元素ListInsert(L,i,x)。其作用是在線性表L的第i個位置上增加一個以x為值的新元素
7、刪除元素ListDelete(L,i)。其作用是刪除線性表L的第i個元素ai。
8、輸出元素值DispList(L)。其作用是按前后次序輸出線性表L的所有元素值

我們通過以下實例進行分析:

1、利用兩個線性表LA,LB分別表示兩個集合\(A\&B\),要求一個新集合\(A=A∪B\)

void Union(SqList &La,SqList Lb)
{
  ElemType e;
  int la_len,lb_len;
  int i;
  la_len = ListLength(La)//ListLength 返回長度
  lb_len = ListLength(Lb)//ListLength 返回長度
  for(i = 1;i <= lb_len ;i++)
  {
    GetElem(Lb,i,e)//取Lb中第i個數據元素賦給e
    if(!LocateElem(La,e,equal))
      ListInsert(La,++la_len,e);//// La中不存在和e相同的元素,則插入之
  }
}

線性表LA和LB,其元素均按非遞減有序排列,編寫一個算法將它們合並成一個線性表LC,且LC的元素也是按非遞減有序排列。

  • 思路:掃描La,Lb的元素,比較當前元素的值,將較小的元素值賦給Lc,最后在處理沒有插入的值
void MergeList(SqList La,SqList Lb,SqList &Lc)
{
  int i = 1,j = 1, k = 0;
  int la_len = ListLength(La),lb_len = ListLength(Lb);
  ElemType ai,bj;
  InitList(Lc);
  //比較大小
  while(i<=la_len&&j<lb_len)
  {
    GetElem(La,i,ai);
    GetElem(Lb,j,bj);
    if(ai<bj)
      ListInsert(Lc,++k,ai),i++;
    else
      ListInsert(Lc,++k,bj),j++;
  }
  //處理La剩余
  while(i<=la_len)
  {
    GetElem(La,i++,ai);
    ListInsert(Lc,++k,ai);
  }
  //處理Lb剩余
  while(j<=lb_len)
  {
    GetElem(Lb,j++,bj);
    ListInsert(Lc,++k,bj);
  }
}

線性表的順序存儲和實現

順序表的定義

  • 順序表的定義和特點

    定義:將將線性表中的元素相繼存放在一個連續的存儲空間中,即構成順序表。
    存儲:它是線性表的順序存儲表示,可利用一維數組描述存儲結構。
    特點:元素的邏輯順序與物理順序一致。
    訪問方式:可順序存取,可按下標直接存取。

  • 順序表的連續存儲方式

\(LOC(i) = LOC(i-1) + l = a + i * l,LOC是元素存儲位置,l是元素大小\)

在介紹存儲結構之前,我們先給出相應的類型重定向:

#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
typedef int Status; // Status值是函數結果狀態代碼,如OK等

假定線性表的數據元素的類型為ElemType,類型聲明如下:

// ------ 線性表的動態分配順序存儲結構 --------
#define LIST_INIT_SIZE 100 // 線性表存儲空間的初始分配量
#define LIST_INCREMENT 10 // 線性表存儲空間的分配增量

typedef int ElemType; //元素的數據類型

typedef struct {
  ElemType *elem; // 存儲空間基址
  int length; // 當前長度
  int listsize; // 當前分配的存儲容量
} SqList;

同樣的,靜態存儲結構類型聲明如下:

// ------ 線性表的靜態分配順序存儲結構 --------
#define MaxSize 100
typedef int ElemType; //假設順序表中所有元素為int類型

typedef struct
{
  ElemType data[MaxSize]; //存放順序表的元素
  int length; //順序表的實際長度
} SqList; //順序表類型

  • 由於順序表采用數組存放元素,數組具有隨機存取特性,所以順序表具有隨機存取特性。

基本運算實現

  • 初始化線性表算法
Status InitList_Sq(List &L)
{
  L.elem=(ElemType*)malloc(LIST_INIT_SIZE*sizeof(ElemType));
  if(!L.elem)return ERROR;  //健壯性,不過一般不會失敗
  L.length = 0;  //長度置為0
  L.listsize = LIST_INIT_SIZE;  //最大長度
  return OK;
}
  • 銷毀線性表算法
Status DestroyList(SqList &L)
{
  free(L.elem);
  L.elem = NULL;  //指針置空,防止野指針
  L.length = 0;
  L.listsize = 0;
  return OK;
}
  • 求線性表長度
int GetLength(SqList L)
{
  return L.length;
}
  • 求第i個元素
Status GetElem(SqList L, int i, ElemType &e)
{
  if(i<1||i>L.length)
    return ERROR;    //后面可以改為:i>Getlength(L);
  e = *(L.elem + i -1);  //公式,隨機存取
  return OK;
}
  • 按值查找

在順序表L找第一個值為e的元素,找到后返回其邏輯序號,否則返回0(由於線性表的邏輯序號從1開始,這里用0表示沒有找到值為e的元素)。

int LocateElem(SqList L, ElemType e)
{
  ElemType *p;
  int i = 1;
  p = L.elem;
  while(i<=L.length && (*p++ != e))//核心代碼
    ++i;
  if(i<=L.length)
    return i;
  else
    return 0;
}
  • 插入算法

將新元素e插入到順序表L中邏輯序號為i的位置(如果插入成功,元素e成為線性表的第i個元素)。
i的合法值為1≤i≤L.Length+1。當i無效時返回0(表示插入失敗)。
有效時將L.elem[i-1..L.length-1]后移一個位置, 在L.elem[i-1]處插入e,順序表長度增1,並返回1 (表示插入成功)

Status ListInsert_Sq(SqList &L, int i, ElemType e)
{
  ElemType *p;
  if(i < 1 || i > L.length + 1)
    return ERROR;

  //這里不再考慮內存不足再使用relloc申請空間的情況

  ElemType *q = &(L.elem[i-1]);    //q為插入位置
  for(p = &(L.elem[L.length - 1]);p>=q;--p)
    *(p+1) = *p;          //插入位置及之后的元素右移
  *q = e;
  ++L.length;    //表長+1
  return OK;
}
  • 刪除算法

刪除順序表L中邏輯序號為i的元素
i的合法值為1≤i≤L.Length。在i無效時返回0 (表示刪除失敗)。
有效時將L.elem[i..length-1]前移一個位置,順序表長度減1,並返回1(表示刪除成功)

Status ListDelete_Sq(SqList &L, int i, ElemType &e)
{
  ElemType *p,*q;
  if(i<1 || i>L.length)//位置不合法
    return ERROR;

  p = &(L.elem[i-1]);//p 為刪除元素的位置
  e = *p;      //存儲其值
  q = L.elem +L.length - 1;  //表尾元素的位置

  for(++p;p<=q;p++)  //從下一個位置開始,逐步覆蓋
    *(p-1) = *p;    //元素左移
  --L.length;
  return OK;
}

拓展實例

  • 假設有一個順序表L,其中元素為整數且所有元素值均不相同。設計一個算法將最大值元素與最小值元素交換。

用maxi和mini記錄順序表L中最大值元素和最小值元素的下標,初始時maxi=mini=0。
i從1開始掃描所有元素:當L.elem[i]>L.elem[maxi]時置maxi=i;否則若L.elem[i]<L.elem[mini]時置mini=i。
掃描完畢時,L.elem[maxi]為最大值元素,L.elem[mini]為最小值元素,將它們交換。

void SwapMaxMin(SqList &L)
{
  int i,maxi,mini;
  maxi = mini = 0;  //存儲的是下標
  for(i = 1;i<L.length;i++)
    if(L.elem[i]>L.elem[maxi])
      maxi = i;
    else if(L.elem[i]<L.elem[mini])
      mini = i;
  swap(L.elem[maxi],L.elem[mini]);//調用庫函數即可
}
  • 從線性表中刪除自第i個元素開始的k個元素,其中線性表用順序表L存儲。

將線性表中a_{i}~a_{i+k-1}元素(對應\(L.elem[i-1..i+k-2]\)的元素)刪除,即將\(a_{i}+k~a_{n}\)(對應\(L.elem[i+k-1..n-1]\))的所有元素依次前移k個位置。

int Deletek(SqList &L,int i,int k)
{
  int j;
  if(i<1||k<1||i+k-1>L.length)
    return 0;
  for(j = i+k-1;j<L.length;j++)  //元素前移k個位置
    L.elem[j-k]=L.elem[j];    //i+k-1是不被刪除的第一個元素,j-k是被刪的第一個元素
  L.length -= k;    //長度減k
  return 1;
}
  • 已知線性表(a1,a2,…,an)采用順序表L存儲,且每個元素都是互不相等的整數。設計一個將所有奇數移到所有的偶數前邊的算法(要求時間最少,輔助空間最少)

並沒有要求有大小次序
置i=0,j=n-1,在順序表L中從左向右找到偶數L.elem[i],從右向左找到奇數L.elem[j],將兩者交換;循環這個過程直到i等於j為止。

void Move(SqList &L)
{
  int i = 0,j = L.length - 1;
  while(i<j)
  {
    while(L.elem[i]%2==1)i++;  //從前向后找偶數
    while(L.elem[j]%2==0)j--;  //從后向前找奇數
    if(i<j)swap(L.elem[i],L.elem[j]);  //交換
  }
}
  • 已知一個整數線性表采用順序表L存儲。設計一個盡可能高效的算法刪除其中所有值為負整數的元素(假設L中值為負整數的元素可能有多個)。

每次刪除都需要移動大量元素,會消耗大量的時間,可以換一個角度考慮
采用整體重建順序表的算法思路,僅僅將插入元素的條件設置為“元素值≥0”即可。

void De(SqList &L)
{
  int i,k = 0;
  for(i = 0;i < L.length;i++)
  {
    if(L.elem[i]>=0)    //非負數插入
    {
      L.elem[k] = L.elem[i];
      k++;
    }
  }
  L.length = k;    //重置長度
}
  • 設將\(n(n>1)\)個整數存放到一維數組R中。試設計一個時間和空間兩方面盡可能高效的算法,將R中整數序列循環左移\(p(0<p<n)\)個位置,即將R中的數據序列\((X_{0},X_{1},…,X_{n-1})\)變換為\((X_{p},X_{p+1},…,X_{n-1},X_{0},X_{1},…,X_{p-1})\)

\(R=(X_{0},X_{1},…,X_{p},X_{p+1}, …,X_{n-1)}\),記\(A=(X_{0},X_{1},…,X_{p-1})\) (共有p個元素),\(B=(X_{p},…,X_{n-1})\)(共有n-p個元素)
設計逆置算法reverse(R),用於原地逆置數組R,則A原地逆置后A’變為\((X_{p-1},…,X_{1},X_{0})\),B原地逆置后B’變為\((X_{n-1},…,X_{p-1},X_{p})\)
就是說\(A’B’=(X_{p-1},…,X_{1},X_{0},X_{n-1},…,X_{p-1},X_{p})\),再將A’B’原地逆置變為\((X_{p},X_{p-1},…,X_{n-1},X_{0},X_{1},…,X_{p-1})\)即為所求,即進行3次逆置操作:\(reverse(R)=reverse(reverse(A),reverse(B))\)

void reverse(int R[],int m,int n) //將R[m..n]逆置
{ 
  int i,tmp;
  for (i=0;i<(n-m+1)/2;i++)
  { 
    tmp=R[m+i]; //將R[m+i]與R[n-i]進行交換
    R[m+i]=R[n-i];
    R[n-i]=tmp;
  }
}

int ListReverse(int R[],int n,int p) //循環左移
{ 
  if (p<=0 || p>=n)
    return 0;
  else
  { 
    reverse(R,0,p-1);
    reverse(R,p,n-1);
    reverse(R,0,n-1);
    return 1;
  }
}

線性表的鏈式存儲和實現

線性鏈表

存儲方式

線性表的單鏈表存儲方法:用一個指針表示結點間的邏輯關系。

因此單鏈表的一個存儲結點包含兩個部分,結點的形式如下:

data:為數據域,用於存儲線性表的一個數據元素,也就是說在單鏈表中一個結點存放一個數據元素。

next:為指針域或鏈域,用於存放一個指針,該指針指向后繼元素對應的結點,也就是說單鏈表中結點的指針用於表示后繼關系。

  • 特點

每個數據元素由結點構成
線性結構

結點可以連續,可以不連續存儲。
結點的邏輯順序與物理順序可以不一致
表可擴充。
鏈表中第一個元素結點稱為首結點;最后一個元素稱為尾結點,其指針域(next)為空(NULL)。

  • 結點類型聲明
typedef int ElemType;
typedef int Status; 
#define OK 1
#define ERROR 0

// ------ 結點的C語言描述 --------
typedef struct node {
  ElemType data;
  struct node *next;
} LNode, *LinkList;

帶頭節點的單鏈表示意圖

常規算法設計

  • 初始化單鏈表算法

創建一個空的單鏈表,它只有一個頭結點,由L指向它。該結點的next域為空,data域未設定任何值。

Status InitList(LinkList &L)
{
  L = (LinkList)malloc(sizeof(LNode));
  if(!L)
    return ERROR;
  L->next = NULL;    //指針域為空
  return OK;
}
  • 銷毀單鏈表算法

一個單鏈表中的所有結點空間都是通過malloc函數分配的,在不再需要時需通過free函數釋放所有結點的空間
借助一個遍歷指針

Status DestroyList(LinkList &L)
{
  LinkList p;  //臨時指針
  while(L)
  {
    p = L->next
    free(L);
    L = p;
  }
  return OK;
}
  • 求單鏈表長度算法

設置一個整型變量i作為計數器,i初值為0,p初始時指向第一個數據結點。
然后沿next域逐個往后查找,每移動一次,i值增1。當p所指結點為空時,結束這個過程,i之值即為表長。

int ListLength(LinkList L)
{
  int i = 0;//計數器
  LinkList p = L->next; // p指向第一個結點
  while(p) { // 沒到表尾
    i++;
    p = p->next;
  }
  return i;
}
  • 求單鏈表中值為e的元素算法

用p從頭開始遍歷單鏈表L中的結點,用計數器i統計遍歷過的結點,其初值為0。
在遍歷時,若p不為空,則p所指結點即為要找的結點,查找成功,算法返回位序i
否則算法返回0表示未找到這樣的結點。

int LocateElem(LinkList L, ElemType e)
{
  int i = 0;
  LinkList p = L->next;
  while(p)
  {
    i++;
    if(p->data == e)
      return i;
    p = p->next;
  }
  return 0;
}
  • 單鏈表的插入算法

在單鏈表L中第i個位置,插入值為x的結點。

找前驅結點
先在單鏈表L中查找第i-1個結點,若未找到返回0;
找到后由p指向該結點,創建一個以x為值的新結點s,將其插入到p指結點之后。
在p結點之后插入s結點的操作如下:
① 將結點s的next域指向p的下一個結點(s->next=p->next)。
② 將結點p的next域改為指向新結點s(p->next=s)。

插入操作的①和②執行順序不能顛倒。
記憶方法:先把沒有指向的指針指向一個位置,進行操作

Status ListInsert_L(LinkList &L, int i, ElemType e)
{
  LinkList p, s;
  p = L;
  int j = 0;
  while(p && j<i-1)p = p->next,j++;  //找結點
  if(!p||j>i-1)  return ERROR;//位置不合法
  s = (LinkList)malloc(sizeof(LNode)); // 生成新結點
  s->data = e;
  s->next = p->next;
  p->next = s;
  return OK;
}
  • 單鏈表的刪除算法

先在單鏈表L中查找第i-1個結點,若未找到返回0
找到后由p指向該結點,然后讓q指向后繼結點(即要刪除的結點)。
若q所指結點為空則返回0,否則刪除q結點並釋放其占用的空間。
刪除p指結點的后繼結點的過程:

p->next=q->next;
free(q);
即需要找到刪除結點的前驅結點

Status ListDelete_L(LinkList &L, int i, ElemType &e)
{
  LinkList p, q;
  p = L;
  int j = 0;
  while(p && j<i-1)p = p->next,j++;  //找結點
  if(!(p->next)||j>i-1)  return ERROR;//位置不合法
  q = p->next;  //p為前驅,q為當前結點
  e = q->data;
  free(q);
  return OK;
}

拓展實例

  • 設計一個算法,通過一趟遍歷確定單鏈表L(至少含兩個數據結點)中第一個元素值最大的結點。

用p遍歷單鏈表,在遍歷時用maxp指向data域值最大的結點(maxp的初值為p)。當單鏈表遍歷完畢,最后返回maxp。

LNode *MaxNode(LNode *L)
{
  LNode *p=L->next, *maxp=p;
  while (p!=NULL) //遍歷所有的結點
  {
    if(maxp->data<p->data)
      maxp = p;  //當p指向的值大於maxp時,更新maxp
  }
  return maxp;
}
  • 刪除一個單鏈表L(至少含兩個數據結點)中第一個元素值最大的結點。

以p遍歷單鏈表,pre指向p結點的前驅結點。
在遍歷時用maxp指向data域值最大的結點,maxpre指向maxp結點的前驅結點。
當單鏈表遍歷完畢,通過maxpre結點刪除其后的結點,即刪除了元素值最大的結點。
用到了同步指針

void DelMaxNode(LNode *&L)
{
  LNode *p=L->next,*pre=L,*maxp=p,*maxpre=pre;  //初始化
  while(p!=NULL)
  {
    if(maxp->data < p->data)
    {
      maxp = p;
      maxpre = pre;
    }
    pre = p;
    p = p->next;//pre、p同步后移,保證pre始終為p的前驅結點
  }
  maxpre->next = maxp->next;//刪除maxp結點
  free(maxp);//釋放maxp結點
}
  • 將一個單鏈表L(至少含兩個數據結點)中所有結點逆置,並分析算法的時間復雜度。

先將單鏈表L拆分成兩部分,一部分是只有頭結點L的空表,另一部分是由p指向第一個數據結點的單鏈表。
然后遍歷p,將p所指結點逐一采用頭(前)插法插入到L單鏈表中,由於頭插法的特點是建成的單鏈表結點次序與插入次序正好相反,從而達到結點逆置的目的。
頭插法天然可以實現單鏈表逆置
一樣,還是先處理沒有指向的指針,再做其他處理

void ListReverse(LNode *&L)
{
  LNode *p = L->next,*q;
  L->next = NULL;
  while(p!=NULL)
  {
    q = p->next;
    
    p->next = L->next;//頭插法
    L->next = p;
    p = q;
  }
}
  • 假設該單鏈表只給出了頭指針list。在不改變鏈表的前提下,請設計一個盡可能高效的算法,查找鏈表中倒數第k個位置上的結點(k為正整數)。若查找成功,算法輸出該結點的data域的值,並返回1;否則,只返回0。

單鏈表只能從前往后遍歷,並不能從后往前,所以想要再一次循環內找到相對應的結點,只能采用特殊的方式。
考慮雙指針的思路,定義兩個指針變量p和q,初始時均指向頭結點的下一個結點。p指針沿鏈表移動; 當p指針移動到第k個結點時,q指針開始與p指針同步移動;當p指針移動到鏈表尾結點時,q指針所指元素為倒數第k個結點。

詳細思路:

①置count=0,p和q指向鏈表表頭結點的下一個結點。
②若p為空,則轉向⑤ 。
③若count等於k,則q指向下一個結點;否則,置count=count+1。
④置p指向下一個結點,轉向② 。
⑤若count等於k,則查找成功,輸出該結點的data域的值,返回1;否則,查找失敗,返回0。
⑥算法結束。

int Searchk(LinkList list,int k)
{
  LinkList p, q;
  int count = 0;
  p = q = list->link;
  while (p!=NULL && count<k) //查找第k個結點*p 
  { 
    count++;
    p=p->link;
  }
  if(p == NULL)
    return 0;
  else
  {
    while(p!=NULL)//沒有時返回0
    {
      q = p->link;//p和q同步后移直到p=NULL
      p = p->link;
    }
    printf("%d",q->data);
    return 1;
  }
}

循環鏈表

循環鏈表是單鏈表的變形,鏈表尾結點的next指針不是NULL,而是指向了單鏈表的前端。

循環鏈表的判空條件:L->next == L;
循環單鏈表的特點是:只要知道表中某一結點的地址,就可搜尋到所有其他結點的地址。
在搜尋過程中,沒有一個結點的 next 域為空

遍歷條件:for ( p = L->next; p != L; p = p->next )
循環單鏈表的所有操作的實現類似於單鏈表,差別在於檢測到鏈尾指針不為NULL,而是回到鏈頭

算法設計

  • 初始化線性表運算算法

創建一個空的循環單鏈表,它只有頭結點,由L指向它。

Status InitList_cl(LinkList &L)
{
  L = (LinkList)malloc(sizeof(LNode));
  if(!L)exit(OVERFLOW);
  L->next = L;
  return OK;
}
  • 求線性表的長度算法
int ListLength(LinkList L)
{
  int i = 0;
  LinkList p = L->next; // p指向第一個結點
  while(p != L) { // 沒到表尾
    i++;
    p=p->next;
  }
  return i; 
}

其他

  • 設計一個算法求一個循環單鏈表L中所有值為x的結點個數
int CountNode(LNode *L, ElemType x)
{
  int i = 0;
  LNode *p = L->next;
  while(p!=L)
  {
    if(p->data == x)i++;
    p = p->next;
  }
  return i;
}
  • 有一個非遞減有序的循環單鏈表L,設計一個算法刪除其中所有值為x的結點

由於循環單鏈表L是非遞減有序的,則所有值為x的結點必然是相鄰的
先找到第一個值為x的結點p,讓pre指向其前驅結點
然后通過pre結點刪除p結點及其后面連續值為x的結點

int Delallx(LNode *&L, ElemType x)
{
  LNode *pre=L,*p=L->next; //pre指向p結點的前驅結點
  while (p!=L && p->data!=x) //找第一個值為x的結點p
  {
    pre = p;
    p = p->next;
  }
  if(p==L)return 0;//沒有找到值為x的結點返回0
  while(p!=L&&p->data == x)//刪除所有值為x的結點
  {
    pre->next = p->next;
    free(p);
    p = pre->next;
  }
  return 1;//成功刪除返回1
}

雙向鏈表

雙向鏈表是指在前趨和后繼方向都能遍歷的線性鏈表。

雙向鏈表每個結點的結構為:

雙向鏈表中用兩個指針表示結點間的邏輯關系
指向其前驅結點的指針域prior。
指向其后繼結點的指針域next。

結點指向:

指向其后繼結點的指針域next。
p->next 指示結點 p 的后繼結點;
p->prior->next指示結點 p 的前趨結點的后繼結點,即結點 p 本身;
p->next->prior指示結點 p 的后繼結點的前趨結點,即結點 p 本身。

p->prior->next == p == p->next->prior 固有特性

存儲聲明

typedef struct DuLNode {
  ElemType data;
  DuLNode *prior, *next;
} DuLNode, *DuLinkList;

雙向循環鏈表

  • 雙向循環鏈表長度
int ListLength(DuLinkList L)
{
  int i = 0;
  DuLinkList p = L->next; // p指向第一個結點
  while(p!=L)
  {
    i++;
    p = p->next;
  }
  return i;
}
  • 插入

① 將結點s的next域指向結點p的下一個結點( s->next=p->next)。
② 若p不是最后結點(若p是最后結點,只插入s作為尾結點),則 將p之后結點的prior域指向s(p->next->prior=s)。
將s的prior域指向p結點(s->prior=p)。
④ 將p的next域指向s(p->next=s)。
還是先將沒有指向的指針指向該指向的位置

在雙向循環鏈表中,可以通過一個結點找到其前驅結點,所以插入操作也可以改為:在雙鏈表中找到第i個結點p,然后在p結點之前插入新結點。

Status ListInsert(DuLinkList L, int i, ElemType e)
{
  DuLinkList p, s;
  if(i<1 || i>ListLength(L) + 1) //判斷位置
    return ERROR;
  p = GetElemP(L, i - 1); // 在L中確定第i個元素前驅的位置指針p
  if(!p) return ERROR;
  s = (DuLinkList)malloc(sizeof(DuLNode));
  if(!s) exit(OVERFLOW);
  
  s->data = e;
  s->prior = p;
  s->next = p->next;
  p->next->prior = s;
  p->next = s;
  return OK;
}

  • 刪除結點運算算法

若p不是尾結點,則將其后繼結點的prior域指向pre結點(p->next->prior=pre)
② 將pre結點的next域改為指向p結點的后繼結點(pre-next=p->next)

Status ListDelete(DuLinkList L, int i, ElemType &e)
{
  DuLinkList p;
  if(i<1) return ERROR;
  p = GetElemP(L,i)//確定第i個位置
  if(!p) return ERROR;
  e = p->data;    //保存刪除元素
  
  p -> prior -> next = p->next;
  p -> next ->prior = p->prior;
  free(p);
  return OK;
}

總結


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM