Tree C++實現
100. 相同的樹
給定兩個二叉樹,編寫一個函數來檢驗它們是否相同。
如果兩個樹在結構上相同,並且節點具有相同的值,則認爲它們是相同的。
一:
class Solution {
public:
static string s;
bool isSameTree(TreeNode* p, TreeNode* q) {
if(p==NULL&&q==NULL)return true;
if(p==NULL||q==NULL)return false;
return p->val==q->val&&isSameTree(p->left, q->left)&&isSameTree(p->right, q->right);
}
};
二:非遞歸算法
class Solution {
public:
static string s;
bool isSameTree(TreeNode* p, TreeNode* q) {
queue<TreeNode*> queuep, queueq;
if(p==NULL&&q==NULL)return true;
if(p==NULL||q==NULL)return false;
queuep.push(p);
queueq.push(q);
while((!queueq.empty())&&(!queuep.empty())){
TreeNode* tp, *tq;
tp=queuep.front();
queuep.pop();
tq=queueq.front();
queueq.pop();
cout<<"tp"<<tp->val<<",tq"<<tq->val<<endl;
if(tp->val!=tq->val)return false;
if(tq->left!=NULL&&tp->left!=NULL){
queuep.push(tp->left);
queueq.push(tq->left);
}
else if(tq->left!=NULL||tp->left!=NULL)return false;
if(tq->right!=NULL&&tp->right!=NULL)
{
queueq.push(tq->right);
queuep.push(tp->right);
}
else if(tp->right!=NULL||tq->right!=NULL) return false;
}
if( (!queueq.empty()) || (!queuep.empty()) ) return false;
return true;
}
};
101. 對稱二叉樹
給定一個二叉樹,檢查它是否是鏡像對稱的
解法一:遞歸
/**
* 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:
bool isSymmetric(TreeNode* root) {
return f(root, root);
}
bool f(TreeNode* t1, TreeNode* t2){
if(!t1&&!t2)return true;
if(!t1||!t2)return false;
if(t1->val==t2->val)return f(t1->left, t2->right)&&f(t1->right, t2->left);
else return false;
}
};
判斷的重點是一棵樹的兩個子樹是不是相等的,爲空、其中一個爲空、不爲空的話就判斷這兩個子樹的根的節點,如果相同的話,就返回左樹的左子樹與右樹的右子樹。。。
遞歸基本上都是這個思路了。
解法二:層次遍歷
思想相當於複製了一棵樹,然後比較這兩棵樹。
每次比較隊列的前兩個節點,所以入隊時前兩個節點應當是對稱的節點。
class Solution {
public:
bool isSymmetric(TreeNode* root)
{
if (root == NULL)return true;
queue<TreeNode*> q;
q.push(root);
q.push(root);
while (!q.empty())
{
TreeNode* t1, * t2;
t1 = q.front();
q.pop();
t2 = q.front();
q.pop();
if (t1 == NULL && t2 == NULL){continue;}
if (t1 == NULL || t2 == NULL){return false;}
if (t1->val != t2->val){return false;}
q.push(t1->left);
q.push(t2->right);
q.push(t1->right );
q.push(t2->left );
}
return true;
}
};
使用隊列是廣度優先,使用棧是深度遍歷。
這個題目也可以使用棧來解決;
104. 二叉樹的最大深度
給定一個二叉樹,找出其最大深度。 二叉樹的深度爲根節點到最遠葉子節點的最長路徑上的節點數。
解法一:
遞歸。思路是寫一個函數,除了根節點增加一個參數dep來表示深度;
如果是空節點,就說明在本節點深度並不增加,返回dep的值即可;
否則,就返回他的左子樹和右子樹的深度,不過參數dep這時要+1.
class Solution {
public:
int maxDepth(TreeNode* root) {
int depth=0;
return d(root, depth);
}
int d( TreeNode* ro, int dep){
if(ro==NULL)return dep;
return max( d(ro->left, dep+1),d(ro->right , dep+1));
}
};
**解法二:**
也是遞歸,但是不用這一個參數,也不再另寫一個函數。思路是爲空就返回0,否則就是左右子樹的深度+1.
class Solution {
public:
int maxDepth(TreeNode* root) {
return root==NULL ? 0 : max(maxDepth(root->left), maxDepth(root->right))+1;
}
};
解法三:
最後當然要有非遞歸實現啦。
思路是使用一個隊列,每次進一層的節點,然後出一層的節點(把他們的左右子節點入隊)。
那麼,就需要一個變量來記錄每層的節點數目:layer_size
。
class Solution {
public:
int maxDepth(TreeNode* root) {
int depth=0;
queue<TreeNode*> que;
if(root==NULL)return 0;
que.push(root);
while(!que.empty()){
depth++;
int layer_size=que.size();
while(layer_size--){
TreeNode* t=que.front();
que.pop();
if(t->left!=NULL)que.push(t->left);
if(t->right!=NULL)que.push(t->right);
}
}
return depth;
}
};
107. 二叉樹的層次遍歷 II
給定一個二叉樹,返回其節點值自底向上的層次遍歷。 (即按從葉子節點所在層到根節點所在的層,逐層從左向右遍歷)
解法一:
使用隊列,就像普通的層次遍歷一樣。
但是題目要求的是從底向上輸出,所以遍歷每一層的時候把他加入vector的最前面而不是末尾;然後使用一個棧把每個一位vector從底向上加入二維vector(每一個一維的vector表示一行)。
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
vector<vector<int>> re;
queue<TreeNode*> que;
if(root==NULL)return re;
que.push(root);
int layer=0;
stack<vector<int>> sta;
while(!que.empty()){
int layer_size=que.size();
vector<int> a;
while(layer_size--){
TreeNode* t= que.front();
que.pop();
a.push_back(t->val);
if(t->left!=NULL)que.push(t->left);
if(t->right!=NULL)que.push(t->right);
}
sta.push(a);
}
vector<int> temp;
while(!sta.empty()){
temp=sta.top();
sta.pop();
re.push_back(temp);
}
return re;
}
};
方法二:
最後不用棧倒着插入每個一維vector了,而是直接在每一層的循環結束時,把它插入二維vector的頭部,但是這樣會慢一點,內存佔用也大一點,這是vector::insert()函數的特點導致的。
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
vector<vector<int>> re;
queue<TreeNode*> que;
if(root==NULL)return re;
que.push(root);
int layer=0;
while(!que.empty()){
int layer_size=que.size();
vector<int> a;
while(layer_size--){
TreeNode* t= que.front();
que.pop();
a.push_back(t->val);
if(t->left!=NULL)que.push(t->left);
if(t->right!=NULL)que.push(t->right);
}
re.insert(re.begin(),a);
}
return re;
}
};
方法三:
只有最後一個地方不同我就只貼最後的代碼了。
用vector::push——back壓入末尾,但是最後reverse一下;就不需要使用棧來反轉了;
re.push_back(a);
}
reverse(re.begin(),re.end());
return re;
}
};
方法四:逆向迭代器
vector<vector<int>> result;
for(auto iter=re.rbegin();iter!=re.rend();iter++) result.push_back(*iter);
return result;
}
};
Auto真好用啊;
108. 將有序數組轉換爲二叉搜索樹
將一個按照升序排列的有序數組,轉換爲一棵高度平衡二叉搜索樹。 本題中,一個高度平衡二叉樹是指一個二叉樹每個節點
的左右兩個子樹的高度差的絕對值不超過 1。
解法一:
利用數組的有序,每次都把位於數組中間的數,作爲根節點,就像二分法查找一樣。
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
return build(0,nums.size()-1, nums );
}
TreeNode* build(int left, int right, vector<int> num){
if(left>right)return NULL;
int mid=(left+right)/2;
TreeNode* root=new TreeNode(num[mid]);
root->left=build(left, mid-1, num);
root->right=build(mid+1, right, num);
return root;
}
};
110. 平衡二叉樹
給定一個二叉樹,判斷它是否是高度平衡的二叉樹。 本題中,一棵高度平衡二叉樹定義爲: 一個二叉樹每個節點
的左右兩個子樹的高度差的絕對值不超過1。
解法一:
遞歸。實際上是計算深度的函數,每次遞歸計算左右子樹的深度,。。在計算深度的過程中,進行左右子樹高度差的判斷。設置一個布爾型的變量,來表示這棵樹是不是平衡的;
class Solution {
public:
bool balance = true;
bool isBalanced(TreeNode* root) {
f(root);
return balance;
}
int f(TreeNode* root) {
if (root == NULL) return 0;
int left_d = f(root->left);
int right_d = f(root->right);
if (abs(left_d - right_d) > 1) balance = false;
return max(left_d, right_d) + 1;
}
};
解法二:
也是遞歸,但是就不用這一個變量來保存結果了。
用剪枝的思想,如果左右子樹不平衡,那後面就不計算了,一路返回-1;(-1就表示不平衡)
與方法一相比,法一會計算出每一層的深度;
class Solution {
public:
bool isBalanced(TreeNode* root) {
return f(root) == -1 ? false : true;
}
int f(TreeNode* root) {
if (root == NULL) return 0;
int left_d = f(root->left);
if (left_d == -1)return -1;
int right_d = f(root->right);
if (right_d == -1)return -1;
if (abs(left_d - right_d) > 1) return -1;
return max(left_d, right_d) + 1;
}
};
111. 二叉樹的最小深度
給定一個二叉樹,找出其最小深度。
最小深度是從根節點到最近葉子節點的最短路徑上的節點數量。
讀題要仔細,是到最近的葉子節點,如果是【1,2】這種樹,那麼輸出是2不是1。
方法一:
使用隊列,層次遍歷。
找到一個左右子樹爲空的節點,就直接返回。否則的話繼續層次遍歷
class Solution {
public:
int minDepth(TreeNode* root) {
queue<TreeNode*> que;
if(root==NULL)return 0;
que.push(root);
int layer=0;
while(!que.empty()){
int layer_size=que.size();
if(layer_size)layer++;
while(layer_size--){
TreeNode* t=que.front();
que.pop();
if(t->left!=NULL)que.push(t->left);
if(t->right!=NULL)que.push(t->right);
if(t->left==NULL&&t->right==NULL) return layer;
}
}
return layer;
}
};
方法二:
遞歸。
找到一個左右子樹都爲空的節點。
首先如果根節點是空的,就返回0;在後續的遞歸中不在把空節點作爲參數傳入;
如果左右子樹都不爲空,就要取他們的深度的最小值。
class Solution {
public:
int minDepth(TreeNode* root) {
if(root==NULL)return 0;
if(root->left==NULL&&root->right==NULL)return 1;
if(root->left!=NULL&&root->right!=NULL)
return min(minDepth(root->left), minDepth(root->right))+1;
if(root->left==NULL)return minDepth(root->right)+1;
if(root->right==NULL) return minDepth(root->left)+1;
return -1;
}
};
112. 路徑總和
給定一個二叉樹和一個目標和,判斷該樹中是否存在根節點到葉子節點的路徑,這條路徑上所有節點值相加等於目標和。
解法一:
遞歸。感覺這道題還挺難的;
class Solution {
public:
bool hasPathSum(TreeNode* root, int sum) {
if(root==NULL)return false;
if(root->left==NULL&&root->right==NULL) return sum-root->val==0;
else return hasPathSum(root->left, sum-root->val) || hasPathSum(root->right, sum-root->val) ;
}
};
解法二:
DFS,比較難想起來的點在於使用兩個棧,保存當前節點的sum.
思路是把節點和它對應的sum同時壓入兩個棧中。
如果這是一個葉子節點(左右子樹都爲空)並且sum與他的當前值相等,就直接返回true;
否則就把他的左右節點壓入棧中。
class Solution {
public:
bool hasPathSum(TreeNode* root, int sum) {
stack< TreeNode*> node;
stack<int> newSum;
if (root == NULL)return sum == 0;
node.push(root);
newSum.push(sum);
while (!node.empty()) {
TreeNode* t;
int s;
t = node.top();
node.pop();
s = newSum.top();
newSum.pop();
s -= t->val;
if (t->left == NULL && t->right == NULL && s == 0) return true;
if (s < 0)continue;
if (t->left != NULL)
{
node.push(t->left);
newSum.push(s);
}
if (t->right != NULL) {
node.push(t->right);
newSum.push(s);
}
}
return false;
}
};
226. 翻轉二叉樹
方法一:
遞歸,不使用swap函數。相當於新建一顆樹,根節點就是root的根節點。
class Solution {
public:
TreeNode* invertTree(TreeNode* root){
if(root==NULL)return NULL;
TreeNode* mirror=new TreeNode(root->val);
mirror->right=invertTree(root->left);
mirror->left=invertTree(root->right);
return mirror;
}
};
解法二:
遞歸使用swap函數;
與剛纔解法相比,沒有創建一個新的樹。
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root){
swap(root->left, root->right);
root->left=invertTree(root->left);
root->right=invertTree(root->right);
}
return root;
}
};
235. 二叉搜索樹的最近公共祖先
給定一個二叉搜索樹, 找到該樹中兩個指定節點的最近公共祖先。
百度百科中最近公共祖先的定義爲:“對於有根樹 T 的兩個結點 p、q,最近公共祖先表示爲一個結點 x,滿足 x 是 p、q 的祖先且 x
的深度儘可能大(一個節點也可以是它自己的祖先)。”
解法一:
思路是先把q搜索過程中經過的節點寫進一個隊列。
在搜索q的過程中,把路上經過的節點與隊列比較,遇到的第一個不同的節點叫做a吧,那麼最近的祖先節點就是a在隊列中的上一個節點。
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
queue<TreeNode*> que;
int dp=depth(root, p);
TreeNode * t=root;
while(dp--){
que.push(t);
if(t->val<p->val) t=t->right;
else if(t->val>p->val)t=t->left;
}
t=root;
TreeNode* a=root;
while(!que.empty()){
if(a!=que.front()) return t;
que.pop();
t=a;
if(t->val>q->val)a=t->left;
if(t->val<q->val)a=t->right;
}
return t;
}
int depth(TreeNode* root, TreeNode* t){
if(root==NULL)return 0;
if(root->val==t->val) return 1;
if(root->val>t->val)return depth(root->left,t)+1;
else return depth(root->right, t)+1;
}
};
解法二:
遞歸有點難想出來,重點是如果找到了一個根節點,p、q是位於他的兩側的。
所以如果p q都小於root,就說明他們都在root的左邊。
大於反之;
一左一右,就說明這個root是他們最後一個祖先節點了。
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
int value_p = p->val, value_q = q->val, value_mid = root->val;
if (value_p < value_mid && value_q < value_mid) return lowestCommonAncestor(root->left, p, q);
if (value_p > value_mid && value_q > value_mid) return lowestCommonAncestor(root->right, p, q);
else return root;
}
};