目录
讀題
669. 修剪二叉搜索树
看完代码随想录之后的想法
看完之後對於刪除節點又有更深的體會,在這個題目當中,想像成是連續刪除,所以在遇到終止條件的時候,還是要繼續往下遞迴,假設是單次刪除,則是直接return,代碼看著不難,但是其中的思想真的很難去想到
108.将有序数组转换为二叉搜索树
自己看到题目的第一想法
我知道中序遍歷之後跟題目給的數組一致,就是一個單調遞增的數列,但假設要回傳的話,那第一個點就是,root是誰,之後用迴圈去處理左右似乎就比較簡單一些了
嘗試去實現過後,發現題目沒那麼簡單,嘗試寫了一版後,雖然一開始的測試用例可以過,但是後續的測試用例都過不了。
後來看著修剪二插搜索,二叉搜索树中的插入操作以及中序以及後序數組組成唯一的二叉樹,藉由終止條件,來回傳NULL或者葉子節點,每次都先處理當前的節點,建立root節點,用左右子樹來接住遇到由下到上的數值。
538.把二叉搜索树转换为累加树
自己看到题目的第一想法
這一題不難,只是將原本的中序遍歷左中右改成右中左,並且將前一個node記錄在pre Node當中,除了第一次pre Node等於NULL 不累加之外,其餘數值都進行累加,整體來說還是比較簡單的一題。
669. 修剪二叉搜索树 - 實作
思路
- 終止條件
- root == NULL
- return root;
- root→val < low
- TreeNode* right = traversal(root→right, low, high);
- return right;\\回傳right 是因為右樹一定比當前的root節點小,所以當前root節點小於邊界,所以將左樹全部捨棄,return 右樹。
- root→val > high
- TreeNode* left = traversal(root→left, low, high);
- return left; \\回傳left 是因為右樹一定比當前的root節點大,所以當前root節點大於邊界,所以將右樹全部捨棄,return 左樹。
- root == NULL
- 如果沒有終止條件,往左右樹去遍歷。
- root→left = traversal(root→left, low, high);
- root→right = traversal(root→right, low, high);
- return root;
Code
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
if(root == NULL) return root;
if(root->val < low) {
TreeNode* right = trimBST(root->right, low, high);
return right;
}
if(root->val > high) {
TreeNode* left = trimBST(root->left, low, high);
return left;
}
root->left = trimBST(root->left, low, high);
root->right = trimBST(root->right, low, high);
return root;
}
};
108.将有序数组转换为二叉搜索树 - 實作
思路
錯誤思路
- 找到root節點
- 利用左右區間使用迴圈建立左右子樹
- 左右子樹接到root節點
正確思路
- 終止條件
- if nums.size() == 0 return NULL;
- if nums.size() == 1 return new TreeNode(nums[0]); 已經到葉子節點了
- 建立root節點
- TreeNode* root = new TreeNode(nums[ nums.size() / 2]);
- 建立左右區間
- vector<int> left (nums.begin(), nums.begin() + (nums.size() / 2));
- vector<int> right (nums.begin() + (nums.size() / 2) + 1 , nums.end()); \\要移除中間節點所以要加一
Code
錯誤代碼
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
if(nums.size() == 0) return NULL;
int rootvalue = nums.size() / 2;
printf("rootvalue %d\\n", rootvalue);
TreeNode* root = new TreeNode(nums[rootvalue]);
TreeNode* rootLeft = new TreeNode();
TreeNode* rootRight = new TreeNode();
root->left = rootLeft;
root->right = rootRight;
for(int i = 0; i < rootvalue; i++) {
TreeNode* right = new TreeNode(nums[i]);
printf("i=%d\\t left->val:%d\\t \\n", i, right->val);
rootLeft->right = right;
rootLeft = rootLeft->right;
}
for(int i = rootvalue + 1; i < nums.size(); i++) {
TreeNode* right = new TreeNode(nums[i]);
printf("i=%d\\t right->val:%d\\t \\n", i, right->val);
rootRight->right = right;
rootRight = rootRight->right;
}
root->left = root->left->right;
root->right = root->right->right;
return root;
}
};
正確思路
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
if(nums.size() == 0) return NULL;
if(nums.size() == 1) return new TreeNode(nums[0]);
TreeNode* root = new TreeNode(nums[nums.size() / 2]);
vector<int> numsLeft(nums.begin(), nums.begin() + (nums.size() / 2));
vector<int> numsRight(nums.begin() + (nums.size() / 2) + 1, nums.end());
root->left = sortedArrayToBST(numsLeft);
root->right = sortedArrayToBST(numsRight);
return root;
}
};
538.把二叉搜索树转换为累加树 - 實作
思路
- 建立一個pre Node記錄前一個節點
- 假設root == NULL 回傳root;
- 中序遍歷 → 根據題意將左右順序調換
- 右遞迴
- 中
- 假設pre != null,當前節點數值累加
- 向後移動,並將pre = root → 紀錄前一個節點
- 左遞迴
- return root;
Code
class Solution {
public:
TreeNode* pre = NULL;
TreeNode* convertBST(TreeNode* root) {
if(root==NULL) return root;
if(root->right) convertBST(root->right);
if(pre != NULL) {
root->val = pre->val + root->val;
}
pre = root;
if(root->left) convertBST(root->left);
return root;
}
};
第六章 二叉樹總結
part 01
在part 01,首先學習貫穿二叉樹整體解法的三個深度遍歷方式,前序遍歷、中序遍歷、後序遍歷,整體思想非常清楚,前中後主要就是看中間節點在哪個位置
part 02
了解另外一種遍歷方法,層序遍歷,利用queue來進行,在這個部分也使用了層序遍歷一口氣解了十道題目,讓我對層序遍歷的迭代法有比較深刻的理解,後續在實行上也因此在層序遍歷上有了初步的了解,但在part02中的翻轉與對稱二叉樹我還需要再花時間去理解
part 03
104二叉樹的最大深度、111二叉樹的最小深度以及完全二叉樹的節點各樹,只要善用part02 的觀念,這三題就會很容易解出來,不過遞迴要再複習,反而是迭代容易許多
part 04
110平衡二叉樹,在這裡我第一次地意識到前中後序的使用時機的不同,假設要去了解高度要使用後序遍歷(下到上),如果要求深度則要用前序遍歷(由上到下),
並且在part04也第一次碰到了回朔,也就是二叉樹的所有路徑,剛碰到時真的不知道怎麼辦,看完卡哥的講解才對回朔有了初步的了解。
part 05
在這裡又碰到中序以及後序遍歷來構造唯一的二叉樹這個新觀念,在這裡也知道除了中序跟前序或後序才能構造出唯一的二叉樹,但前序與後序不行,因為找不到分割點
而在part 05 又碰到了路徑總和這個回朔為重點的題目,以及層序遍歷做起來很簡單的尋找左下的值,慢慢要把前面的觀念結合再一起
part 06
又再一次用到了part05的觀念構造二叉樹,654最大二叉樹以及617合併二叉樹只是使用不同的條件,來進行構造
也碰到了二叉搜索樹的搜索,整體思想經過前面的淬鍊,理解起來就快很多了,
只是在驗證二叉搜索樹時,看到了自己的問題,對於二叉搜索樹的觀念不理解,分別是以下兩點
- 在二叉搜索樹中,用中序遍歷得出的數組會是單調遞增
- 深度遍歷要用Stack來實現,而不是層序遍歷
經過98驗證二叉搜索樹,才對這兩個觀念比較熟悉
part 07
這裡又碰到了刷題時的老朋友”雙指針” 在這裡530二叉搜索樹的最小絕對差、501二叉搜索樹中的眾數都發揮了很大的作用
530二叉搜索樹的最小絕對差,因為BST的特性會是單調遞增,利用這個特性使用雙指針法前後相減,假設min值大於這個相減的值,代表這個是當前最小的絕對差
501二叉搜索樹中的眾數,則也用到了part06的思想中序遍歷、單調遞增以及雙指針,將這些觀念綜合起來來解題
而在236. 二叉树的最近公共祖先則又用到了回朔的觀念來解題
part 08
在這裡就比較偏向是對於二叉樹的一些基本操作,分別是插入以及刪除,插入因為不用更改樹的結構比較好理解
但在刪除的部分,幸好卡哥說得非常清晰,所以在理解上不困難,但仍要自己多複習,不然還是無法解出
part 09
就像是前面的一些匯總
669修剪二叉樹,在part08時有進行過刪除,只是在part09 從單次刪除變成連續刪除目標值,整體思路需要在終止條件時,繼續遞迴,並且要將下面的值接住
108有序樹組轉換成二叉搜索樹,其實整體思路跟前面的構造二叉樹差不多就是一層一層往下去構造反而就不會太難了,一開始想複雜了
538把二叉搜索樹轉換為累加樹,在這裡其實困難不是很大就是利用前面有說過的雙指針法,將值給記住並賦值給當前節點而已,整體思路還是比較簡單的
心得
在二叉樹章節當中,我不敢說自己完全清晰了,但對於之前的我來說,已經是個巨大的進步,之前看到二叉樹只在前中後序遍歷就停下腳步了,沒有想過自己可以把這個部分完整的跑過一次,對於二叉樹的概念也有比較清晰的感覺了,只是接下來還是要對一些項目進行釐清
- 二叉樹
- 前中後序的迭代法與統一法
- 翻轉與對稱
- 回朔
- 前中後序
- 高度與深度
- 搜索樹是單調遞增 並使用中序遍歷
- 最小公共節點
- 最小公共節點 by BST
- 普通二叉樹的刪除
總結
自己实现过程中遇到哪些困难
今天在将有序数组转换为二叉搜索树的部分一開始沒有想清楚,而在修剪二叉樹的部分,因為卡哥的講解,其實沒有遇到太大的困難,只是自己還是需要去花時間理解這個部分
今日收获,记录一下自己的学习时长
今天大概學習了3hr,將總結全部寫完的時候,發現二叉樹章節真的是前後環環相扣,很多觀念都揉合在一起,但因為前面一步步的學習,在這裡反而理解的速度有明顯的上升。
相關資料
● 今日学习的文章链接和视频链接
669. 修剪二叉搜索树
题目链接/文章讲解: https://programmercarl.com/0669.修剪二叉搜索树.html
视频讲解: 你修剪的方式不对,我来给你纠正一下!| LeetCode:669. 修剪二叉搜索树_哔哩哔哩_bilibili
108.将有序数组转换为二叉搜索树
https://programmercarl.com/0108.将有序数组转换为二叉搜索树.html
视频讲解:构造平衡二叉搜索树!| LeetCode:108.将有序数组转换为二叉搜索树_哔哩哔哩_bilibili
538.把二叉搜索树转换为累加树
https://programmercarl.com/0538.把二叉搜索树转换为累加树.html
视频讲解:普大喜奔!二叉树章节已全部更完啦!| LeetCode:538.把二叉搜索树转换为累加树_哔哩哔哩_bilibili
1541





