鏈表問題、單調棧-LeetCode 430、725、168、1290、215、1019、503(遞減型單調棧)
- 2019 年 12 月 24 日
- 筆記
作者:TeddyZhang,公眾號:算法工程師之路
鏈表問題(三)、單調棧:
LeetCode # 430 725 168 1290 215 1019 503
1
編程題
【LeetCode #430】扁平化多級雙向鏈表
您將獲得一個雙向鏈表,除了下一個和前一個指針之外,它還有一個子指針,可能指向單獨的雙向鏈表。這些子列表可能有一個或多個自己的子項,依此類推,生成多級數據結構,如下面的示例所示。
扁平化列表,使所有結點出現在單級雙鏈表中。您將獲得列表第一級的頭部。
示例: 輸入: 1—2—3—4—5—6–NULL | 7—8—9—10–NULL | 11–12–NULL 輸出: 1-2-3-7-8-11-12-9-10-4-5-6-NULL
解題思路:
把上面的圖向左旋轉90度,就會發現其實一個二叉樹,然後這道題的實質就是二叉樹的先序遍歷,下面使用先序遍歷的迭代法,不知道大家忘了沒有。在遍歷完成的同時進行雙向鏈表的構建,由於其還有child指針,應該置為nullptr. 注意:先,中,後序遍歷都是指的根節點的訪問順序。
/* // Definition for a Node. class Node { public: int val; Node* prev; Node* next; Node* child; }; */ class Solution { public: Node* flatten(Node* head) { stack<Node*> sta; if (head == nullptr) return nullptr; sta.push(head); Node* pre = nullptr; while(!sta.empty()) { Node* tmp = sta.top(); sta.pop(); if (tmp->next != nullptr) sta.push(tmp->next); if (tmp->child != nullptr) { // 雙向鏈表中child為空 sta.push(tmp->child); tmp->child = nullptr; } if(pre) { // 構建雙向鏈表 pre->next = tmp; tmp->prev = pre; } pre = tmp; } return head; } };
來源:力扣(LeetCode) 鏈接:https://leetcode-cn.com/problems/flatten-a-multilevel-doubly-linked-list
【LeetCode #725】分割鏈表
給定一個頭結點為 root 的鏈表, 編寫一個函數以將鏈表分隔為 k 個連續的部分。 每部分的長度應該儘可能的相等: 任意兩部分的長度差距不能超過 1,也就是說可能有些部分為 null。 這k個部分應該按照在鏈表中出現的順序進行輸出,並且排在前面的部分的長度應該大於或等於後面的長度。 返回一個符合上述規則的鏈表的列表。 舉例:1->2->3->4, k = 5 // 5 結果 [ [1], [2], [3], [4], null ]
示例 1: 輸入: root = [1, 2, 3], k = 5 輸出: [[1],[2],[3],[],[]] 解釋: 輸入輸出各部分都應該是鏈表,而不是數組。 例如, 輸入的結點 root 的 val= 1, root.next.val = 2, root.next.next.val = 3, 且 root.next.next.next = null。 第一個輸出 output[0] 是 output[0].val = 1, output[0].next = null。 最後一個元素 output[4] 為 null, 它代表了最後一個部分為空鏈表。
解題思路:
常規思路,首先根據總長度和k值,計算得到每個分割區間的長度,然後利用pre節點得到每個區間的最後一個節點,使其指向為nullptr,遍歷完整個鏈表即可! 遍歷完成,如果res.size()小於k值,那麼向res中添加nullptr即可,使其長度為k.
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: vector<ListNode*> splitListToParts(ListNode* root, int k) { int length = ; vector<ListNode*> res; ListNode* cur = root; while(cur != nullptr) { length++; cur = cur->next; } int nums = length / k; int left = length % k; cur = root; ListNode* pre = nullptr; while(cur != nullptr) { int tmp_nums = left ? nums+ : nums; if (left > ) left -= ; // left >= 0 res.push_back(cur); while(tmp_nums--) { pre = cur; cur = cur->next; } if (pre) pre->next = nullptr; // 進行鏈表分割 } int tt = k - res.size(); while(tt--) res.push_back(nullptr); return res; } };
來源:力扣(LeetCode) 鏈接:https://leetcode-cn.com/problems/split-linked-list-in-parts
【LeetCode #168】Excel表列名稱
給定一個正整數,返回它在 Excel 表中相對應的列名稱。
例如, 1 -> A 2 -> B 3 -> C … 26 -> Z 27 -> AA 28 -> AB …
示例 1: 輸入: 1 輸出: "A"
解題思路:也就是一個26進制的問題
class Solution { public: string convertToTitle(int n) { char ch = 'A'; string res = ""; while(n != ) { n--; res += (ch + n % ); n /= ; } reverse(res.begin(), res.end()); return res; } };
來源:力扣(LeetCode) 鏈接:https://leetcode-cn.com/problems/excel-sheet-column-title
【LeetCode #1290】二進制鏈錶轉整數
給你一個單鏈表的引用結點 head。鏈表中每個結點的值不是 0 就是 1。已知此鏈表是一個整數數字的二進制表示形式。 請你返回該鏈表所表示數字的 十進制值 。
示例 1: 輸入:head = [1,0,1] 輸出:5 解釋:二進制數 (101) 轉化為十進制數 (5)
解題思路:
複習下鏈表反轉,首先翻轉鏈表,接着遍歷鏈表即可
class Solution { public: int getDecimalValue(ListNode* head) { ListNode* pre = nullptr; while(head != nullptr) { ListNode* next = head->next; head->next = pre; pre = head; head = next; } int sum = ; int tmp = ; while(pre != nullptr) { sum += (pre->val * tmp); tmp *= ; pre = pre->next; } return sum; } };
來源:力扣(LeetCode) 鏈接:https://leetcode-cn.com/problems/convert-binary-number-in-a-linked-list-to-integer
【LeetCode #215】數組中的第K個最大元素
在未排序的數組中找到第 k 個最大的元素。請注意,你需要找的是數組排序後的第 k 個最大的元素,而不是第 k 個不同的元素。
示例 1: 輸入: [3,2,1,5,6,4] 和 k = 2 輸出: 5
解題思路:
大根堆的創建,注意在priority_queue中建立大根堆要使用less函數(默認),小根堆使用greater函數,以及lambda表達式和priority_queue的結合使用!
class Solution { public: int findKthLargest(vector<int>& nums, int k) { if(nums.size() == ) return ; auto cmp = [](int a, int b) { return a <= b; }; priority_queue<int, vector<int>, decltype(cmp)> pq(cmp); for(auto i: nums) { pq.push(i); } while((k--) > ){ pq.pop(); } return pq.top(); } };
來源:力扣(LeetCode) 鏈接:https://leetcode-cn.com/problems/kth-largest-element-in-an-array
【LeetCode #1019】鏈表中的下一個更大節點
給出一個以頭節點 head 作為第一個節點的鏈表。鏈表中的節點分別編號為:node_1, node_2, node_3, … 。 每個節點都可能有下一個更大值(next larger value):對於 node_i,如果其 next_larger(node_i) 是 node_j.val,那麼就有 j > i 且 node_j.val > node_i.val,而 j 是可能的選項中最小的那個。如果不存在這樣的 j,那麼下一個更大值為 0 。 返回整數答案數組 answer,其中 answer[i] = next_larger(node_{i+1}) 。
注意:在下面的示例中,諸如 [2,1,5] 這樣的輸入(不是輸出)是鏈表的序列化表示,其頭節點的值為 2,第二個節點值為 1,第三個節點值為 5 。
示例 1: 輸入:[2,7,4,3,5] 輸出:[7,0,5,5,0]
解題思路:
構建一個單調遞減的單調棧,比如[2,7,4,3,5],遍歷壓入棧中,當棧中元素為[2]時,滿足單調棧,遍歷到7,由於7 > 2,則不滿足,應將所有小於7的元素全部pop,這樣才能滿足單調棧,因此遍歷結束後,單調棧中只會剩餘[7, 5],將其置零即可!
class Solution { public: vector<int> nextLargerNodes(ListNode* head) { vector<int> res; stack<int> sta; // 構建單調棧,存放單調遞減的數值序列 int i = ; while(head != nullptr) { while(!sta.empty() && head->val > res[sta.top()]){ res[sta.top()] = head->val; sta.pop(); } res.push_back(head->val); sta.push(i++); head = head->next; } while(!sta.empty()) { res[sta.top()] = ; sta.pop(); } return res; } };
來源:力扣(LeetCode) 鏈接:https://leetcode-cn.com/problems/next-greater-node-in-linked-list
【LeetCode #503】下一個更大的元素 II
給定一個循環數組(最後一個元素的下一個元素是數組的第一個元素),輸出每個元素的下一個更大元素。數字 x 的下一個更大的元素是按數組遍歷順序,這個數字之後的第一個比它更大的數,這意味着你應該循環地搜索它的下一個更大的數。如果不存在,則輸出 -1。
示例 1: 輸入: [1,2,1] 輸出: [2,-1,2] 解釋: 第一個 1 的下一個更大的數是 2; 數字 2 找不到下一個更大的數; 第二個 1 的下一個最大的數需要循環搜索,結果也是 2。
解題思路:
這道題與上面的題是一個解法,使用單調遞減的單調棧處理,不同的是上面由於是鏈表,不支持index,需要先拷貝到res中,再進行處理。而這道題目本身就是vector,因此不再需要將數據拷貝到res中,可以直接通過索引賦值! 本題最後一個元素是第一個元素,屬於循環數組,我們遍歷兩次就可以了!
class Solution { public: vector<int> nextGreaterElements(vector<int>& nums) { int n = nums.size(); vector<int> res(n, -1); stack<int> sta; for(int i = ; i < n*; i++) { while(!sta.empty() && nums[sta.top()] < nums[i % n]) { res[sta.top()] = nums[i % n]; sta.pop(); } if (i < n) { sta.push(i); } } return res; } };
來源:力扣(LeetCode) 鏈接:https://leetcode-cn.com/problems/next-greater-element-ii