「数据结构」:模拟指针(simulated pointer)
模拟指针,也就是清华严老师《数据结构-C语言描述》中的静态链表,静态链表的引用是使用一段连续的存储区还模拟指针的功能,可以有效的利用一段连续内存进行一定范围内可变的子链表的空间分配,此数据结构原理比较简单,但是实现起来(至少我个人感觉)有一些绕,原因在于结点的指针域和所申请的整个空间数组的下标都是用整型来表示,极易出错,由于使用连续存储区,稍有不甚将指针地址错写成数组下标则很容易出错并且很难被发现。
以下为本次实现的模拟指针代码,由三个文件构成:
1、 simulatedPointer.h 定义了模拟指针的一些基本结构和方法
2、excp.h 和前面一样,包含定制的异常类。
3、simulatedPointer.cp 测试用的主函数
1 //simulatedPointer.h 2 #ifndef __SIMULATEDPOINTER__ 3 #define __SIMULATEDPOINTER__ 4 #include <iostream> 5 #include "excp.h" 6 using namespace std; 7 8 template<class T> 9 class SimPointerList; 10 11 template<class T> 12 class Node { 13 public: 14 friend class SimPointerList<T>; 15 private: 16 T data; 17 int next; 18 }; 19 20 template<class T> 21 class SimPointerList { 22 public: 23 SimPointerList(int maxaSize = 10); 24 ~SimPointerList(){delete [] list;} 25 int getFreeNode(); 26 void recycleFreeNode(int i); 27 bool isEmpty() const; 28 int getLength() const; 29 SimPointerList<T>& deleteElement(int k, T& x); 30 SimPointerList<T>& insertElement(int k, const T& x); 31 void findElement(int k, T& x) const; 32 int searchElement(const T& x) const; 33 void output(ostream& out) const; 34 int getFreeCapacity() const; 35 private: 36 int freeHeader;//point to the head of the free-list 37 int usedHeader; 38 int maxSize; 39 Node<T> *list; 40 }; 41 42 template<class T> 43 SimPointerList<T>::SimPointerList(int maxaSize) 44 { 45 maxSize = maxaSize; 46 freeHeader = 0; 47 usedHeader = -1; 48 list = new Node<T>[maxSize]; 49 for (int i=0; i<maxSize-1;i++) 50 list[i].next = i+1; 51 list[maxSize-1].next = -1; 52 } 53 54 template<class T> 55 int SimPointerList<T>::getFreeNode() 56 { 57 if (freeHeader == -1) 58 throw FullMemory(); 59 int i = freeHeader; 60 freeHeader = list[i].next; 61 list[i].next=-1; 62 return i; 63 } 64 65 template<class T> 66 void SimPointerList<T>::recycleFreeNode(int i) 67 { 68 if (i<0 || i>=maxSize) 69 throw OutOfBounds(); 70 list[i].next = freeHeader; 71 freeHeader = i; 72 } 73 74 template<class T> 75 bool SimPointerList<T>::isEmpty() const 76 { 77 return usedHeader == -1; 78 } 79 80 template<class T> 81 int SimPointerList<T>::getLength() const 82 { 83 int len = 0; 84 if (usedHeader != -1) 85 for (int begin=usedHeader; begin!=-1; begin=list[begin].next) 86 len++; 87 return len; 88 } 89 90 template<class T> 91 SimPointerList<T>& SimPointerList<T>::deleteElement(int k, T& x) 92 { 93 if (k<=0 || k>getLength()) 94 throw OutOfBounds(); 95 int index = 0; 96 if (k == 1) 97 { 98 index = usedHeader; 99 usedHeader = list[usedHeader].next; 100 } 101 else if (getLength()==2 && k == 2) 102 { 103 index = list[usedHeader].next; 104 list[usedHeader].next = -1; 105 } 106 else 107 { 108 int prior = usedHeader; 109 for (int i=1; i<k-1; i++) 110 prior = list[prior].next; 111 index = list[prior].next; 112 list[prior].next = list[list[prior].next].next; 113 } 114 x = list[index].data; 115 list[index].next = -1; 116 recycleFreeNode(index); 117 return *this; 118 } 119 120 template<class T> 121 SimPointerList<T>& SimPointerList<T>::insertElement(int k, const T& x) 122 { 123 int len = getLength(); 124 if (k<1 || k>len+1) 125 throw OutOfBounds(); 126 if (len == maxSize) 127 throw FullMemory(); 128 int index = getFreeNode();//.next field has been setted by -1 129 list[index].data = x; 130 if (usedHeader == -1 || k == 1) 131 { 132 list[index].next = usedHeader; 133 usedHeader = index; 134 return *this; 135 } 136 137 int begin = usedHeader; 138 for (int i=1; i<k-1; i++) 139 begin = list[begin].next; 140 list[index].next = list[begin].next; 141 list[begin].next = index; 142 return *this; 143 } 144 145 template<class T> 146 int SimPointerList<T>::getFreeCapacity() const 147 { 148 return maxSize - getLength(); 149 } 150 151 template<class T> 152 void SimPointerList<T>::findElement(int k, T& x) const 153 { 154 if (k<=0 || k>getLength()) 155 throw OutOfBounds(); 156 int index = usedHeader; 157 for (int i=1; i<k; i++) 158 index = list[index].next; 159 x = list[index].data; 160 } 161 162 template<class T> 163 int SimPointerList<T>::searchElement(const T& x) const 164 { 165 int index = usedHeader; 166 int len = 0; 167 while (index != -1) 168 { 169 if (list[index].data == x) 170 break; 171 index = list[index].next; 172 len++; 173 } 174 return len; 175 } 176 177 template<class T> 178 void SimPointerList<T>::output(ostream& out) const 179 { 180 int len = usedHeader; 181 while (len != -1) 182 { 183 out<<list[len].data<<" "; 184 len = list[len].next; 185 } 186 } 187 188 template<class T> 189 ostream& operator<<(ostream& out, const SimPointerList<T>& x) 190 { 191 x.output(out); 192 return out; 193 } 194 #endif
1 //excp.h 2 #ifndef _EXCP_ 3 #define _EXCP_ 4 #include <new> 5 using namespace std; 6 7 class OutOfBounds { 8 public: 9 OutOfBounds(){} 10 }; 11 12 class FullMemory { 13 public: 14 FullMemory(){} 15 }; 16 17 void my_new_handler() 18 { 19 throw FullMemory(); 20 } 21 22 new_handler old_handler = set_new_handler(my_new_handler); 23 #endif
1 //simulatedPointer.cpp 2 #include <iostream> 3 #include "excp.h" 4 #include "simulatedPointer.h" 5 using namespace std; 6 7 int main() 8 { 9 try{ 10 SimPointerList<int> L(10); 11 cout<<"length : "<<L.getLength()<<endl; 12 L.insertElement(1, 2).insertElement(2, 6).insertElement(2, 4).insertElement(1, 5); 13 cout<<"length : "<<L.getLength()<<endl; 14 int x; 15 cout<<L<<endl; 16 L.findElement(2, x); 17 cout<<"The second element is : "<<x<<endl; 18 L.deleteElement(2, x); 19 cout<<"delete : "<<x<<endl; 20 cout<<"After delete : "<<L<<endl; 21 cout<<"new length is : "<<L.getLength()<<endl; 22 L.insertElement(2, 3).insertElement(1, 10); 23 cout<<"Least : "<<L<<endl; 24 cout<<"Now the length is : "<<L.getLength()<<endl; 25 } 26 catch(OutOfBounds e) 27 { 28 cout<<"out of bounds error occured"<<endl; 29 } 30 catch(FullMemory e) 31 { 32 cout<<"full memory error occured"<<endl; 33 } 34 return 0; 35 }
总结:
1、如果要以传统思维来规范数组位置(下标从1开始),那就时刻要注意,在代码中时时小心处理和机器表示的区别,或者浪费一个存储空间list[0]以保持一致,
2、要区分开模拟的链表下标和数组下标(因为这个,找了近两个小时的BUG,整个人都快疯掉了)。
3、代码注意规范,变量、方法名尽量表达内丰含意。