1.給定一個二叉搜索樹,編寫一個函數 kthSmallest
來查找其中第 k 個最小的元素。
解:要求找第k個最小的元素,即需要對樹進行遍歷排序,中序遍歷元素得到的結果即爲二叉搜索樹的排序好的結果,因此採用中序遍歷,每遍歷一次都保存下父結點並且遍歷k次。
代碼:
#include <iostream>
#include<queue>
#include<vector>
using namespace std;
struct TreeNode
{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) :val(x), left(NULL), right(NULL) {}
};
class Solution {
public:
int kthSmallest(TreeNode* root, int k) {
return fun(root,k)->val;
}
TreeNode* fun(TreeNode* root, int& k)
{
TreeNode* target = NULL;
if (root->left != NULL)
{
target = fun(root->left, k);//不斷向左子樹遞歸
}
if (target == NULL)//當前沒有左葉子結點
{
if (k == 1)
target = root;//記錄左葉子結點的父結點
k--;
}
if (target == NULL && root->right != NULL)
{
target = fun(root->right, k);//向右子樹遍歷
}
return target;
}
};
int main()
{
TreeNode* root =new TreeNode(3);
root->left = new TreeNode(1);
root->right = new TreeNode(4);
root->right->right = new TreeNode(5);
root->left->right = new TreeNode(2);
Solution s1;
cout << s1.kthSmallest(root, 2) << endl;
}
2.給定一個二叉樹, 找到該樹中兩個指定節點的最近公共祖先,一個節點也可以是它自己的祖先。
解:首先,要想通過遞歸來實現,就需要先確定臨界條件,那麼臨界條件是什麼呢?換句話說,臨界條件就是遞歸中能夠直接返回的特殊情況,第一點則是最常見的“判空”,判斷根結點是否是空節點,如果是,那麼肯定就可以馬上返回了,這是一個臨界條件;再來考慮題意,在以root爲根結點的樹中找到p結點和q結點的最近公共祖先,那麼特殊情況是什麼呢?很顯然,特殊情況就是根結點就等於q結點或p結點的情況,想一下,如果根結點爲二者之一,那麼根結點就必定是最近公共祖先了,這時直接返回root即可。由此看來,這道題就一共有三種特殊情況,root == q 、root == p和root==null,這三種情況均直接返回root即可。
根據臨界條件,實際上可以發現這道題已經被簡化爲查找以root爲根結點的樹上是否有p結點或者q結點,如果有就返回p結點或q結點,否則返回null。從左右子樹分別進行遞歸,即查找左右子樹上是否有p結點或者q結點,就一共有4種情況:
第一種情況:左子樹和右子樹均找沒有p結點或者q結點;(這裏特別需要注意,雖然題目上說了p結點和q結點必定都存在,但是遞歸的時候必須把所有情況都考慮進去,因爲題目給的條件是針對於整棵樹,而遞歸會到局部,不一定都滿足整體條件)
第二種情況:左子樹上能找到,但是右子樹上找不到,此時就應當直接返回左子樹的查找結果;
第三種情況:右子樹上能找到,但是左子樹上找不到,此時就應當直接返回右子樹的查找結果;
第四種情況:左右子樹上均能找到,說明此時的p結點和q結點分居root結點兩側,此時就應當直接返回root結點了。
代碼:
#include <iostream>
using namespace std;
struct TreeNode
{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) :val(x), left(NULL), right(NULL) {}
};
class Solution {
public:
int kthSmallest(TreeNode* root, int k) {
return fun(root,k)->val;
}
TreeNode* fun(TreeNode* root, int& k)
{
TreeNode* target = NULL;
if (root->left != NULL)
{
target = fun(root->left, k);//不斷向左子樹遞歸
}
if (target == NULL)//當前沒有左葉子結點
{
if (k == 1)
target = root;//記錄左葉子結點的父結點
k--;
}
if (target == NULL && root->right != NULL)
{
target = fun(root->right, k);//向右子樹遍歷
}
return target;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == p || root == q || !root) return root;
TreeNode*left = lowestCommonAncestor(root->left, p, q);
TreeNode*right = lowestCommonAncestor(root->right, p, q);
if (!left && !right) return NULL;
else if (left && !right)
return left;
else if (right && !left)
return right;
return root;
}
};
int main()
{
TreeNode* root =new TreeNode(3);
root->left = new TreeNode(1);
root->right = new TreeNode(4);
root->right->right = new TreeNode(5);
root->left->right = new TreeNode(2);
Solution s1;
//cout << s1.kthSmallest(root, 2) << endl;
cout << s1.lowestCommonAncestor(root, root->right->right, root->left)->val << endl;
}
3.序列化是將一個數據結構或者對象轉換爲連續的比特位的操作,進而可以將轉換後的數據存儲在一個文件或者內存中,同時也可以通過網絡傳輸到另一個計算機環境,採取相反方式重構得到原數據。請設計一個算法來實現二叉樹的序列化與反序列化。這裏不限定你的序列 / 反序列化算法執行邏輯,你只需要保證一個二叉樹可以被序列化爲一個字符串並且將這個字符串反序列化爲原始的樹結構。
代碼:
#include "pch.h"
#include<string>
#include <iostream>
#include<vector>
using namespace std;
struct TreeNode
{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) :val(x), left(NULL), right(NULL) {}
};
class Solution {
public:
void serializeCore(TreeNode* root, string & s)
{
if (root==NULL)
{
s += "#";
return;
}
s += to_string(root->val) + " ";
serializeCore(root->left, s);
serializeCore(root->right, s);
}
string serialize(TreeNode* root)
{
string s = "";
serializeCore(root, s);
s.pop_back();
return s;
}
TreeNode* deserializeCore(vector<string> &data, int &len, int &n)
{
TreeNode* node = NULL;
if (data[n]=="#")
{
return node;
}
else {
node = new TreeNode(stoi(data[n]));
}
TreeNode* leftnode = NULL, *rightnode = NULL;
n++;
if (n<=len-2)
{
leftnode = deserializeCore(data, len, n);
n++;
rightnode = deserializeCore(data, len, n);
}
node->left = leftnode;
node->right = rightnode;
return node;
}
TreeNode* deserialize(string data)
{
if (data=="#"||data=="")
{
return NULL;
}
int len = 0;
vector<string> list_s;
string s = "";
for (auto d:data)
{
if (d==' ')
{
list_s.push_back(s);
s = "";
len++;
}
else
s.push_back(d);
}
list_s.push_back(s);
len++;
int n = 0;
TreeNode* root = deserializeCore(list_s, len, n);
return root;
}
};
int main()
{
TreeNode* root =new TreeNode(3);
Solution s1;
root = s1.deserialize("1231342");
}
4.城市的天際線是從遠處觀看該城市中所有建築物形成的輪廓的外部輪廓。現在,假設您獲得了城市風光照片(圖A)上顯示的所有建築物的位置和高度,請編寫一個程序以輸出由這些建築物形成的天際線(圖B)
每個建築物的幾何信息用三元組 [Li,Ri,Hi]
表示,其中 Li
和 Ri
分別是第 i 座建築物左右邊緣的 x 座標,Hi
是其高度。可以保證 0 ≤ Li, Ri ≤ INT_MAX
, 0 < Hi ≤ INT_MAX
和 Ri - Li > 0
。您可以假設所有建築物都是在絕對平坦且高度爲 0 的表面上的完美矩形。
例如,圖A中所有建築物的尺寸記錄爲:[ [2 9 10], [3 7 15], [5 12 12], [15 20 10], [19 24 8] ]
。
輸出是以 [ [x1,y1], [x2, y2], [x3, y3], ... ]
格式的“關鍵點”(圖B中的紅點)的列表,它們唯一地定義了天際線。關鍵點是水平線段的左端點。請注意,最右側建築物的最後一個關鍵點僅用於標記天際線的終點,並始終爲零高度。此外,任何兩個相鄰建築物之間的地面都應被視爲天際線輪廓的一部分。
例如,圖B中的天際線應該表示爲:[ [2 10], [3 15], [7 12], [12 0], [15 10], [20 8], [24, 0] ]
。
說明:
- 任何輸入列表中的建築物數量保證在
[0, 10000]
範圍內。 - 輸入列表已經按左
x
座標Li
進行升序排列。 - 輸出列表必須按 x 位排序。
- 輸出天際線中不得有連續的相同高度的水平線。例如
[...[2 3], [4 5], [7 5], [11 5], [12 7]...]
是不正確的答案;三條高度爲 5 的線應該在最終輸出中合併爲一個:[...[2 3], [4 5], [12 7], ...]
代碼:
class Solution {
public:
vector<vector<int>> getSkyline(vector<vector<int>>& buildings) {
if (buildings.empty()) return {};
multiset<pair<int, int>> st;
for (auto b : buildings) {
st.insert(make_pair(b[0], -b[2]));
st.insert(make_pair(b[1], b[2]));
}
vector<vector<int>> ret;
multiset<int> height{0};
int m = 0;
for (auto s : st) {
if (s.second < 0) height.insert(-s.second); // 矩形左側
else height.erase(height.find(s.second)); // 矩形右側
if (m != *height.rbegin())
ret.push_back({s.first, *height.rbegin()});
m = *height.rbegin();
}
return ret;
}
};