【演算法篇】連結串列專題
前言:
從今天開始要攻克演算法專題了,今天是連結串列篇,關於連結串列相關的考題,不會太多涉及時間複雜度,而主要考察連結串列和指標操作;為啥大廠喜歡考察資料結構和演算法?因為這些是對基本功的昇華,不會考察陣列指標、函式指標等,考一個連結串列,就能考察對指標的理解,我相信不理解指標,連結串列學起來很費勁!
一、簡介
我會一個模組一個模組進行學習和練習,練習時我會從leetcode上選題,都知道leetcode吧?是OJ中最權威的平臺了,在上面可以找演算法題和練習,很好的一個網站,每一個題都會說明leetcode的第幾題,方便大家查詢和練習。
二、反轉連結串列
LeetCode上第206題:Reverse Linked List,官網是英文,但鑑於英文對一些人看起來比較費勁,翻譯成中文,如下:
反轉單鏈表。 例子: 輸入:1 - > 2 - > 3 - > 4 - > 5 - > NULL 輸出:5 - > 4 - > 3 - > 2 - > 1 - > NULL 跟進: 連結串列可以迭代或遞迴地反轉。你能實現這兩個嗎?
可能有人會想到直接去改連結串列節點裡的值,這是不允許,一般都是操作next指標,去改變指標指向;畫圖進行講解,如下:
需要三個指標pre/cur/next去反轉,將2位置指向pre位置,pre指向1號位置,1號位置指向3號位置,這樣就可以進行反轉了。程式碼如下:
structListNode { int val; ListNode *next; ListNode(int x) : val(x), next(NULL) {} }; // 206. Reverse Linked List // https://leetcode.com/problems/reverse-linked-list/description/ // 時間複雜度: O(n) // 空間複雜度: O(1) class Solution { public: ListNode* reverseList(ListNode* head) { ListNode* pre = NULL; ListNode* cur = head; while(cur != NULL){ ListNode* next = cur->next; cur->next = pre; pre = cur; cur = next; } return pre; } };
我是按照LeetCode的格式進行編寫的,然後去LeetCode上去試一下,鑑於可能有人不知道怎麼使用LeetCode,我簡單進行演示一下怎麼使用:
第一步:百度leetcode,如下:
第二步:點選“Create Account”,建立自己的使用者,需要填寫郵箱,需要點選連結進行啟用,否則刷題無法提交;剛開始寫的郵箱沒給我發郵件,又在個人資料裡重新換了郵箱,就可以收到了;
第三步:在首頁找題,如果能記住題目,可以輸入題目進行搜尋;也可以搜尋題號,如206,也可以搜尋到,如下圖:
第四步:提交程式碼,我將上面寫的程式碼放到leetcode,點選右下角的“Submit Solution”,就可以看到下面的“Submission Solution:Accepted”,就表示通過了,如下圖:
這樣就OK了。
三、測試程式
這部分主要說明一下怎麼去自己測試程式的執行?主要實現連結串列的建立、遍歷、銷燬(C++堆上記憶體要自己管理)。
1、建立連結串列
將陣列傳給函式,根據陣列實現連結串列賦值;還會傳入n建立多大的連結串列,程式碼如下:
// 根據n個元素的陣列arr建立一個連結串列, 並返回連結串列的頭 ListNode* createLinkedList(int arr[], int n){ if(n == 0) return NULL; ListNode* head = new ListNode(arr[0]); ListNode* curNode = head; for(int i = 1 ; i < n ; i ++){ curNode->next = new ListNode(arr[i]); curNode = curNode->next; } return head; }
注意:建立的連結串列,沒有真實的“頭結點”,就是隻存一個開始指標的節點,所以刪除第一個節點要注意!
2、遍歷連結串列
通過頭結點進行遍歷連結串列,程式碼如下:
// 列印以head為頭結點的連結串列資訊內容 void printLinkedList(ListNode* head){ ListNode* curNode = head; while(curNode != NULL){ cout << curNode->val << " -> "; curNode = curNode->next; } cout << "NULL" << endl; return; }
3、銷燬連結串列
將建立時分配的記憶體釋放,程式碼如下:
// 釋放以head為頭結點的連結串列空間 void deleteLinkedList(ListNode* head){ ListNode* curNode = head; while(curNode != NULL){ ListNode* delNode = curNode; curNode = curNode->next; delete delNode; } return; }
4、測試程式
對反轉連結串列程式碼進行測試,整體程式碼如下:
#include <iostream> using namespace std; /** * Definition for singly-linked list. */ struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(NULL) {} }; /// LinkedList 測試輔助函式 // 根據n個元素的陣列arr建立一個連結串列, 並返回連結串列的頭 ListNode* createLinkedList(int arr[], int n){ if(n == 0) return NULL; ListNode* head = new ListNode(arr[0]); ListNode* curNode = head; for(int i = 1 ; i < n ; i ++){ curNode->next = new ListNode(arr[i]); curNode = curNode->next; } return head; } // 列印以head為頭結點的連結串列資訊內容 void printLinkedList(ListNode* head){ ListNode* curNode = head; while(curNode != NULL){ cout << curNode->val << " -> "; curNode = curNode->next; } cout << "NULL" << endl; return; } // 釋放以head為頭結點的連結串列空間 void deleteLinkedList(ListNode* head){ ListNode* curNode = head; while(curNode != NULL){ ListNode* delNode = curNode; curNode = curNode->next; delete delNode; } return; } // 206. Reverse Linked List // https://leetcode.com/problems/reverse-linked-list/description/ // 時間複雜度: O(n) // 空間複雜度: O(1) class Solution { public: ListNode* reverseList(ListNode* head) { ListNode* pre = NULL; ListNode* cur = head; while(cur != NULL){ ListNode* next = cur->next; cur->next = pre; pre = cur; cur = next; } return pre; } }; int main(){ int arr[] = {1, 2, 3, 4, 5}; int n = sizeof(arr)/sizeof(int); ListNode* head = createLinkedList(arr, n); printLinkedList(head); ListNode* head2 = Solution().reverseList(head); printLinkedList(head2); deleteLinkedList(head2); return 0; }View Code
執行結果如下:
進行了反轉,沒有問題;
四、刪除連結串列元素
1、題目
LeetCode上第203題目:Remove Linked List Elements,題目如下:
從具有值val的整數連結串列中刪除所有元素。 例子: 輸入:1->2->6->3->4->5->6,val = 6 輸出:1 - > 2 - > 3 - > 4 - > 5
2、分析題目
先來分析一下題目,用圖來解釋如下:
假如刪除值為4的節點,先把4的next指標儲存,在3號位置指向5,這完全沒有問題;但問題會發生在第一個節點位置,它沒有前一個節點,那怎麼辦呢?在前面建立連結串列時也說過:沒有頭結點,所以使用虛擬頭結點!
程式碼如下:
// 203. Remove Linked List Elements // https://leetcode.com/problems/remove-linked-list-elements/description/ // 使用虛擬頭結點 // 時間複雜度: O(n) // 空間複雜度: O(1) class Solution { public: ListNode* removeElements(ListNode* head, int val) { // 建立虛擬頭結點 ListNode* dummyHead = new ListNode(0); dummyHead->next = head; ListNode* cur = dummyHead; while(cur->next != NULL){ if(cur->next->val == val){ ListNode* delNode = cur->next; cur->next = delNode->next; delete delNode; } else cur = cur->next; } ListNode* retNode = dummyHead->next; delete dummyHead; return retNode; } };
3、測試
測試程式也是上面的連結串列建立和遍歷,程式如下:
#include <iostream> using namespace std; ///Definition for singly-linked list. struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(NULL) {} }; /// LinkedList Test Helper Functions ListNode* createLinkedList(int arr[], int n){ if(n == 0) return NULL; ListNode* head = new ListNode(arr[0]); ListNode* curNode = head; for(int i = 1 ; i < n ; i ++){ curNode->next = new ListNode(arr[i]); curNode = curNode->next; } return head; } void printLinkedList(ListNode* head){ if(head == NULL){ cout << "NULL" << endl; return; } ListNode* curNode = head; while(curNode != NULL){ cout << curNode->val; if(curNode->next != NULL) cout << " -> "; curNode = curNode->next; } cout << endl; return; } void deleteLinkedList(ListNode* head){ ListNode* curNode = head; while(curNode != NULL){ ListNode* delNode = curNode; curNode = curNode->next; delete delNode; } return; } // 203. Remove Linked List Elements // https://leetcode.com/problems/remove-linked-list-elements/description/ // 使用虛擬頭結點 // 時間複雜度: O(n) // 空間複雜度: O(1) class Solution { public: ListNode* removeElements(ListNode* head, int val) { // 建立虛擬頭結點 ListNode* dummyHead = new ListNode(0); dummyHead->next = head; ListNode* cur = dummyHead; while(cur->next != NULL){ if(cur->next->val == val){ ListNode* delNode = cur->next; cur->next = delNode->next; delete delNode; } else cur = cur->next; } ListNode* retNode = dummyHead->next; delete dummyHead; return retNode; } }; int main() { int arr[] = {1, 2, 6, 3, 4, 5, 6}; int n = sizeof(arr) / sizeof(int); ListNode* head = createLinkedList(arr, n); printLinkedList(head); Solution().removeElements(head, 6); printLinkedList(head); deleteLinkedList(head); return 0; }View Code
執行結果如下:
總結:
希望通過這篇部落格,大家能對基本的連結串列演算法題能輕鬆應對;歡迎點贊,不懂的歡迎隨時評論!多多支援,謝謝!