问题:
如何遍历单链表中的每一个元素?
示例:
在头部插入元素时,时间复杂度是O(n)。 获取元素时,时间复杂度是O(n*n),因为内层定位位置时有一个O(n)复杂度。
从理论上来说遍历一个单链表,只需要线性的时间就够了。
设计思路:
提供一组相关的遍历函数,遍历时使用这些函数来操作:
move函数的i参数为目标位置,step参数为每次移动的节点数。
end用来判断当前的游标是否到达了单链表的尾部。
current返回当前游标指向的数据元素。
next移动游标,移动的次数根据move中的step的值来决定。
遍历时,这四个函数必须配合使用才能得到最大效率。
改进LinkList.h文件中的函数:
1 #ifndef LINKLIST_H 2 #define LINKLIST_H 3 4 #include "List.h" 5 #include "Exception.h" 6 7 namespace DTLib 8 { 9 10 template < typename T > 11 class LinkList : public List<T> 12 { 13 protected: 14 struct Node : public Object 15 { 16 T value; 17 Node* next; 18 }; 19 20 mutable struct : public Object 21 { 22 char reserved[sizeof(T)]; 23 Node* next; 24 }m_header; 25 26 int m_length; 27 int m_step; 28 Node* m_current; 29 30 Node* position(int i) const // O(n) 31 { 32 Node* ret = reinterpret_cast<Node*>(&m_header); 33 34 for(int p = 0; p < i; p++) 35 { 36 ret = ret->next; 37 } 38 39 return ret; 40 } 41 public: 42 LinkList() 43 { 44 m_header.next = NULL; 45 m_length = 0; 46 m_step = 1; 47 m_current = NULL; 48 } 49 50 bool insert(const T& e) 51 { 52 return insert(m_length, e); 53 } 54 55 bool insert(int i, const T& e) // O(n) 56 { 57 bool ret = ((0 <= i) && (i <= m_length)); 58 59 if( ret ) 60 { 61 Node* node = new Node(); 62 63 if( node != NULL ) 64 { 65 Node* current = position(i); 66 67 node->value = e; 68 node->next = current->next; 69 current->next = node; 70 71 m_length++; 72 } 73 else 74 { 75 THROW_EXCEPTION(NoEnoughMemoryException, "No memery to insert new element..."); 76 } 77 } 78 79 return ret; 80 } 81 82 bool remove(int i) // O(n) 83 { 84 bool ret = ((0 <= i) && (i < m_length)); 85 86 if( ret ) 87 { 88 Node* current = position(i); 89 90 Node* toDel = current->next; 91 92 current->next = toDel->next; 93 94 delete toDel; 95 96 m_length--; 97 } 98 99 return ret; 100 } 101 102 bool set(int i, const T& e) // O(n) 103 { 104 bool ret = ((0 <= i) && (i < m_length)); 105 106 if( ret ) 107 { 108 position(i)->next->value = e; 109 } 110 111 return ret; 112 } 113 114 T get(int i) const 115 { 116 T ret; 117 118 if( get(i, ret) ) 119 { 120 return ret; 121 } 122 else 123 { 124 THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ..."); 125 } 126 127 return ret; 128 } 129 130 bool get(int i, T& e) const // O(n) 131 { 132 bool ret = ((0 <= i) && (i < m_length)); 133 134 if( ret ) 135 { 136 e = position(i)->next->value; 137 } 138 139 return ret; 140 } 141 142 int find(const T& e) const // O(n) 143 { 144 int ret = -1; 145 int i = 0; 146 147 Node* node = m_header.next; 148 149 while( node ) 150 { 151 if( node->value == e ) 152 { 153 ret = i; 154 break; 155 } 156 else 157 { 158 node = node->next; 159 i++; 160 } 161 } 162 163 return ret; 164 } 165 166 int length() const // O(1) 167 { 168 return m_length; 169 } 170 171 void clear() // O(n) 172 { 173 while( m_header.next ) 174 { 175 Node* toDel = m_header.next; 176 177 m_header.next = toDel->next; 178 179 delete toDel; 180 } 181 182 m_length = 0; 183 } 184 185 bool move(int i, int step = 1) 186 { 187 bool ret = (0 <= i) && (i < m_length) && (step > 0); 188 189 if( ret ) 190 { 191 m_current = position(i)->next; 192 m_step = step; 193 } 194 195 return ret; 196 } 197 198 bool end() 199 { 200 return (m_current == NULL); 201 } 202 203 T current() 204 { 205 if( !end() ) 206 { 207 return m_current->value; 208 } 209 else 210 { 211 THROW_EXCEPTION(InvalidOperationException, "No value at current position ..."); 212 } 213 } 214 215 bool next() //每次移动step步 216 { 217 int i = 0; 218 219 while((i < m_step) && !end()) 220 { 221 m_current = m_current->next; 222 i++; 223 } 224 225 return (i == m_step); 226 } 227 228 ~LinkList() // O(n) 229 { 230 clear(); 231 } 232 }; 233 234 } 235 236 #endif // LINKLIST_H
第27、28行我们添加了两个成员变量,第185-226行添加了遍历需要的函数。
测试程序如下:
1 #include <iostream> 2 #include "LinkList.h" 3 4 5 using namespace std; 6 using namespace DTLib; 7 8 9 int main() 10 { 11 LinkList<int> list; 12 13 for(int i = 0; i<5; i++) 14 { 15 list.insert(0,i); 16 } 17 18 //遍历时必须先调用move函数 19 for(list.move(0); !list.end(); list.next()) 20 { 21 cout << list.current() << endl; 22 } 23 24 return 0; 25 }
遍历list时必须先调用move函数,指定位置和步进值step,step是给next函数用的,例如:step为5,则每次调用next函数就会向后移动5个元素。
结果如下:
这时的遍历操作时间复杂度是O(n)。
步进值为2时,结果如下:
单链表内部封装:
程序改进:
1 #ifndef LINKLIST_H 2 #define LINKLIST_H 3 4 #include "List.h" 5 #include "Exception.h" 6 7 namespace DTLib 8 { 9 10 template < typename T > 11 class LinkList : public List<T> 12 { 13 protected: 14 struct Node : public Object 15 { 16 T value; 17 Node* next; 18 }; 19 20 mutable struct : public Object 21 { 22 char reserved[sizeof(T)]; 23 Node* next; 24 }m_header; 25 26 int m_length; 27 int m_step; 28 Node* m_current; 29 30 Node* position(int i) const // O(n) 31 { 32 Node* ret = reinterpret_cast<Node*>(&m_header); 33 34 for(int p = 0; p < i; p++) 35 { 36 ret = ret->next; 37 } 38 39 return ret; 40 } 41 42 virtual Node* create() 43 { 44 return new Node(); 45 } 46 47 virtual void destroy(Node* pn) 48 { 49 delete pn; 50 } 51 52 public: 53 LinkList() 54 { 55 m_header.next = NULL; 56 m_length = 0; 57 m_step = 1; 58 m_current = NULL; 59 } 60 61 bool insert(const T& e) 62 { 63 return insert(m_length, e); 64 } 65 66 bool insert(int i, const T& e) // O(n) 67 { 68 bool ret = ((0 <= i) && (i <= m_length)); 69 70 if( ret ) 71 { 72 Node* node = create(); 73 74 if( node != NULL ) 75 { 76 Node* current = position(i); 77 78 node->value = e; 79 node->next = current->next; 80 current->next = node; 81 82 m_length++; 83 } 84 else 85 { 86 THROW_EXCEPTION(NoEnoughMemoryException, "No memery to insert new element..."); 87 } 88 } 89 90 return ret; 91 } 92 93 bool remove(int i) // O(n) 94 { 95 bool ret = ((0 <= i) && (i < m_length)); 96 97 if( ret ) 98 { 99 Node* current = position(i); 100 101 Node* toDel = current->next; 102 103 current->next = toDel->next; 104 105 destroy(toDel); 106 107 m_length--; 108 } 109 110 return ret; 111 } 112 113 bool set(int i, const T& e) // O(n) 114 { 115 bool ret = ((0 <= i) && (i < m_length)); 116 117 if( ret ) 118 { 119 position(i)->next->value = e; 120 } 121 122 return ret; 123 } 124 125 T get(int i) const 126 { 127 T ret; 128 129 if( get(i, ret) ) 130 { 131 return ret; 132 } 133 else 134 { 135 THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ..."); 136 } 137 138 return ret; 139 } 140 141 bool get(int i, T& e) const // O(n) 142 { 143 bool ret = ((0 <= i) && (i < m_length)); 144 145 if( ret ) 146 { 147 e = position(i)->next->value; 148 } 149 150 return ret; 151 } 152 153 int find(const T& e) const // O(n) 154 { 155 int ret = -1; 156 int i = 0; 157 158 Node* node = m_header.next; 159 160 while( node ) 161 { 162 if( node->value == e ) 163 { 164 ret = i; 165 break; 166 } 167 else 168 { 169 node = node->next; 170 i++; 171 } 172 } 173 174 return ret; 175 } 176 177 int length() const // O(1) 178 { 179 return m_length; 180 } 181 182 void clear() // O(n) 183 { 184 while( m_header.next ) 185 { 186 Node* toDel = m_header.next; 187 188 m_header.next = toDel->next; 189 190 destroy(toDel); 191 } 192 193 m_length = 0; 194 } 195 196 bool move(int i, int step = 1) 197 { 198 bool ret = (0 <= i) && (i < m_length) && (step > 0); 199 200 if( ret ) 201 { 202 m_current = position(i)->next; 203 m_step = step; 204 } 205 206 return ret; 207 } 208 209 bool end() 210 { 211 return (m_current == NULL); 212 } 213 214 T current() 215 { 216 if( !end() ) 217 { 218 return m_current->value; 219 } 220 else 221 { 222 THROW_EXCEPTION(InvalidOperationException, "No value at current position ..."); 223 } 224 } 225 226 bool next() //每次移动step步 227 { 228 int i = 0; 229 230 while((i < m_step) && !end()) 231 { 232 m_current = m_current->next; 233 i++; 234 } 235 236 return (i == m_step); 237 } 238 239 ~LinkList() // O(n) 240 { 241 clear(); 242 } 243 }; 244 245 } 246 247 #endif // LINKLIST_H
调用new生成Node的地方我们全换成了create函数,调用delete销毁Node的地方我们全换成了destroy函数。
问题:
封装这两个函数的意义是什么?
小结: