BAT 經典演算法筆試題: 映象二叉樹
再過不到 2 個月,網際網路行業就要再次迎來面試高峰了。為了讓大家能順利透過所有面試環節必經的筆試階段,我提前給大夥準備了一套常見的演算法筆試題。這套演算法題來源於 LeetCode,題目都是 BAT、京東頭條滴滴美團等大型網際網路公司都喜歡考的題目。
演算法這個東西很難,縱使你瞭解了其中的邏輯,用程式碼寫出來仍然不是一件容易的事,內部有太多的細節需要處理。為了便於大夥輕鬆理解演算法邏輯,對於所有的題目,我會使用圖文加動畫的形式進行講解,讓讀者可以輕鬆理解演算法邏輯的同時,還可以留下深刻的影像不容易遺忘。
好,下面我們開始今天的演算法題:映象二叉樹,就是將一顆二叉樹上的左右節點全部交換,就好比鏡子裡的二叉樹,左右方向是反過來的。
二叉樹節點表示
class Node<T> {
T value;
Node<T> left;
Node<T> right;
Node(T value) {
this.value = value;
}
Node(T value, Node<T> left, Node<T> right) {
this.value = value;
this.left = left;
this.right = right;
}
}
一個引數的構造器是葉子節點,三個引數的構造器是中間節點,看到這裡讀者應該知道這是 Java 語言,我使用了範型
構造二叉樹
Node<Integer> node2 = new Node<>(2);
Node<Integer> node3 = new Node<>(3);
Node<Integer> node1 = new Node<>(1, node2, node3);
// 一次性構造
Node<Integer> node1 = new Node<>(1, new Node<>(2), new Node<>(3));
呈現二叉樹結構
如果我們正確寫出了映象演算法,那如何來直觀驗證映象的結構是否正確呢?LeetCode 使用的是單元測試,它使用一系列的單元測試和壓力測試指令碼程式碼來驗證使用者編寫的演算法的正確性和效能。但是我們不要這樣做,因為不直觀。我們選擇對二叉樹的結構內容進行直觀的呈現,如此就可以使用肉眼來進行快速驗證。如何直觀呈現呢?我們使用最簡單的括號表示法,它並不是最直觀的,但是它易於實現。
class Node<T> {
T value;
Node<T> left;
Node<T> right;
public String toString() {
// 葉節點
if (left == right) {
return String.format("[%d]", value);
}
// 中間節點
return String.format("(%d, %s, %s)", value, left, right);
}
}
Node<Integer> node2 = new Node<>(2);
Node<Integer> node3 = new Node<>(3);
Node<Integer> node1 = new Node<>(1, node2, node3);
System.out.println(node1);
System.out.println(node2);
System.out.println(node3);
---------------------
[2]
[3]
(1, [2], [3])
遞迴映象二叉樹
映象二叉樹有兩種演算法,一種是遞迴,一種是迭代。遞迴的演算法簡單易於理解,我們先使用遞迴演算法來求解。遞迴的思想就是深度遍歷,遇到一個節點,先遞迴映象它的左子樹,再遞迴映象它的右子樹,然後再交換自己的左右子樹。如果遇到的是葉子節點,就不必處理了。為了避免無限遞迴,一定要及時設定好遞迴的停止條件,在這裡停止條件就是遇到了葉節點。
public void mirrorFrom(Node<T> node) {
// 葉子結點
if (node.left == node.right) {
return;
}
// 遞迴映象左子樹
if (node.left != null)
mirrorFrom(node.left);
// 遞迴映象右子樹
if (node.right != null)
mirrorFrom(node.right);
// 交換當前節點的左右子樹
Node<T> tmp = node.left;
node.left = node.right;
node.right = tmp;
}
迭代映象二叉樹
遞迴演算法的優勢在於邏輯簡單,缺點在於每一次遞迴呼叫函式都會增加一個新的函式堆疊,如果樹的深度太深,函式的堆疊記憶體就會持續走高,一不小心就會觸發臭名昭著的異常 StackOverflowException。如果二叉樹分佈比較均勻,那麼樹就不會太深,但是遇到偏向的二叉樹,比如所有的子節點都掛在了右節點上,二叉樹就退化成了線性連結串列,連結串列的長度就是樹的深度,那這顆樹的深度就比較可怕了。
所以下面我來介紹第二種演算法 —— 迭代演算法。迭代的基本思想就是將遞迴演算法轉換成迴圈演算法,用一個 for 迴圈來交換所有節點的左右子樹。我們需要再重新理解一下演算法的目標,這個目標非常簡單,就是遍歷整顆二叉樹,將遍歷途中遇到的所有中間節點的左右指標交換一下。
那如何設計這個迴圈呢?一個很明顯的方法是分層迴圈,第一次迴圈處理第 1 層二叉樹節點,也就是唯一的根節點。下一個迴圈處理第 2 層二叉樹節點,也就是根節點的兩個兒子。如此一直處理到最底層,迴圈的終止條件就是後代節點沒有了。所以我們需要使用一個容器來容納下一次迴圈需要處理的後代節點。
public MirrorBinaryTree<T> mirrorByLoop() {
// 空樹不必處理
if (root == null) {
return this;
}
// 當前迴圈需要處理的節點
LinkedList<Node<T>> expandings = new LinkedList<>();
expandings.add(root);
// 沒有後臺節點就可以終止迴圈
while (!expandings.isEmpty()) {
// 下一次迴圈需要處理的節點
// 也就是當前節點的所有兒子節點
LinkedList<Node<T>> nextExpandings = new LinkedList<>();
// 遍歷處理當前層的所有節點
for (Node<T> node : expandings) {
// 將後代節點收集起來,留著下一次迴圈
if (node.left != null) {
nextExpandings.add(node.left);
}
if (node.right != null) {
nextExpandings.add(node.right);
}
// 交換當前節點的左右指標
Node<T> tmp = node.left;
node.left = node.right;
node.right = tmp;
}
// 將後代節點設定為下一輪迴圈的目標節點
expandings = nextExpandings;
}
return this;
}
完整程式碼
下面的完整程式碼可以複製過去直接執行,如果讀者還是不夠明白歡迎在留言區及時提問。
package leetcode;
import java.util.LinkedList;
public class MirrorBinaryTree<T> {
static class Node<T> {
T value;
Node<T> left;
Node<T> right;
Node(T value) {
this.value = value;
}
Node(T value, Node<T> left, Node<T> right) {
this(value);
this.left = left;
this.right = right;
}
public String toString() {
if (left == right) {
return String.format("[%d]", value);
}
return String.format("(%d, %s, %s)", value, left, right);
}
}
private Node<T> root;
public MirrorBinaryTree(Node<T> root) {
this.root = root;
}
public MirrorBinaryTree<T> mirrorByLoop() {
if (root == null) {
return this;
}
LinkedList<Node<T>> expandings = new LinkedList<>();
expandings.add(root);
while (!expandings.isEmpty()) {
LinkedList<Node<T>> nextExpandings = new LinkedList<>();
for (Node<T> node : expandings) {
if (node.left != null) {
nextExpandings.add(node.left);
}
if (node.right != null) {
nextExpandings.add(node.right);
}
Node<T> tmp = node.left;
node.left = node.right;
node.right = tmp;
}
expandings = nextExpandings;
}
return this;
}
public MirrorBinaryTree<T> mirrorByRecursive() {
mirrorFrom(root);
return this;
}
public void mirrorFrom(Node<T> node) {
if (node.left == node.right) {
return;
}
if (node.left != null)
mirrorFrom(node.left);
if (node.right != null)
mirrorFrom(node.right);
Node<T> tmp = node.left;
node.left = node.right;
node.right = tmp;
}
public String toString() {
if (root == null) {
return "()";
}
return root.toString();
}
public static void main(String[] args) {
Node<Integer> root = new Node<>(
1,
new Node<>(
2,
new Node<>(4),
new Node<>(
5,
new Node<>(8),
null)),
new Node<>(
3,
new Node<>(
6,
null,
new Node<>(9)),
new Node<>(7)));
MirrorBinaryTree<Integer> tree = new MirrorBinaryTree<>(root);
System.out.println(tree);
tree.mirrorByRecursive();
System.out.println(tree);
tree.mirrorByLoop();
System.out.println(tree);
}
}
---------------
(1, (2, [4], (5, [8], null)), (3, (6, null, [9]), [7]))
(1, (3, [7], (6, [9], null)), (2, (5, null, [8]), [4]))
(1, (2, [4], (5, [8], null)), (3, (6, null, [9]), [7]))
擴充套件思考:為什麼鏡子裡面左右是反過來的,但是上下不是?這不是一道程式設計題,但是確實不容易回答
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31561269/viewspace-2374664/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- BAT 經典演算法筆試題 —— 磁碟多路歸併排序BAT演算法筆試排序
- BAT 經典演算法筆試題 —— 逆轉單向連結串列BAT演算法筆試
- [演算法總結] 20 道題搞定 BAT 面試——二叉樹演算法BAT面試二叉樹
- LeetCode 74,直擊BAT經典面試題LeetCodeBAT面試題
- JavaScript經典筆試題JavaScript筆試
- BAT面試經典送送送命題——微服務架構BAT面試微服務架構
- BAT經典面試題,深入理解Java記憶體模型JMMBAT面試題Java記憶體模型
- 二叉樹的映象二叉樹
- google經典演算法面試題-雞蛋問題Go演算法面試題
- 經典面試題面試題
- Mysql中的筆試和麵試---20個經典面試題MySql筆試面試題
- javascript經典面試題JavaScript面試題
- java經典面試題Java面試題
- Js 經典面試題JS面試題
- 前端經典面試題前端面試題
- BAT前端經典面試問題:史上最最最詳細的手寫Promise教程BAT前端面試Promise
- 27. 二叉樹的映象二叉樹
- [每日一題] 第二十五題:二叉樹的映象每日一題二叉樹
- 幾道 BAT 演算法面試中經常問的「字串」問題BAT演算法面試字串
- 面試題7:重建二叉樹面試題二叉樹
- 前端面試必備-40道LeetCode經典面試演算法題前端面試LeetCode演算法
- 幾道和「二叉樹」有關的演算法面試題二叉樹演算法面試題
- 資料結構和演算法面試題系列—二叉樹面試題彙總資料結構演算法面試題二叉樹
- 經典Java面試題收集Java面試題
- Google經典面試題解析Go面試題
- 阿里歷年經典Java面試題彙總,想進BAT你還不快收藏!阿里Java面試題BAT
- Codeup《演算法筆記》9.2小節——資料結構專題(2)->二叉樹的遍歷->二叉樹演算法筆記資料結構二叉樹
- 刷題系列 - Python判斷是否映象對稱二叉樹Python二叉樹
- JZ-018-二叉樹的映象二叉樹
- NC72—二叉樹的映象二叉樹
- JavaScript經典面試題詳解JavaScript面試題
- 經典Java面試題收集(二)Java面試題
- Runtime經典面試題(附答案)面試題
- 經典 JS 閉包面試題JS面試題
- leetcode-面試經典150題LeetCode面試
- 20道JavaScript經典面試題JavaScript面試題
- 前端經典面試題(有答案)前端面試題
- Python經典面試題(附答案)!Python面試題