C++連結串列類
連結串列是一種線性資料結構,佔用不連續的記憶體,用一個或兩個指標(或元素的代號)儲存與其相鄰的元素。
怎麼用類實現一個連結串列呢?(這裡我們討論雙向連結串列)
首先,我們可以建一個數組,儲存各元素,只不過元素的順序不一定是按照下標排列的。
元素可以用一個結構體表示,它有三個元素:數值、上一個元素的指標、下一個元素的指標。
然後,要新增頭指標和尾指標,以確定頭和尾的位置。
接著,我們最好考慮一下各函式的實現,以免出現一些問題。
- 建構函式。建構函式可以把陣列中的全部元素都用上嗎?顯然不能。因為這樣就無法往裡面插入新的元素了。只能初始化一部分或全不初始化。
- 輸入/輸出函式。這一部分實不實現都行,如果一定要實現,按照指標依次尋找就行了。
- 查詢元素函式。根據連結串列的性質,它的時間複雜度最快只能是O(n)。這也一樣,按照指標依次尋找就行了。
- 刪除元素函式。這也很簡單,把相鄰元素的指標修改一下就行了。
- 插入元素函式。第一步,要找到一個不用的元素,這就需要再建一個數組,用來標記有沒有用過,再加入一個迴圈,選擇第一個不用的元素,如果全用過了,那就不插入或報錯。第二步,修改相鄰元素的指標。
根據以上幾點,我們要再建一個數組表示是否用過。
程式碼如下:
#ifndef BASIC_LINKED_LIST #define BASIC_LINKED_LIST #include<iostream> #include<cstdio> using namespace std; #ifndef MAX_SIZE #define MAX_SIZE 1003 #endif //macros #ifdef FOR_LIST #define for_list(list_name,type,ptr_name)\ list_element<type> *ptr_name=list_name.head;\ for(;p!=list_name.tail;p=p->next) #define for_list_reverse(list_name,type,ptr_name)\ list_element<type> *ptr_name=list_name.tail;\ for(;p!=list_name.head;p=p->pre) #endif #ifdef DEFINE_TYPE #define def_list_ele(type,name) typedef list_element<type> name #define def_plist_ele(type,name) typedef list_element<type>* name #define def_list(type,name) typedef basic_linked_list<type> name #endif //list_element class template<typename T> class list_element{ public: T data; list_element *next,*pre; }; //basic_linked_list class template<typename T> class basic_linked_list{ //Without new/delete.The name of a linked list class with new/delete will be "linked_list". public: list_element<T> datas[MAX_SIZE]; bool used[MAX_SIZE]; list_element<T> *head,*tail; //constructors basic_linked_list(){ // WARNING: The datas in this list will be RANDOM !!! And there is NO PLACE to INSERT !!! datas[0].next=&datas[1],datas[0].pre=0,used[0]=1,head=datas; int stsz_cut1=MAX_SIZE-1; //st==start,sz==size,cut1=="minus 1" for(int i=1;i<stsz_cut1;i++){ datas[i].next=&datas[i+1],datas[i].pre=&datas[i-1],used[i]=1; } datas[stsz_cut1].next=0,datas[stsz_cut1].pre=&datas[stsz_cut1-1],used[stsz_cut1]=1,tail=&datas[stsz_cut1]; } basic_linked_list(int start_size){ // WARNING: The datas in this list will be RANDOM !!! datas[0].next=&datas[1],datas[0].pre=0,used[0]=1,head=datas; int stsz_cut1=start_size-1; //st==start,sz==size,cut1=="minus 1" for(int i=1;i<stsz_cut1;i++){ datas[i].next=&datas[i+1],datas[i].pre=&datas[i-1],used[i]=1; } datas[stsz_cut1].next=0,datas[stsz_cut1].pre=&datas[stsz_cut1-1],used[stsz_cut1]=1,tail=&datas[stsz_cut1]; } basic_linked_list(T start_data,int start_size){ //if start_size==5 : 0/1/2/3/4 can be used datas[0].data=start_data,datas[0].next=&datas[1],datas[0].pre=0,used[0]=1,head=datas; int stsz_cut1=start_size-1; //st==start,sz==size,cut1=="minus 1" for(int i=1;i<stsz_cut1;i++){ datas[i].data=start_data,datas[i].next=&datas[i+1],datas[i].pre=&datas[i-1],used[i]=1; } datas[stsz_cut1].data=start_data,datas[stsz_cut1].next=0,datas[stsz_cut1].pre=&datas[stsz_cut1-1]; used[stsz_cut1]=1,tail=&datas[stsz_cut1]; } //inputs and outputs //input void input_cin(){ list_element<T> *p=head; for(;p!=tail;p=p->next){ cin>>p->data; } cin>>p->data; } void input_scanf(const char T_str[]="%d"){ list_element<T> *p=head; for(;p!=tail;p=p->next){ scanf(T_str,p->data); } scanf(T_str,p->data); } void input_cin(int size){ list_element<T> *p=head; for(int i=0;i<size;i++,p=p->next){ cin>>p->data; } } void input_scanf(int size,const char T_str[]="%d"){ list_element<T> *p=head; for(int i=0;i<size;i++,p=p->next){ scanf(T_str,&p->data); } } void input_cin(int begin,int end){ // [begin,end] list_element<T> *p=head; for(int i=0;i<begin;i++,p=p->next); for(int i=begin;i<end;i++,p=p->next){ cin>>p->data; } } void input_scanf(int begin,int end,const char T_str[]="%d"){ // [begin,end] list_element<T> *p=head; for(int i=0;i<begin;i++,p=p->next); for(int i=begin;i<end;i++,p=p->next){ scanf(T_str,&p->data); } } //output--from head void output_cout(){ list_element<T> *p=head; for(;p!=tail;p=p->next){ cout<<p->data<<" "; } cout<<p->data<<" "; } void output_printf(const char T_str[]="%d "){ list_element<T> *p=head; for(;p!=tail;p=p->next){ printf(T_str,p->data); } printf(T_str,p->data); } void output_cout(int size){ list_element<T> *p=head; for(int i=0;i<size;i++,p=p->next){ cout<<p->data<<" "; } } void output_printf(int size,const char T_str[]="%d "){ list_element<T> *p=head; for(int i=0;i<size;i++,p=p->next){ printf(T_str,p->data); } } void output_cout(int begin,int end){ // [begin,end] list_element<T> *p=head; for(int i=0;i<begin;i++,p=p->next); for(int i=begin;i<end;i++,p=p->next){ cout<<p->data<<" "; } } void output_printf(int begin,int end,const char T_str[]="%d "){ // [begin,end] list_element<T> *p=head; for(int i=0;i<begin;i++,p=p->next); for(int i=begin;i<end;i++,p=p->next){ printf(T_str,p->data); } } //output--from tail (it is said,reverse it) void output_cout_reverse(){ list_element<T> *p=tail; for(;p!=head;p=p->pre){ cout<<p->data<<" "; } cout<<p->data<<" "; } void output_printf_reverse(const char T_str[]="%d "){ list_element<T> *p=tail; for(;p!=head;p=p->pre){ printf(T_str,p->data); } printf(T_str,p->data); } void output_cout_reverse(int size){ list_element<T> *p=tail; for(int i=0;i<size;i++,p=p->pre){ cout<<p->data<<" "; } } void output_printf_reverse(int size,const char T_str[]="%d "){ list_element<T> *p=tail; for(int i=0;i<size;i++,p=p->pre){ printf(T_str,p->data); } } void output_cout_reverse(int begin,int end){ // [begin,end] list_element<T> *p=head; for(int i=0;i<end;i++,p=p->next); //find end for(int i=end;i>=begin;i--,p=p->pre){ cout<<p->data<<" "; } } void output_printf_reverse(int begin,int end,const char T_str[]="%d "){ // [begin,end] list_element<T> *p=head; for(int i=0;i<end;i++,p=p->next); //find end for(int i=end;i>=begin;i--,p=p->pre){ printf(T_str,p->data); } } //functions bool full(){ for(int i=0;i<MAX_SIZE;i++){ if(used[i]==0) return 0; } return 1; } list_element<T>* find(int pos){ //from 0 list_element<T> *p=head; for(int i=0;i<pos;i++,p=p->next); return p; } T find_data(int pos){ //from 0 list_element<T> *p=head; for(int i=0;i<pos;i++,p=p->next); return p->data; } T insert(int pos,T new_data){ // a b c d (e) // ^ : pos==c list_element<T> *findpos=find(pos); for(int i=MAX_SIZE-1;i>=0;i--){ if(!used[i]){ used[i]=1; if(findpos==head){ datas[i].data=new_data; datas[i].next=findpos; head=&datas[i]; return new_data; } datas[i].data=new_data; datas[i].next=findpos; datas[i].pre=findpos->pre; findpos->pre->next=&datas[i]; findpos->pre=&datas[i]; return new_data; } } } T insert_back(T new_data){ for(int i=MAX_SIZE-1;i>=0;i++){ if(!used[i]){ used[i]=1; datas[i].data=new_data; datas[i].pre=tail; tail->next=&datas[i]; tail=&datas[i]; return new_data; } } } T erase(int pos){ used[pos]=0; list_element<T> *findpos=find(pos); findpos->pre->next=findpos->next; findpos->next->pre=findpos->pre; return findpos->data; } }; #ifdef TYPEDEF typedef list_element<int> intnode; typedef list_element<double> realnode; typedef basic_linked_list<int> intlist; typedef basic_linked_list<double> reallist; #endif #endif
這時候我們發現,插入元素本來是O(1)的,現在卻變成了O(n),而且陣列的大小還是確定的。
所以,可以想到,用記憶體申請可以解決這些問題。
用到了記憶體申請後,就只用判斷要不要擴大了,如果資料到頂了就擴大。這樣可能會浪費一點空間(刪除後的元素還會佔空間卻不會被利用),但是插入元素函式的時間複雜度降到了O(1)。另外,拷貝建構函式和擴大空間函式也需要實現。
程式碼如下:
#ifndef LINKED_LIST #define LINKED_LIST #include<iostream> #include<cstdio> #include<cassert> using namespace std; template<typename T> class list_node{ public: T data; list_node<T> *next,*pre; }; template<typename T> class linked_list{ public: list_node<T> *datas; int length,used_length,used_pos; //used_pos:for insert list_node<T> *head,*tail; // (copy) constructors and destructors linked_list(){ datas=0,length=used_length=0,head=tail=0,used_pos=0; } linked_list(int size){ // WARNING: The datas in this pointer might be RANDOM !!! if(size>1){ datas=new list_node<T>[size],length=used_length=size,used_pos=size; datas[0].next=&datas[1],datas[0].pre=0,head=&datas[0]; for(int i=1;i<size-1;i++){ datas[i].next=&datas[i+1],datas[i].pre=&datas[i-1]; } datas[size-1].next=0,datas[size-1].pre=&datas[size-2],tail=&datas[size-1]; head->pre=0,tail->next=0; } else if(size==1){ datas=new list_node<T>[1],length=used_length=1,used_pos=1; datas[0].next=0,datas[0].pre=0,head=tail=&datas[0]; head->pre=0,tail->next=0; } else if(size==0){ datas=0,length=used_length=0,head=tail=0,used_pos=0; } else assert(0); } linked_list(T start_data,int size){ if(size>1){ datas=new list_node<T>[size],length=used_length=size,used_pos=size; datas[0].data=start_data,datas[0].next=&datas[1],datas[0].pre=0,head=&datas[0]; for(int i=1;i<size-1;i++){ datas[i].data=start_data,datas[i].next=&datas[i+1],datas[i].pre=&datas[i-1]; } datas[size-1].data=start_data,datas[size-1].next=0,datas[size-1].pre=&datas[size-2],tail=&datas[size-1]; head->pre=0,tail->next=0; } else if(size==1){ datas=new list_node<T>[1],length=used_length=1,used_pos=1; datas[0].data=start_data,datas[0].next=0,datas[0].pre=0,head=tail=&datas[0]; head->pre=0,tail->next=0; } else if(size==0){ datas=0,length=used_length=0,head=tail=0,used_pos=0; } else assert(0); } linked_list(const linked_list &list){ if(list.length>1){ delete[] datas; datas=new list_node<T>[list.length],length=list.length,used_pos=list.used_pos; int headnum=list.head-list.datas,tailnum=list.tail-list.datas; datas[0].data=list.datas[0].data,datas[0].next=&datas[list.datas[0].next-list.datas],datas[0].pre=0; head=&datas[headnum]; for(int i=1;i<length-1;i++){ int nextnum=list.datas[i].next-list.datas,prenum=list.datas[i].pre-list.datas; datas[i].data=list.datas[i].data,datas[i].next=&datas[nextnum],datas[i].pre=&datas[prenum]; } datas[length-1].data=list.datas[length-1].data,datas[length-1].next=0; tail=&datas[tailnum],datas[length-1].pre=&datas[list.datas[length-1].pre-list.datas]; head->pre=0,tail->next=0; } else if(list.length==1){ delete[] datas; datas=new list_node<T>[1],length=1,used_pos=1; datas[0].data=list.datas[0].data,datas[0].next=0,datas[0].pre=0,head=tail=&datas[0]; } else if(list.length==0){ datas=0,length=0,head=tail=0,used_pos=0; } else assert(0); } ~linked_list(){ if(datas){ delete[] datas; datas=0; } } //input and output //functions bool full(){ return length==used_length; } bool pos_full(){ return used_pos==length; } bool is_pos_ok(int pos){ return pos<used_length && pos>=0; } list_node<T>* find(int pos){ pos--; if(pos>used_length) return 0; list_node<T> *p=head; for(int i=0;i<pos;i++,p=p->next); return p; } T find_data(int pos){ pos--; if(pos>used_length) assert(0); list_node<T> *p=head; for(int i=0;i<pos;i++,p=p->next); return p->data; } T add_place(){cout<<" ADD PLACE "; int new_length=(length==0 ? 1 : length<<1); list_node<T> *new_datas=new list_node<T>[new_length]; int i=0; for(;i<length;i++){ int nextnum=datas[i].next-datas,prenum=datas[i].pre-datas; new_datas[i].data=datas[i].data,new_datas[i].next=&datas[nextnum],new_datas[i].pre=&datas[prenum]; } int headnum=head-datas,tailnum=tail-datas; used_pos=length,datas=new_datas,length=new_length,head=&datas[headnum],tail=&datas[tailnum]; head->pre=0,tail->next=0; // for(;i<length-1;i++){ // datas[i].next=&datas[i+1],datas[i].pre=&datas[i-1]; // } // datas[length-1].next=0,datas[length-1].pre=&datas[length-2]; } T insert(int pos,T new_data){ if(!is_pos_ok(pos)) assert("The wrong place is : in insert function,the argument \"pos\" is not ok."); if(pos_full()) add_place(); list_node<T> *findpos=find(pos); if(pos==0){ datas[used_pos].data=new_data; datas[used_pos].next=findpos; datas[used_pos].pre=0; findpos->pre=&datas[used_pos]; head=&datas[used_pos]; } else{ datas[used_pos].data=new_data; datas[used_pos].next=findpos; datas[used_pos].pre=findpos->pre; findpos->pre->next=&datas[used_pos]; findpos->pre=&datas[used_pos]; } used_pos++,used_length++; } T insert_back(T new_data){ if(pos_full()) add_place(); datas[used_pos].data=new_data; datas[used_pos].next=0; datas[used_pos].pre=tail->pre; tail->pre->next=&datas[used_pos]; tail->pre=&datas[used_pos]; tail=&datas[used_pos]; used_pos++,used_length++; } T erase(int pos){ if(!is_pos_ok(pos)) assert("The wrong place is :"=="in erase function,the argument \"pos\" is not ok."); list_node<T> *findpos=find(pos); findpos->pre->next=findpos->next; findpos->next->pre=findpos->pre; used_length--; } }; #endif
這裡有一個BUG,你們能發現嗎?(關於指標的)
在評論區中回覆這個BUG但沒有回覆解決方法的,測試通過(系統:Windows,IDE:Dev-C++,編譯器:g++)後會有獎勵:如果你寫了文章(原創/轉載/翻譯均可),那麼我會在幾篇文章(最多3篇,如果文章數量不夠3篇,那麼我會全點贊)中點贊。(截止時間:2018年12月31日23:59)
在評論區中回覆這個BUG和解決方法的,測試通過(系統:Windows,IDE:Dev-C++,編譯器:g++)後會有獎勵:如果你寫了文章(原創/轉載/翻譯均可),那麼我會在幾篇文章(最多3篇,如果文章數量不夠3篇,那麼我會全點贊)中點贊。(截止時間:2018年12月31日23:59)
除了綠色+下劃線部分以外的部分均可轉載。轉載時請附上本文連結:https://blog.csdn.net/weixin_41461277/article/details/84872583 。