AcWing 46. 二叉搜索樹的后序遍歷序列
輸入一個整數數組,判斷該數組是不是某二叉搜索樹的后序遍歷的結果。
如果是則返回true,否則返回false。
假設輸入的數組的任意兩個數字都互不相同。
樣例
輸入:[4, 8, 6, 12, 16, 14, 10]
輸出:true
題解:
- 中序(二叉搜索樹的中序是從小到大的結果)+后序建樹
- 后序遍歷最后一個數為根,二叉搜索樹的性質,我們直接從頭開始找到比根小的值,這便是左子樹;右邊部分便是右子樹,如果不符合便是錯誤。
class Solution {
public:
vector<int> seq;
bool verifySequenceOfBST(vector<int> sequence) {
seq = sequence;
if(seq.empty()) return true;
return dfs(1, seq.size() - 1);
}
bool dfs(int l, int r){
if(l >= r) return true;
int root = seq[r];
int k = 0;
while(k < r && seq[k] < root) k++;
for(int i = k; i < r; i++)
{A
if(seq[i] < root) return false;
}
return dfs(l, k - 1) && dfs(k, r - 1);
}
};
AcWing 47. 二叉樹中和為某一值的路徑
輸入一棵二叉樹和一個整數,打印出二叉樹中結點值的和為輸入整數的所有路徑。
從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。
樣例
給出二叉樹如下所示,並給出num=22。
dfs
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> res;
vector<vector<int>> findPath(TreeNode* root, int sum) {
if(root == NULL) return res;
vector<int> path;
dfs(root, sum, path);
return res;
}
void dfs(TreeNode* root, int sum, vector<int>&path){
if(root->left == NULL && root->right == NULL)
{
if(sum - root->val == 0)
{
path.push_back(root->val);
res.push_back(path);
path.pop_back();
}
return;
}
path.push_back(root->val);
if(root->left != NULL) dfs(root->left, sum - root->val, path);
if(root->right != NULL) dfs(root->right, sum - root->val, path);
path.pop_back();
}
};
AcWing 48. 復雜鏈表的復刻
請實現一個函數可以復制一個復雜鏈表。
在復雜鏈表中,每個結點除了有一個指針指向下一個結點外,還有一個額外的指針指向鏈表中的任意結點或者null。
注意:
函數結束后原鏈表要與輸入時保持一致。
- hash表做映射 時間O(n) 空間O(n)
/**
* Definition for singly-linked list with a random pointer.
* struct ListNode {
* int val;
* ListNode *next, *random;
* ListNode(int x) : val(x), next(NULL), random(NULL) {}
* };
*/
class Solution {
public:
ListNode *copyRandomList(ListNode *head) {
if(!head) return head;
unordered_map<ListNode*, ListNode*> pos;
for(auto p = head; p; p = p -> next)
{
pos[p] = new ListNode(p->val);
}
pos[NULL] = NULL;
for(auto p = head; p; p = p -> next)
{
pos[p]->next = pos[p->next];
pos[p]->random = pos[p->random];
}
return pos[head];
}
};
/**
* Definition for singly-linked list with a random pointer.
* struct ListNode {
* int val;
* ListNode *next, *random;
* ListNode(int x) : val(x), next(NULL), random(NULL) {}
* };
*/
class Solution {
public:
ListNode *copyRandomList(ListNode *head) {
// 每一個節點后面加復制
for(auto p = head; p; p = p->next)
{
auto np = new ListNode(p->val);
auto next = p->next;
p->next = np;
np->next = next;
p = np;
}
// 復制random指針
for(auto p = head; p; p = p->next->next)
{
if(p->random)
p->next->random = p->random->next;
}
// 分離兩個鏈表
auto dummy = new ListNode(-1);
auto cur = dummy;
for(auto p = head; p; p = p->next)
{
cur->next = p->next;
cur = cur->next;
p->next = p->next->next;
}
return dummy->next;
}
};
AcWing 49. 二叉搜索樹與雙向鏈表
輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。
要求不能創建任何新的結點,只能調整樹中結點指針的指向。
注意:
需要返回雙向鏈表最左側的節點。
例如,輸入下圖中左邊的二叉搜索樹,則輸出右邊的排序雙向鏈表。
解題思路
實現返回每一個子樹的兩端節點即可
AcWing 50. 序列化二叉樹
AcWing 51. 數字排列(good)
輸入一組數字(可能包含重復數字),輸出其所有的排列方式。
樣例
輸入:[1,2,3]
輸出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
題解
根以前的枚舉不一樣,需要用占位的思想去枚舉,預先開出n個坑位,將nums每一個數往里面放,一個數只能放在前一個相同數字的后面。
class Solution {
public:
int n = 0;
vector<vector<int> > ans;
vector<int> path;//位置
//u表示枚舉到nums第幾個數了,start表示從空位的第幾位開始可以放
void dfs(int u, int start, int state, vector<int>& nums)
{
if(u == n)
{
ans.push_back(path);
return;
}
// 頭一個數或者與上一個數不同
if(u == 0 || nums[u] != nums[u-1]) start = 0;
for(int i = start; i < n; i++)
{
if(!(state>>i&1))
{
path[i] = nums[u];
dfs(u + 1, i + 1, state+(1<<i), nums);
}
}
}
vector<vector<int>> permutation(vector<int>& nums) {
sort(nums.begin(), nums.end());
n = nums.size();
path.resize(n);
dfs(0, 0, 0, nums);
return ans;
}
};
庫函數
class Solution {
public:
vector<vector<int>> permutation(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<vector<int>> res;
do
{
res.push_back(nums);
}while (next_permutation(nums.begin(),nums.end()));
// do res.push_back(nums); while (next_permutation(nums.begin(), nums.end()));
return res;
}
};
AcWing 52. 數組中出現次數超過一半的數字
數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。
假設數組非空,並且一定存在滿足條件的數字。
思考題:
假設要求只能使用 O(n) 的時間和額外 O(1) 的空間,該怎么做呢?
樣例
輸入:[1,2,1,1,3]
輸出:1
題解
抵消(配對)的思想。
循環數組
- count == 0: value=a[i], count=1
- count != 0:
- value != a[i] : count -= 1
- value == a[i] : count += 1
n個村民
好人數量 > n/2 (回答的是正確)
壞蛋數量 < n/2 (回答隨機)
o(n)次詢問找出壞蛋。
count: 當前人被信任的次數,0
val: 人是誰,-1
從頭循環,
- count == 0,首先問自己是否被信任,
回答是好人,count += 1,val = id
回答壞蛋的一定是壞蛋,好人只會回答自己是好人。 - count != 0, 問val十分是好人,若val是好人,則count += 1。
若是壞人,則count -= 1。count減到0,則替換
好人的信任值。
好人的信用量一定大於壞人的信用量,消耗一個好人肯定需要消耗一個壞人。
class Solution {
public:
int moreThanHalfNum_Solution(vector<int>& nums) {
int cnt = 0, val = -1;
for(auto it: nums)
{
if(cnt == 0)
{
val = it;
cnt ++;
}
else
{
if(val == it) cnt ++;
else cnt --;
}
}
return val;
}
};
AcWing 53. 最小的k個數
輸入n個整數,找出其中最小的k個數。
注意:
數據保證k一定小於等於輸入數組的長度;
輸出數組內元素請按從小到大順序排序;
樣例
輸入:[1,2,3,4,5,6,7,8] , k=4
輸出:[1,2,3,4]
用大根堆維護一下即可,(優先隊列)
class Solution {
public:
vector<int> getLeastNumbers_Solution(vector<int> input, int k) {
priority_queue<int> heap;
for(auto it : input)
{
if(heap.size() >= k)
{
if(it < heap.top())
{
heap.pop();
heap.push(it);
}
}
else
heap.push(it);
}
vector<int> res;
for(int i = 0; i < k; i++)
{
res.push_back(heap.top());
heap.pop();
}
reverse(res.begin(), res.end());
return res;
}
};
class Solution {
public:
vector<int> getLeastNumbers_Solution(vector<int> input, int k) {
priority_queue<int> heap;
for (auto x : input)
{
if (heap.size() < k || heap.top() > x) heap.push(x);
if (heap.size() > k) heap.pop();
}
vector<int> res;
while (heap.size()) res.push_back(heap.top()), heap.pop();
reverse(res.begin(), res.end());
return res;
}
};
AcWing 54. 數據流中的中位數
如何得到一個數據流中的中位數?
如果從數據流中讀出奇數個數值,那么中位數就是所有數值排序之后位於中間的數值。
如果從數據流中讀出偶數個數值,那么中位數就是所有數值排序之后中間兩個數的平均值。
樣例
輸入:1, 2, 3, 4
輸出:1,1.5,2,2.5
解釋:每當數據流讀入一個數據,就進行一次判斷並輸出當前的中位數。
題解
維護兩個堆,大根堆維護較小的那部分,小根堆維護較大那部分,保證大跟堆的數量最多只比小根堆的數量多1個即可,代碼中實現的是比較簡捷的做法。
class Solution {
public:
priority_queue<int, vector<int>, greater<int> > heap_u;
priority_queue<int> heap_d;
int n = 0;
void insert(int num){
n++;
heap_d.push(num);
while(heap_u.size() != 0 && heap_d.top() > heap_u.top())
{
int maxx = heap_d.top(), minn = heap_u.top();
heap_d.pop(), heap_u.pop();
heap_d.push(minn), heap_u.push(maxx);
}
if(heap_d.size() - heap_u.size() > 1)
{
heap_u.push(heap_d.top());
heap_d.pop();
}
}
double getMedian(){
if(n & 1)
return heap_d.top();
else
return (heap_d.top() + heap_u.top()) / 2.0;
}
};
AcWing 55. 連續子數組的最大和
輸入一個 非空 整型數組,數組里的數可能為正,也可能為負。
數組中一個或連續的多個整數組成一個子數組。
求所有子數組的和的最大值。
要求時間復雜度為O(n)。
樣例
輸入:[1, -2, 3, 10, -4, 7, 2, -5]
輸出:18
題解
狀態表示:
集合划分:以a[k] 為結尾的連續子數組
- 只有a[k]一個數
- 至少兩個數,一定選a[k],所以可以去掉a[k]
相當於f(k-1) + a[k]
f(k) = max{a[k], f(k-1) +a[k]}
f(k) = a[k] + max(0, f(k-1))
S = a[k] + max(0, S)