做題目前隨便說點
-
樹是一種抽象數據類型,一種具有樹結構形式的數據集合。
-
節點個數確定,有層次關係。
-
有根節點。
-
除了根,每個節點有且只有一個父節點。
-
沒有環路。
-
所有數據結構都可以用鏈表表示或者用數組表示,樹也一樣。
27. 二叉樹的鏡像
請完成一個函數,輸入一個二叉樹,該函數輸出它的鏡像。
解題:
審題:
- “請完成一個函數”也就是說,答案是一個函數。
- 輸入的是一個二叉樹,可以是鏈表結構,也可以是數組結構。
- 輸出是一個鏡像,可以是鏈表結構也可以是數組結構。
- 什麼是鏡像,題目的要求一定是充分的,所以要認真品,用到每一個條件。
- 鏡像就是左右節點顛倒位置。
- 樹是通常都是從上往下看的。進入視野的節點會由少到多。這樣比較合適人的視覺習慣,當然我們也可以從下往上。當沒必要。
- 對比輸入和輸出,根節點的位置是沒變的,然後逐步把根節點的左右子樹的根節點對換。然後對左右子樹也做同樣的操作。根據樹的定義,左右子樹也是樹。
- 對調左右子樹的過程實際上就是修改樹的屬性的過程。
- 也就是說只是修改屬性,修改後結果依然是樹,我們只是訪問樹的每個節點,然後修改他們的屬性即可。
- 也就是說我們拿到根節點時只做兩件事情,修改節點屬性,然後把每個子節點輸入一個函數。這個函數的功能也是修改子節點的屬性,然後繼續每子節點的子節點傳入一個相同的函數。簡單的說就是遞歸的意思了。
- 可以不用遞歸算法麼?可以比較這種二叉樹操作具有數學規律,可以用歸納法找出遞推公式,然後一個一個地算。但是數據畢竟枯燥無味。結構化的思維,更符合人的思維方式。
- 這道題目說到底還是要遍歷整個樹,我們可以找一些簡單的樹模型來模擬,就會得出這個規律了。所以我們選用樹的遍歷框架作爲代碼的起步。然後再逐步擴展到整個項目。
- 如果是遞歸的話,還是鏈表結構的樹比較好一點,因爲沒有二義性。所以,建議把數組轉化爲鏈式結構再處理。
代碼框架如下:
class Solution {
TreeNode recursive(TreeNode node) {
// 邊界判斷
if(...) {
return node;
}
// 訪問節點,讀取或者修改屬性值。
// 遞歸訪問左右子樹
recursive(node.left);
recursive(node.right);
// 也可以在這裏讀取或者修改屬性值,這種是回溯思路。
// 返回值。
return node;
}
}
- 如果先遞歸左右子樹叫回溯法,因爲編譯成計算機指令程序的話,會先修改葉子的值再修改根的值。然後一步一步return。他是自下而上的update每個節點的屬性。
- 如果先訪問修改節點信息,然後才遞歸左右子樹,就是普通的非線性遞歸了。
(這裏不要扯複雜先。專心學習框架。暴力解題。)
開始解題:
class Solution {
public TreeNode mirrorTree(TreeNode root) {
// 邊界判斷
// explain: 根節點不爲空,其次就是有可以修改的屬性。理論上來說,空屬性也是可以修改,所以我們可以偷一下懶,就不對屬性進行判斷。
if(root == null) {
return root;
}
// 訪問節點,讀取或者修改屬性值。
// explain: 這其實就是讀取節點屬性,然後創建tmp變量做值對換。
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
// 遞歸訪問左右子樹
// explain: 先修改左子樹和先修改右子樹也是沒差別。
mirrorTree(root.left);
mirrorTree(root.right);
// 也可以在這裏讀取或者修改屬性值,這種是回溯思路。
// 返回值。
return root;
}
}
一道簡單題目可以講這麼複雜,其實有必要。簡單的問題知識多。
review一下代碼,沒有可以優化的地方就完事了。
總結:
該題目相對簡單,只用到了兩個數對換的算法。