劍指offer練習-連結串列
阿新 • • 發佈:2018-11-09
由於最近太忙,幾個連結串列的題目都寫在了一個解決方案裡了,程式碼可能有點亂,以下先提交程式碼,思路都在註釋裡,後期有時間了再整理筆記
1 // printListFromTailToHead.cpp: 定義控制檯應用程式的入口點。 2 // 3 4 #include "stdafx.h" 5 #include<iostream> 6 #include<vector> 7 #include<queue> 8 #include<set> 9 #include<stack> 10 #include<malloc.h> 11 #include<map> 12 #include<debugapi.h> 13 using namespace std; 14 15 #define N 40 16 struct ListNode { 17 int val; 18 struct ListNode *next; 19 20 }; 21 struct RandomListNode {//複雜連結串列 22 int label; 23 struct RandomListNode *next, *random; 24 RandomListNode(int x) : 25 label(x), next(NULL), random(NULL) { 26 } 27 }; 28 class Solution { 29 public: 30 vector<int> printListFromTailToHead(ListNode *head) {//將連結串列逆序列印 31 /*思路 32 通常,這種情況下,我們不希望修改原連結串列的結構。返回一個反序的連結串列,這就是經典的“後進先出”,我們可以使用棧實現 33 這種順序。每經過一個結點的時候,把該結點放到一個棧中。 34 當遍歷完整個連結串列後,再從棧頂開始逐個輸出結點的值,給一個新的連結串列結構,這樣連結串列就實現了反轉。*/ 35 stack<int> nodes; 36 ListNode *node = head; 37 vector<int> result; 38 while (node != NULL) { 39 nodes.push(node->val); 40 node = node->next; 41 } 42 while (!nodes.empty()) { 43 result.push_back(nodes.top()); 44 nodes.pop(); 45 } 46 return result; 47 } 48 ListNode* CreateListNode(int num, int nodes[]) {//建立連結串列 49 ListNode *p, *q; 50 ListNode *listnode; 51 listnode = (ListNode *)malloc(sizeof(ListNode));//分配空間,也可以用c++的new 52 listnode->next = NULL; 53 p = listnode; 54 for (int i = 0; i<num; i++) { 55 q = (ListNode *)malloc(sizeof(ListNode)); 56 q->val = nodes[i]; 57 p->next = q; 58 p = q; 59 } 60 p->next = NULL; 61 return listnode; 62 } 63 ListNode* CreateListNodeWithHead(int num, int nodes[]) {//建立連結串列 64 ListNode *p, *q; 65 ListNode *listnode; 66 listnode = (ListNode *)malloc(sizeof(ListNode));//分配空間,也可以用c++的new 67 //listnode->next = NULL; 68 p = listnode; 69 for (int i = 0; i<num; i++) { 70 q = (ListNode *)malloc(sizeof(ListNode)); 71 q->val = nodes[i]; 72 p->next = q; 73 p = q; 74 } 75 listnode = listnode->next;//這裡將頭結點指向下一個結點,是為了使頭結點數值有意義,注意與上一個建立方法的程式碼的兩處區別 76 p->next = NULL; 77 return listnode; 78 } 79 void PrintListNode(ListNode *listnode) {//遍歷輸出連結串列 80 ListNode *p = listnode->next;//因為建立的連結串列頭結點數值為空,所以要從頭結點下一個結點開始列印 81 //cout <<"test"<< listnode->val<<"test"; 82 while (p!= NULL) { 83 cout << p->val; 84 p = p->next; 85 } 86 cout << endl; 87 } 88 void PrintReverseListNode(ListNode *listnode) {//遍歷輸出逆置後的連結串列,因為逆置之後的頭結點數值非空,所以要從頭結點開始列印 89 ListNode *p = listnode; 90 //cout << "test" << listnode->val << "test"; 91 while (p->next!= NULL) {//在逆置的時候,逆置後的連結串列的尾結點是原連結串列的頭結點,所以輸出的時候不要把尾結點輸出 92 cout << p->val; 93 p = p->next; 94 } 95 cout << endl; 96 } 97 ListNode* FindKthToTail(ListNode *plistnode,int k) {//找到連結串列中倒數第k個元素 98 /*思路 99 我們可以定義兩個指標。第一個指標從連結串列的頭指標開始遍歷向前走k-1,第二個指標保持不動;從第k步開始, 100 第二個指標也開始從連結串列的頭指標開始遍歷。由於兩個指標的距離保持在k-1, 101 當第一個(走在前面的)指標到達連結串列的尾結點時,第二個指標(走在後面的)指標正好是倒數第k個結點。*/ 102 if (plistnode == NULL || k ==0) { 103 return NULL; 104 } 105 ListNode *palistnode; 106 ListNode *pblistnode; 107 palistnode = plistnode; 108 pblistnode = plistnode; 109 for (int i = 0; i < k-1; i++) { 110 if(palistnode->next != NULL) { 111 palistnode = palistnode->next; 112 } 113 else { 114 return NULL; 115 } 116 } 117 while (palistnode->next != NULL) { 118 palistnode = palistnode->next; 119 pblistnode = pblistnode->next; 120 } 121 return pblistnode; 122 } 123 ListNode* ReverseList(ListNode *head) {//將連結串列逆置 124 /*思路 125 這個很簡單,我們使用三個指標,分別指向當前遍歷到的結點、它的前一個結點以及後一個結點。 126 在遍歷的時候,做當前結點的尾結點和前一個結點的替換。 127 */ 128 ListNode *Cur, *Nex, *Pre; 129 if (head == NULL) 130 return NULL; 131 if (head->next == NULL) 132 return head; 133 Cur = head; 134 Nex = NULL; 135 Pre = NULL; 136 while (Cur != NULL) { 137 Nex = Cur->next; 138 Cur->next = Pre; 139 Pre = Cur; 140 Cur = Nex; 141 } 142 return Pre; 143 } 144 ListNode* Merge(ListNode *listnode1, ListNode *listnode2) {//合併兩個有序連結串列,還可以使用遞迴演算法合併兩個有序連結串列 145 146 if (listnode1== NULL) 147 return listnode2; 148 if (listnode2 == NULL) 149 return listnode1; 150 ListNode *p = listnode1->next;//因為這兩個連結串列建立的時候頭結點數值為空,所以要從頭結點下一個結點開始遍歷 151 ListNode *q = listnode2->next; 152 ListNode *result; 153 ListNode *t; 154 result = (ListNode *)malloc(sizeof(ListNode)); 155 result->next = NULL; 156 ListNode *temp = result; 157 while (p != NULL&&q != NULL) { 158 if (p->val < q->val) { 159 t= (ListNode *)malloc(sizeof(ListNode)); 160 t->val = p->val; 161 temp->next = t; 162 temp = t; 163 p = p->next; 164 } 165 else { 166 t = (ListNode *)malloc(sizeof(ListNode)); 167 t->val = q->val; 168 temp->next = t; 169 temp = t; 170 q = q->next; 171 } 172 } 173 while (p != NULL) { 174 t = (ListNode *)malloc(sizeof(ListNode)); 175 t->val = p->val; 176 temp->next = t; 177 temp = t; 178 p = p->next; 179 } 180 while (q != NULL) { 181 t = (ListNode *)malloc(sizeof(ListNode)); 182 t->val = q->val; 183 temp->next = t; 184 temp = t; 185 q = q->next; 186 } 187 temp->next = NULL; 188 return result; 189 } 190 ListNode* MergeWithRecursion(ListNode *listnode1, ListNode *listnode2) {//遞迴合併兩個有序連結串列 191 if (listnode1 == NULL) 192 return listnode2; 193 if (listnode2 == NULL) 194 return listnode1; 195 ListNode *listnode =NULL; 196 if (listnode1->val < listnode2->val) { 197 listnode = listnode1; 198 listnode->next = MergeWithRecursion(listnode1->next, listnode2); 199 } 200 else { 201 listnode = listnode2; 202 listnode->next = MergeWithRecursion(listnode1, listnode2->next); 203 } 204 return listnode; 205 } 206 RandomListNode* CloneWithMap(RandomListNode *pHead) {//複製複雜的連結串列:節點可以有兩個指向,一個指向下一節點,一個指向隨機節點 207 /* 208 方法一思路:首先複製原連結串列普通指標域,一次遍歷即可,複製過程中將新舊連結串列中的節點一一對應,將其對映關係放入到 209 map<RandomListNode *, RandomListNode *> nodeMap中,然後複製隨機指標域,先找到隨機指標指向的隨機節點,由於map中對應的有該隨機節點對應的 210 新節點,所以也就找到了隨機指標指向的結點對應的新節點,該新節點即為新連結串列前結點對應的隨機指標指向的結點,然後新節點隨機指標指向該節點,時間複雜度:O(N) 211 */ 212 if (pHead = NULL) { 213 return NULL; 214 } 215 map<RandomListNode *, RandomListNode *> nodeMap; 216 RandomListNode *currNode = pHead; 217 RandomListNode *newHead = NULL, *preNode = NULL, *newNode = NULL; 218 //首先複製原連結串列的普通指標域,一次遍歷即可完成 219 while (currNode != NULL) { 220 newNode = new RandomListNode(currNode->label); 221 nodeMap[currNode] = newNode; 222 if (preNode == NULL) { 223 newHead = newNode; 224 } 225 else 226 { 227 preNode->next = newNode; 228 } 229 preNode = newNode; 230 } 231 // 接著複製隨機指標域, 需要兩次遍歷 232 currNode = pHead; 233 newNode = newHead; 234 while (currNode != NULL&&newNode != NULL) { 235 RandomListNode *randNode = currNode->random; 236 RandomListNode *newRandNode = nodeMap[randNode]; 237 newNode->random = newRandNode;//這裡找到原連結串列對應的隨機指標指向的結點後,再從map中找到對應的新節點,然後將新連結串列隨機指標指向該新節點 238 currNode = currNode->next; 239 newNode = newNode->next; 240 } 241 return newHead; 242 } 243 RandomListNode* CloneTogether(RandomListNode *pHead) { 244 /* 245 我們需要的就只是能夠建立新節點與原節點之前的對應關係就可以了, 連結串列中的順序非隨機訪問方式,能夠很簡單的通過接單的nex指標t域查詢下一個節點 246 ,那麼我們將新節點直接插入到原結點的後面,這樣可以很方便的通過原來節點找到新節點, 247 1.遍歷一遍原始連結串列,複製結點N對應的N’,將其插入到結點N的後面 248 2.確定每個隨機指標的指向,只需遍歷一遍連結串列即可確定每個結點的隨機指標的指向 249 次遍歷一遍,將原始連結串列和複製連結串列分開,奇數為原始連結串列,偶數為複製連結串列 250 */ 251 if (pHead = NULL) { 252 return NULL; 253 } 254 RandomListNode *currNode = pHead; 255 RandomListNode *newHead = NULL, *preNode = NULL, *newNode = NULL; 256 while (currNode != NULL) { 257 if ((newNode = new RandomListNode(currNode->label)) == NULL) { 258 perror("new error:"); 259 exit(-1); 260 } 261 // 將新的節點newNode連線在currNode的後面 262 newNode->next = currNode->next; 263 currNode->next = newNode; 264 // 指向指向下一個節點 265 currNode = newNode->next; 266 } 267 // 接著複製隨機指標域,原來節點的下一個位置就是其對應的新節點 268 currNode = pHead; 269 newNode = currNode->next; 270 while (currNode != NULL) { 271 RandomListNode *randNode = currNode->random;// 隨機指標域randNode 272 RandomListNode *newNode = currNode->next; 273 if (randNode != NULL) { 274 newNode->random = randNode->next;//新節點的隨機指標指向的節點即為舊節點的隨機指標指向的節點指向的下一個節點 275 } 276 else 277 { 278 newNode->random = NULL; 279 } 280 currNode = newNode->next;// 連結串列同步移動 281 } 282 // 將連結在一起的新舊兩個連結串列拆分開,脫鏈,更新各連結串列的next指標 283 currNode = pHead; 284 newNode = newHead = pHead->next; 285 while (currNode != NULL) { 286 currNode->next = newNode->next; 287 if (newNode->next != NULL) { 288 newNode->next = newNode->next->next; 289 } 290 else { 291 newNode->next = NULL; 292 } 293 currNode = currNode->next; 294 newNode = newNode->next; 295 } 296 return newHead; 297 } 298 void printReverse() {//用於使用者互動來轉置連結串列 299 int num; 300 //ListNode *listnode;//要建立的連結串列 301 int listnodes[N]; 302 cout << "請輸入連結串列大小:" << endl; 303 cin >> num; 304 cout << "請依次輸入連結串列每個節點數值:" << endl; 305 for (int i = 0; i<num; i++) { 306 cin >> listnodes[i]; 307 } 308 Solution solution; 309 ListNode *listnode = solution.CreateListNode(num, listnodes); 310 solution.PrintListNode(listnode); 311 ListNode *result = solution.ReverseList(listnode); 312 solution.PrintReverseListNode(result); 313 } 314 void printMerge() {//用於使用者互動有序連結串列合併過程 315 Solution solution; 316 int num1, num2; 317 ListNode *listnode1, *listnode2; 318 int listnodes1[N], listnodes2[N]; 319 ListNode *result2; 320 cout << "請輸入第一個連結串列大小:" << endl; 321 cin >> num1; 322 cout << "請按照數值遞增順序依次輸入第一個連結串列每個節點數值:" << endl; 323 for (int i = 0; i<num1; i++) { 324 cin >> listnodes1[i]; 325 } 326 //Solution solution; 327 listnode1 = solution.CreateListNode(num1, listnodes1); 328 solution.PrintListNode(listnode1); 329 cout << "請輸入第二個連結串列大小:" << endl; 330 cin >> num2; 331 cout << "請按照數值遞增順序依次輸入第二個連結串列每個節點數值:" << endl; 332 for (int i = 0; i<num2; i++) { 333 cin >> listnodes2[i]; 334 } 335 listnode2 = solution.CreateListNode(num2, listnodes2); 336 solution.PrintListNode(listnode2); 337 cout << "合併兩個有序連結串列" << endl; 338 //如果使用遞迴呼叫的話要把連結串列移到頭結點的額下一個結點,不然會把頭結點的空值也合併進去了 339 listnode1 = listnode1->next; 340 listnode2 = listnode2->next; 341 result2 = solution.MergeWithRecursion(listnode1, listnode2); 342 solution.PrintReverseListNode(result2); 343 } 344 void printprintListFromTailToHead() {//使用者互動從尾到頭列印連結串列過程 345 int num; 346 //ListNode *listnode;//要建立的連結串列 347 int listnodes[N]; 348 cout << "請輸入連結串列大小:" << endl; 349 cin >> num; 350 cout << "請依次輸入連結串列每個節點數值:" << endl; 351 for (int i = 0; i<num; i++) { 352 cin >> listnodes[i]; 353 } 354 Solution solution; 355 ListNode *listnode = solution.CreateListNode(num, listnodes); 356 solution.PrintListNode(listnode); 357 vector<int> result; 358 result = solution.printListFromTailToHead(listnode); 359 cout << endl << "逆序輸出該連結串列" << endl; 360 for (int i = 0; i < num; i++) { 361 cout << result[i]; 362 } 363 cout << endl; 364 } 365 void printFindKthToTail() {//使用者互動列印倒數第K個值 366 int num; 367 //ListNode *listnode;//要建立的連結串列 368 int listnodes[N]; 369 cout << "請輸入連結串列大小:" << endl; 370 cin >> num; 371 cout << "請依次輸入連結串列每個節點數值:" << endl; 372 for (int i = 0; i<num; i++) { 373 cin >> listnodes[i]; 374 } 375 Solution solution; 376 ListNode *listnode = solution.CreateListNode(num, listnodes); 377 solution.PrintListNode(listnode); 378 int k; 379 cout << "請輸入K值" << endl; 380 cin >> k; 381 ListNode *result = solution.FindKthToTail(listnode, k); 382 cout << result->val; 383 } 384 }; 385 int main() { 386 /*int num; 387 //ListNode *listnode;//要建立的連結串列 388 int listnodes[N]; 389 cout << "請輸入連結串列大小:" << endl; 390 cin >> num; 391 cout << "請依次輸入連結串列每個節點數值:" << endl; 392 for (int i = 0; i<num; i++) { 393 cin >> listnodes[i]; 394 } 395 Solution solution; 396 ListNode *listnode=solution.CreateListNode(num, listnodes); 397 solution.PrintListNode(listnode);*/ 398 Solution solution; 399 solution.printReverse(); 400 }