單鏈表:
一.單鏈表與順序表相比:
1.順序表可以方便的隨機存取表中的任一節點,速度快;但是在表中插入刪除一個數據時,為了保持其他元素的相對次序不變,平均需要移動一半的元素,效率很低;還有若事先對表長估計不足,過小會形成內存浪費,過大則需要拷貝到一個更大的數組,時間開銷很大。相反,鏈表則適用於插入刪除頻繁,表長估計不定的情形。
2.順序表邏輯位置和物理位置都連續,而單鏈表中的邏輯位置連續,物理位置非連續。
二.為什么要帶附加表頭?
因為不帶附加表頭在插入刪除時要分兩種情況:操作節點在表頭和不在表頭;而帶了附加表頭便可以對所有節點一視同仁。
1 template<class T> 2 struct LinkNode{//鏈表節點 3 T data; 4 LinkNode *link; 5 LinkNode(const T& args,LinkNode<T> *ptr=NULL){ 6 data=args; 7 link=ptr; 8 } 9 }; 10 11 template<class T> 12 class List{//帶附加頭節點的單鏈表 13 protected: 14 LinkNode<T> *first;//鏈表頭指針 15 public: 16 List(){ 17 first=new LinkNode<T>; 18 } 19 List(const T& x){ 20 first=new LinkNode<T>(x); 21 } 22 List(List<T>& L){ 23 T value; 24 LinkNode<T> *L_head=L.getHead(); 25 LinkNode<T> *temp=first=new LinkNode<T>; 26 while(L_head->link!=NULL){ 27 value=L_head->link->data; 28 temp->link=new LinkNode<T>(value); 29 L_head=L_head->link; 30 temp=temp->link; 31 } 32 temp->link=NULL; 33 } 34 ~List(){ 35 makeEmpty(); 36 } 37 void makeEmpty(){//將鏈表置空 38 LinkNode<T> *temp; 39 while(first->link!=NULL){ 40 temp=first->link; 41 first->link=temp->link; 42 delete temp; 43 } 44 } 45 int length()const{ 46 LinkNode<T> *temp=first->link; 47 int count=0; 48 while(temp!=NULL){ 49 temp=temp->link; 50 count++; 51 } 52 return count; 53 } 54 LinkNode<T> *getHead()const{//返回附加頭節點地址 55 return first; 56 } 57 LinkNode<T> *search(T x,int& i){//搜索數據x的地址和序列號 58 LinkNode<T> *current=first->link; 59 int index=1; 60 while(current!=NULL){ 61 if(current->data==x) 62 break; 63 current=current->link; 64 index++; 65 } 66 i=index; 67 return current; 68 } 69 LinkNode<T> *locate(int i){//搜索第i個節點的地址 70 if(i<0) 71 return NULL; 72 LinkNode<T> *current=first; 73 int temp=0; 74 while(current!=NULL&&temp<i){ 75 current=current->link; 76 temp++; 77 } 78 return current; 79 } 80 bool getData(int i,T& x){//取出第i個元素的值 81 if(i<=0) 82 return false; 83 LinkNode<T> *current=locate(i); 84 if(current==NULL) return false; 85 x=current->data; 86 return true; 87 } 88 void setData(int i,T& x){//修改第i個元素的值 89 if(i<=0) 90 return; 91 LinkNode<T> *current=locate(i); 92 if(current==NULL) return; 93 current->data=x; 94 } 95 bool insert(int i,T& x){//在第i個元素后插入x 96 LinkNode<T> *current=locate(i); 97 if(current==NULL) return false; 98 LinkNode<T> *newNode=new LinkNode<T>(x); 99 newNode->link=current->link; 100 current->link=newNode; 101 return true; 102 } 103 bool remove(int i,T& x){//刪除第i個元素的值 104 LinkNode<T> *current=locate(i-1); 105 if(current==NULL||current->link==NULL) return false; 106 LinkNode<T> *del=current->link; 107 current->link=del->link; 108 x=del->data; 109 delete del; 110 return true; 111 } 112 bool isEmpty()const{ 113 return (first->link==NULL)?true:false; 114 } 115 void inputFront(){//向前插入鏈表法 116 makeEmpty(); 117 LinkNode<T> *newNode; 118 T value; 119 cin>>value; 120 while(value!=-1){ 121 newNode=new LinkNode<T>(value); 122 newNode->link=first->link; 123 first->link=newNode; 124 cin>>value; 125 } 126 } 127 void inputRear(){//向后插入鏈表法 128 makeEmpty(); 129 LinkNode<T> *newNode,*last=first; 130 T value; 131 cin>>value; 132 while(value!=-1){ 133 newNode=new LinkNode<T>(value); 134 last->link=newNode; 135 last=newNode; 136 cin>>value; 137 } 138 } 139 void output(){//輸出整個鏈表 140 LinkNode<T> *current=first->link; 141 while(current!=NULL){ 142 cout<<current->data<<" "; 143 current=current->link; 144 } 145 cout<<endl; 146 } 147 };
測試代碼如下:
1 void menu(){ 2 cout<<"1.向前插入建表(-1結束)"<<endl; 3 cout<<"2.向后插入建表(-1結束)"<<endl; 4 cout<<"3.輸出鏈表"<<endl; 5 cout<<"4.搜索元素x所在節點的序號"<<endl; 6 cout<<"5.取出第i個元素的值"<<endl; 7 cout<<"6.用x的值修改第i個元素的值"<<endl; 8 cout<<"7.刪除第i個元素"<<endl; 9 cout<<"8.在第i個元素后面插入x"<<endl; 10 cout<<"9.退出"<<endl; 11 } 12 13 template<class T> 14 void function(int num,List<T> *list){ 15 switch(num){ 16 int x,i; 17 case 1: 18 list->inputFront(); 19 break; 20 case 2: 21 list->inputRear(); 22 break; 23 case 3: 24 list->output(); 25 break; 26 case 4: 27 cin>>x; 28 list->search(x,i); 29 cout<<i<<endl; 30 break; 31 case 5: 32 cin>>i; 33 list->getData(i,x); 34 cout<<x<<endl; 35 break; 36 case 6: 37 cin>>x>>i; 38 list->setData(i,x); 39 break; 40 case 7: 41 cin>>i; 42 list->remove(i,x); 43 break; 44 case 8: 45 cin>>i>>x; 46 list->insert(i,x); 47 break; 48 default: 49 exit(1); 50 } 51 } 52 int main(int argc, char** argv) { 53 int num; 54 List<int> *list=new List<int>(-1); 55 while(true){ 56 menu(); 57 cin>>num; 58 function(num,list); 59 } 60 return 0; 61 }
其次是循環鏈表:
代碼我就不放了,就講講要修改的地方:
1.構造函數。first=new LinkNode<T>;后面接上:first->link=first;目的是讓自身首尾相連。如圖:
2.在判斷循環指針是否到達表尾的條件要從NULL換成first。
小結:
循環鏈表的目的是只要知道表中任一一個節點的地址,就能遍歷表中其他任一節點。
最后是雙向鏈表:
修改:
1.節點的構造函數。需要增加一個前驅指針。
2.搜索、插入刪除算法。首先是方向,確定方向后算法和單鏈表差不多,區別在於通過前驅指針還是后續指針訪問節點。其次是插入刪除,講起來比較啰嗦,直接看圖:
這是插入(中間是插入元素),虛線是原來的情況,實線是插入后的情況,可見一共涉及到4個指針。
這是刪除(中間是刪除元素)。看上去也是涉及4個指針,實際只動了1和4。
小結:
雙向鏈表的目的是為了解決在鏈表中不同方向(前/后)訪問元素的問題。