1. 程式人生 > >連結串列的多種實現方式

連結串列的多種實現方式

 連結串列是什麼???

  連結串列是一種區別於順序儲存的非連續、非順序的物理儲存結構,也就是說,不像佇列、堆疊之類的邏輯資料結構,它只是一種資料儲存的物理結構。我們所使用的陣列便是一種物理儲存結構。

  但與陣列不同的是,它是鏈式儲存的,而陣列採用了順序儲存。

  那麼,什麼是順序儲存與鏈式儲存呢(⊙o⊙)?

  順序儲存:把邏輯上相鄰的結點儲存在物理位置上相鄰的儲存單元,即每個結點之間的地址是連續的。

  鏈式儲存:用一組任意的儲存單元儲存線性表的陣列元素,結點之間的地址不一定是連續的,但每個結點之間一定有著某種先後關係。

  我們使用鏈式儲存,可以更方便地插入、刪除、更新元素,但是頻繁查詢元素時較耗時間。同時,連結串列由於多了指標域,更耗空間,卻又避免了陣列的大幅空間浪費的情況。

連結串列程式碼之指標實現:

 

//連結串列(包括有關連結串列的各種操作) 
#include<cstdio>
#include<cstdlib>
using namespace std;
typedef struct List *Link;//Link代表連結串列指標 
typedef struct List Lnode;//Lnode代表連結串列結點 

struct List
{
    int data;
    Link next;
};


Link create(Link Head)//建表操作並返回頭指標 
{ int newdata; Link newnode; Head=(Link)malloc(sizeof(Lnode)); printf("Please input number:\n"); scanf("%d",&newdata); Head->data=newdata; Head->next=NULL; while(1) { newnode=(Link)malloc(sizeof(Lnode)); if(newnode==NULL)//若空間分配失敗 ,則終止迴圈
{ printf("Sorry, it failed!"); break; } printf("Please input number : intput '-1' means exit\n");//輸入連結串列元素,輸入-1代表輸入結束 scanf("%d",&newdata); if(newdata==-1) return Head; newnode->data=newdata; newnode->next=Head; Head=newnode; } return Head; } void dispaly(Link Head)//連結串列元素的顯示 { Link p=Head; if(p==NULL)//連結串列為空 printf("\nList is empty\n"); else do { printf("%d",p->data); p=p->next; }while(p!=NULL); return; } Link insert(Link Head, int value, int i)//Head為頭指標,value為插入值,i為插入結點的編號 { Link newnode,p=Head; int newdata,j=1; newnode=(Link)malloc(sizeof(Lnode)); newnode->data=value; if(i==1) { newnode->next=Head; Head=newnode; } else { while(j<i-1&&p->next!=NULL)//找到 i 結點的前一個元素 { p=p->next; j++; } if(j==i-1) { newnode->next=p->next; p->next=newnode; } else printf("Insert is Failure,i is not right!\n"); } return Head; } Link del(Link Head, int i)//Head為頭指標,i為刪除結點編號 { Link p=Head,t; int j=1; if(i==1) { Head=Head->next; } else { while(j<i-1&&p->next!=NULL) { p=p->next; j++; } if(j==i-1&&p->next!=NULL) { t=p->next; p->next=t->next; } if(t!=NULL) free(t);//釋放記憶體,儘可能地節省空間 } return Head; } int get(Link Head, int i)//取編號為i的結點的值 { int j=1; Link p; p=Head; while(j<i&&p!=NULL) { p=p->next; j++; } if(p!=NULL) return p->data; else printf("Data is error!\n"); return -1; } int locate(Link Head, int x)//查詢第一個值為x的結點的編號,若失敗,則返回-1 { int n=1; Link p=Head; while(p!=NULL&&p->data!=x) { p=p->next; n++; } if(p!=NULL) return n; else printf("Not found!\n"); return -1; } int lengh(Link Head)//取連結串列長度 { int len=0; Link p=Head; while(p!=NULL) { len++; p=p->next; } return len; } Link connect(Link Head1, Link Head2)//以前面的連結串列為頭,連線後續連結串列 { Link p=Head1; while(p->next!=NULL) { p=p->next; } p->next=Head2; return Head1; } bool compare(Link Head1, Link Head2)//比較兩個連結串列是否相同 { Link p1,p2; p1=Head1; p2=Head2; while(1) { if(p1->next==NULL&&p2->next==NULL) return 1; else if(p1->data!=p2->data) return 0; else { p1=p1->next; p2=p2->next; } } } Link Set_NULL(Link Head)//清空連結串列 { Link p; p=Head; while(p!=NULL) { p=p->next; free(Head); Head=p; } return Head; } int main() { //連結串列的相關操作已給出 //以下省略一千字...... }
View Code

 

  但實際上,我們同樣可以用陣列來模擬連結串列,而且對其操作的速度將大大提高(我也是在一位神犇那兒知道的),因為CPU快取對連續的資料結構速度更快(貌似是因為儲存結構層次之類亂七八糟的東西)!

  因此,下面給出陣列模擬連結串列的程式碼。

連結串列程式碼之陣列實現:

//陣列模擬連結串列的演示程式
#include<iostream>
#include<cstdlib>
#define MAXN 100001//連結串列中最多能容納的元素數+1
using namespace std;

int linklst_data[MAXN] ;//linklst_data 為連結串列中元素的資料
int linklst_point[MAXN] ;//linklst_point 為連結串列中元素的指標
int head = -1;/*連結串列的頭指標*/

void del_by_data(int del_data)//刪除一個含有del_data的元素
{
  int p=head,pre=-1;//從頭開始遍歷連結串列
  while(p != -1)
  {
    if(linklst_data[p] == del_data)//如找到了要刪除的值,則進行刪除操作
    {
      if(p == head)//如果刪除的是頭指標,則更新頭指標
        head = linklst_point[head];
      else//這個else 等同於 if (pre != 1)因為當p!=head時p前面已經有元素了
        linklst_point[pre] = linklst_point[p];
      linklst_data[p] = -1;//刪除p指向的元素的值*/
      linklst_point[p] = -1;
      return ;/*結束刪除操作*/
    }
      pre = p;
      p = linklst_point[p];
  }
}

void add_front(int add_data)//在連結串列前段加入元素
{
  int p = 1;
  while(linklst_data[p] != -1 && p < MAXN)  //找一空位儲存資料,這裡可以優化
    ++p;
  linklst_data[p] = add_data; //將要加入的節點選好的空位賦值
  linklst_point[p] = head;  //將當前加入元素的指標指向head
  head = p; //使當前的元素成為連結串列頭指標
}

void add_rear(int add_data)//在連結串列末尾加入元素
{
  int p = 1,pre;
  while(linklst_data[p] != -1 && p < MAXN )//找到空位 
    ++p;
  linklst_data[p] = add_data; //該空位賦值
  if( head != -1 ) //連結串列不為空
  {
    pre = head;//找到連結串列中的最後一個元素*/
    while(linklst_point[pre] != -1)//找到空指標 
      pre = linklst_point[pre];
    linklst_point[pre] = p;//將當前連結串列中最後一個元素的指標指向要加入的元素
  }
  else//否則直接對head所指的元素賦值
    head = p;
  return ;
}

void output()
{
  int p=head;
  cout<<"list is: ";
  while(p != -1)
  {
    cout<<linklst_data[p]<<" ";
    p = linklst_point[p];
  }
  cout<<"\n";
}

void init()//初始化陣列指標值
{
  for(int i = 0;i < MAXN;i++)//連結串列中的空值設定為-1
  {
    linklst_point[i] = -1;
    linklst_data[i] = -1;
  }
}

int main()//演示連結串列的操作
{
  int ins,data;
  init();
  while(1)
  { 
    cout<<"1.insert a value in front \n";
    cout<<"2.insert a value in rear \n";
    cout<<"3.delete a value \n";
    cout<<"4.quit \n";
    cin>>ins;
    switch(ins)
    {
        case 1:
        cout<<"please insert a value :";
        cin>>data;
        add_front(data);
        break;
        case 2:
        cout<<"please insert a value :";
        cin>>data;
        add_rear(data);
        break;
      case 3:
        cout<<"please insert a value :";
        cin>>data;
        del_by_data(data);
        break;
      default:    
        return 0;
    }
    system("cls");//清空螢幕,此命令只能在window平臺下使用 
    output();
 }
 return 0;
}
View Code

  從程式碼中可以發現,每次進行插入操作時,都要在陣列中找一空位進行操作,這就導致每次插入操作的時間複雜度都是線性的,導致很大的時間浪費。

  因此,我們可以通過堆疊記錄沒有被加入連結串列的元素的下標,每次插入操作只需從棧中獲取下標即可。這樣,O(n)的線性查詢便轉化為了O(1)的出棧操作,時間上大大優化。

連結串列程式碼之陣列實現(堆疊優化):

//陣列連結串列的演示程式(棧優化)
#include<iostream>
#include<cstdlib>
#define MAXN 100001
using namespace std;

int linklst_data[MAXN] ;
int linklst_point[MAXN] ;
int stack[MAXN];//用於記錄沒有加入連結串列的陣列下標的棧
int head = -1;//連結串列的頭指標
int stack_head = 0;//棧頂指標 

void del_by_data(int del_data)//刪除一個含有del_data的元素
{
  int p=head,pre=-1;  //從頭開始遍歷連結串列
  while(p != -1)
  {
    if(linklst_data[p] == del_data)//找到要刪除的值,進行刪除操作
    {
      
      if(p == head)//如果刪除的是頭指標,則更新頭指標
        head = linklst_point[head];
      else//這個else 等同於 if (pre != 1)因為當p!=head時p前面已經有元素了
        linklst_point[pre] = linklst_point[p];
      
      linklst_data[p] = -1;//刪除p指向的元素的值
      linklst_point[p] = -1;
      stack[--stack_head] = p;//將刪除元素的陣列下標入棧
      return ;
    }
      pre = p;
      p = linklst_point[p];
  }
  return ;
}

void add_front(int add_data)//在連結串列前段加入元素*/
{
  int p = 1;
  p = stack[stack_head++];//直接取出棧中的空位*/
  linklst_data[p] = add_data;//將要加入的節點選好的空位賦值*/
  linklst_point[p] = head; //將當前加入元素的指標指向head*/
  head = p;  //使當前的元素成為連結串列頭指標*/
}

void add_rear(int add_data)//在連結串列末尾加入元素*/
{
  int p = 1,pre;
  p = stack[stack_head++];//直接取出棧中的空位*/
  linklst_data[p] = add_data;  //對要加入的節點的空位進行賦值*/
  if( head != -1 ) //連結串列不為空
  {
    pre = head;//找到連結串列中的最後一個元素*/
    while(linklst_point[pre] != -1)
        pre = linklst_point[pre];
    linklst_point[pre] = p;//將當前連結串列中最後一個元素的指標指向要加入的元素
  }
  else//否則直接對head所指的元素賦值
    head = p;
}

void output()
{
  int p=head;
  cout<<"List: ";
  while(p != -1)
  {
    cout<<linklst_data[p]<<" ";
    p = linklst_point[p];
  }
  cout<<"\n";
  return ;
}


void init()//初始化陣列指標值
{
  for(int i = 0;i < MAXN;i++)
  {
    linklst_point[i] = -1;
    linklst_data[i] = -1;
    stack[i] = i + 1;//從1到MAXN給棧加入陣列下標
  }
  return ;
}

int main()//演示連結串列的操作
{
  int ins,data;
  init();
  while(1)
  { 
    cout<<"1.insert a value in front \n";
    cout<<"2.insert a value in rear \n";
    cout<<"3.delete a value \n";
    cout<<"4.quit \n";
    cin>>ins;
    switch(ins)
    {
        case 1:
        cout<<"please insert a value :";
        cin>>data;
        add_front(data);
        break;
        case 2:
        cout<<"please insert a value :";
        cin>>data;
        add_rear(data);
        break;
      case 3:
        cout<<"please insert a value :";
        cin>>data;
        del_by_data(data);
        break;
      default:
        return 0;
    }
    system("cls");//此命令只能在window平臺下使用 
    output();
 }
 return 0;
}
View Code

  以上便是我所學習的第一個資料結構--連結串列了,若有不足,請指正!!!

O(∩_∩)O謝謝