1. 程式人生 > >C++ 資料結構的單鏈表的建立,插入,刪除操作

C++ 資料結構的單鏈表的建立,插入,刪除操作

動態記憶體分配應用舉例(連結串列)
 我們知道,陣列式計算機根據事先定義好的陣列型別與長度自動為其分配一連續的儲存單元,相同陣列的位置和距離都是固定的,也就是說,任何一個數組元素的地址都可一個簡單的公式計算出來,因此這種結構可以有效的對陣列元素進行隨機訪問。但若對陣列元素進行插入和刪除操作,則會引起大量資料的移動,從而使簡單的資料處理變得非常複雜,低效。
 為了能有效地解決這些問題,一種稱為“連結串列”的資料結構得到了廣泛應用。
1. 連結串列概述
連結串列是一種動態資料結構,他的特點是用一組任意的儲存單元(可以是連續的,也可以是不連續的)存放資料元素。
連結串列中每一個元素成為“結點”,每一個結點都是由資料域和指標域組成的,每個結點中的指標域指向下一個結點。Head是“頭指標”,表示連結串列的開始,用來指向第一個結點,而最後一個指標的指標域為NULL(空地址),表示連結串列的結束。
可以看出連結串列結構必須利用指標才能實現,即一個結點中必須包含一個指標變數,用來存放下一個結點的地址。
實際上,連結串列中的每個結點可以用若干個資料和若干個指標。結點中只有一個指標的連結串列稱為單鏈表,這是最簡單的連結串列結構。
再c++中實現一個單鏈表結構比較簡單。例如,可定義單鏈表結構的最簡單形式如下
   struct Node
{
 int Data;
 Node*next;
};
這裡用到了結構體型別。其中,*next是指標域,用來指向該結點的下一個結點;Data是一個整形變數,用來存放結點中的資料。當然,Data可以是任何資料型別,包括結構體型別或類型別。
在此基礎上,我們在定義一個連結串列類list,其中包含連結串列結點的插入,刪除,輸出等功能的成員函式。

class list
{
 Node*head;
public:
 list(){head=NULL;}
 void insertlist(int aDate,int bDate);//連結串列結點的插入
 void Deletelist(int aDate);//連結串列結點的刪除
 void Outputlist();//連結串列結點的輸出
 Node*Gethead(){return head;}
};
2. 連結串列結點的訪問
由於連結串列中的各個結點是由指標連結在一起的,其儲存單元文筆是連續的,因此,對其中任意結點的地址無法向陣列一樣,用一個簡單的公式計算出來,進行隨機訪問。只能從連結串列的頭指標(即head)開始,用一個指標p先指向第一個結點,然後根據結點p找到下一個結點。以此類推,直至找到所要訪問的結點或到最後一個結點(指標為空)為止。
下面我們給出上述連結串列的輸出函式;
void list::outputlist()
{
 Node*current=head;
 while(current!=NULL)
 {
  cout<<current->Data<<" ";
  current=current->next;
 }
 cout<<endl;
}
3. 連結串列結點的插入
如果要在連結串列中的結點a之前插入結點b,則需要考慮下面幾點情況。
(1) 插入前連結串列是一個空表,這時插入新結點b後。
(2) 若a是連結串列的第一個結點,則插入後,結點b為第一個結點。
(3) 若連結串列中存在a,且不是第一個結點,則首先要找出a的上一個結點a_k,然後使a_k的指標域指向b,在令b的指標域指向a,即可完成插入。
(4) 如連結串列中不存在a,則插在最後。先找到連結串列的最後一個結點a_n,然後使a_n的指標域指向結點b,而b指標的指標為空。
以下是連結串列類的結點插入函式,顯然其也具有建立連結串列的功能。
void list::insertlist(int aDate,int bDate)  //設aDate是結點a中的資料,bDate是結點b中的資料
{
 Node*p,*q,*s;        //p指向結點a,q指向結點a_k,s指向結點b
 s=(Node*)new(Node);  //動態分配一個新結點
 s->Data=bDate;   //設b為此結點
 p=head;
 if(head==NULL)  //若是空表,使b作為第一個結點
 {
  head=s;
  s->next=NULL;
 }
 else
  if(p->Data==aDate)   //若a是第一個結點
  {
   s->next=p;
   head=s;
  }
  else
  {
   while(p->Data!=aDate&&p->next!=NULL)//查詢結點a
   {
    q=p;
    p=p->next;
   }
   if(p->Data==aDate)     ///若有結點a
   {
    q->next=s;
    s->next=p;
   }
   else                    //若沒有結點a;
   {
    p->next=s;
    s->next=NULL;
   }
  }
}
4. 連結串列結點的刪除
如果要在連結串列中刪除結點a並釋放被刪除的結點所佔的儲存空間,則需要考慮下列幾種情況。
(1) 若要刪除的結點a是第一個結點,則把head指向a的下一個結點。
(2) 若要刪除的結點a存在於連結串列中,但不是第一個結點,則應使a得上一個結點a_k-1的指標域指向a的下一個結點a_k+1。
(3) 空表或要刪除的結點a不存在,則不做任何改變。
 以下是連結串列類的結點刪除函式。
void list::deletelist(int aDate)  //設aDate是要刪除的結點a中的資料成員
{
 Node*p,*q;            //p用於指向結點a,q用於指向結a的前一個結點
 p=head;
 if(p==NULL)  //若是空表
  return;
 if(p->Data==aDate)  //若a是第一個結點
 {
  head=p->next;
  delete p;
 }
 else
 {
  while(p->Data!=aDate&&p->next!=NULL)  //查詢結點a
  {
   q=p;
   p=p->next;
  }
  if(p->Data==aDate)    //若有結點a
  {
   q->next=p->next;
   delete p;
  }
 }
}
例題;利用以上三個連結串列操作成員函式insertlist,deletelist.outputlist,可形成以下的簡單鏈表操作程式。
#include"iostream.h"
struct Node
{
 int Data;
 Node*next;
};
class list
{
 Node*head;
public:
 list(){head=NULL;}
 void insertlist(int aData,int bData);
 void deletelist(int aData);
 void outputlist();
 Node*gethead(){return head;}
};

void list::insertlist(int aData,int bData)  //設aData是結點a中的資料,bData是結點b中的資料
{
 Node*p,*q,*s;        //p指向結點a,q指向結點a_k,s指向結點b
 s=(Node*)new(Node);  //動態分配一個新結點
 s->Data=bData;   //設b為此結點
 p=head;
 if(head==NULL)  //若是空表,使b作為第一個結點
 {
  head=s;
  s->next=NULL;
 }
 else
  if(p->Data==aData)   //若a是第一個結點
  {
   s->next=p;
   head=s;
  }
  else
  {
   while(p->Data!=aData && p->next!=NULL)//查詢結點a
   {
    q=p;
    p=p->next;
   }
   if(p->Data==aData)     ///若有結點a
   {
    q->next=s;
    s->next=p;
   }
   else                    //若沒有結點a;
   {
    p->next=s;
    s->next=NULL;
   }
  }
}
void list::deletelist(int aData)  //設aData是要刪除的結點a中的資料成員
{
 Node*p,*q;            //p用於指向結點a,q用於指向結a的前一個結點
 p=head;
 if(p==NULL)  //若是空表
  return;
 if(p->Data==aData)  //若a是第一個結點
 {
  head=p->next;
  delete p;
 }
 else
 {
  while(p->Data!=aData&&p->next!=NULL)  //查詢結點a
  {
   q=p;
   p=p->next;
  }
  if(p->Data==aData)    //若有結點a
  {
   q->next=p->next;
   delete p;
  }
 }
}
void list::outputlist()
{
 Node*current=head;
 while(current!=NULL)
 {
  cout<<current->Data<<" ";
  current=current->next;
 }
 cout<<endl;
}
void main()
{
 list A,B;
 int Data[10]={25,41,16,98,5,67,9,55,1,121};
 A.insertlist(0,Data[0]); //建立連結串列A首結點
 for(int i=1;i<10;i++)
  A.insertlist(0,Data[i]); //順序向後插入
 cout<<"\n連結串列A:";
 A.outputlist();
  A.deletelist(Data[7]);
 cout<<"刪除元素Data[7]後";
 A.outputlist();
 B.insertlist(0,Data[0]); //建立連結串列B首結點
 for(i=0;i<10;i++)
  B.insertlist(B.gethead()->Data,Data[i]); //在首結點處順序向後插入
    cout<<"\n連結串列B:";
 B.outputlist();
 B.deletelist(67);
    cout<<"刪除元素67後";
 B.outputlist(); 
}
程式執行結果為
連結串列A;25,41,16,98,5,67,9,55,1,121
刪除元素Data[7]後;
25,41,16,98,5,67,9,1,121
連結串列B;121,1,55,9,67,5,98,16,41,25,
刪除元素67後;
121,1,55,9,5,98,16,41,25,

     下面是楊輝三角的程式碼:
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
 const int n=11;
 int i,j,a[n][n];
 for(i=1;i<n;i++)
    {
  a[i][i]=1;
  a[i][1]=1;
    }
 for(i=3;i<n;i++)
    {
  for(j=2;j<=i-1;j++)  
  a[i][j]=a[i-1][j-1]+a[i-1][j];  
 }
 for(i=1;i<n;i++)
    {
  for(j=1;j<=i;j++)
  cout<<setw(5)<<a[i][j]<<" ";
  cout<<endl;
    }
 cout<<endl;
 return 0;
}