把二叉樹列印成多行
阿新 • • 發佈:2018-12-10
題目
從上到下按層列印二叉樹,同一層結點從左至右輸出。每一層輸出一行。
思路1
層序遍歷,只有一點需要考慮,就是如何把層序遍歷序列按層分開來,因為返回的是每一層的遍歷序列。
最簡單的做法是遍歷當前層的時候就逐步確定下一層最右邊的結點
(遍歷的過程中,下一層的最右結點一直在更新),這樣當這一層遍歷完時,下一層的最右結點也就確定了,這樣當遍歷下一層的時候就有了一個終點,這樣子就完成了分層。
/**
* 解法1:記錄每層的最右節點:
* 遍歷當前層的時候就逐步確定下一層最右邊的結點(遍歷的過程中,下一層的最右結點一直在更新),
* 這樣當這一層遍歷完時,下一層的最右結點也就確定了,這樣當遍歷下一層的時候就有了一個終點,
* 這樣子就完成了分層。
*/
public List<List<Integer>> levelOrder2(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
ArrayList<Integer> arr = new ArrayList<>();
if (root == null)
return result;
TreeNode pre = root, now = null ;
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while (!q.isEmpty()) {
TreeNode node = q.poll();
arr.add(node.val);
if (node.left != null) {
q.offer(node.left);
now = node.left;
}
if (node.right != null) {
q.add(node.right);
now = node.right;
}
//當前遍歷節點是某層的最右節點時就新增arr到result中,並清空arr以便接著存下一層
if (pre == node) {
result.add(arr);
/*
* 這裡直接用clear錯誤,因為result裡儲存的是arr指向的這個ArrayList物件,這裡把arr清空了意味著
* 把這個物件裡的內容清空了,所以result裡就為空了!
* 應該將arr再重新指向一個新的ArrayList物件即可!
*/
arr = new ArrayList<>();
//pre更新為下一層節點的最右節點,即為node.right如果node.rigth存在的話!
pre = now;
}
}
return result;
}
思路2
使用一個佇列,根據佇列中元素的個數來確定分層,當佇列中的某一層元素全部遍歷完(遍歷完就會彈出佇列)後,佇列中就只有下一層的元素,所以,可以依據這個來分層。
/**
* 解法2:使用一個佇列
* 根據佇列中元素的個數來確定分層,當佇列中的某一層元素全部遍歷完(遍歷完就會彈出佇列)後,
* 佇列中就只有下一層的元素此時佇列長度length就是下一層元素個數,然後再遍歷這一層(即遍歷length長度)時將下一層入隊。
*/
public List<List<Integer>> levelOrder3(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) return result;
//只用一個佇列來做
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while (!q.isEmpty()) {
List<Integer> oneLevel = new ArrayList<>();
int size = q.size();
for (int i = 0; i < size; ++i) {
//獲取隊頭元素而不移出該元素,使用element()或者peek(),獲取並移出使用poll()
TreeNode node = q.poll();
oneLevel.add(node.val);
if (node.left!=null) q.offer(node.left);
if (node.right!=null) q.offer(node.right);
}
result.add(oneLevel);
}
return result;
}
思路3
思路2需要在佇列中確定某一層的個數,這個操作可以用兩個佇列來實現,也就是遍歷一個佇列的所有孩子放到另一個佇列中;遍歷另一個佇列時,又把另一個佇列中的所有孩子又放回來這個佇列,這樣也實現了分層。而且,兩個佇列始終有一個佇列保持為空。這樣子的操作叫滾動佇列。
/**
* 解法3:使用兩個佇列current和next
* 只用一個佇列要每次記錄下一層的個數,這個操作其實可以用兩個佇列來實現,也就是遍歷一個佇列的所有孩子放到另一個佇列中;
* 遍歷另一個佇列時,又把另一個佇列中的所有孩子又放回來這個佇列,這樣也實現了分層。
* 而且,兩個佇列始終有一個佇列保持為空。這樣子的操作叫滾動佇列。
*/
public List<List<Integer>> levelOrder4(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
//使用兩個佇列來做
Queue<TreeNode> current = new LinkedList<>();
Queue<TreeNode> next = new LinkedList<>();
if(root == null) {
return result;
} else {
current.offer(root);
}
while (!current.isEmpty()) {
//儲存某一層的元素
ArrayList<Integer> level = new ArrayList<>();
while (!current.isEmpty()) {
TreeNode node = current.poll();
level.add(node.val);
if (node.left != null) next.add(node.left);
if (node.right != null) next.add(node.right);
}
result.add(level);
// 交換兩個佇列
Queue<TreeNode> tmp = current;
current = next;
next = tmp;
}
return result;
}
思路4
雖然是層序遍歷,但是我們也可以用DFS
來做,雖然DFS
是深度優先(這是從豎直方向看),但是從水平方向看,每一層遍歷結點的順序依然是從左到右。這就是用DFS
做的依據。
/**
* 解法4:遞迴法
* 雖然是層序遍歷,但是我們也可以用dfs來做,雖然dfs是深度優先(這是從豎直方向看),
* 但是從水平方向看,每一層遍歷結點的順序依然是從左到右。這就是用dfs做的依據。
*/
public List<List<Integer>> levelOrder1(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
//根節點為第一層
traverse(root, 1, result);
return result;
}
當然這題我們還需要將給定的陣列轉化成二叉樹,使用下面的函式實現:
/**
* 根據給定的陣列建立二叉樹
*/
public TreeNode createBinaryTreeByArray(int[] array, int index) {
TreeNode tn = null;
if (index < array.length) {
int value = array[index];
tn = new TreeNode(value);
tn.left = createBinaryTreeByArray(array, 2 * index + 1);
tn.right = createBinaryTreeByArray(array, 2 * index + 2);
return tn;
}
return tn;
}
最終的測試程式碼如下:
@Test
public void test1() {
Scanner sc = new Scanner(System.in);
System.out.println("Enter the length of Array:");
while(sc.hasNext()) {
int length = sc.nextInt();
int[] arr = new int[length];
for(int i = 0;i<length;i++) {
arr[i] = sc.nextInt();
}
TreeNode root = createBinaryTreeByArray(arr, 0);
System.out.println(levelOrder1(root));
System.out.println(levelOrder2(root));
System.out.println(levelOrder3(root));
System.out.println(levelOrder4(root));
}
sc.close();
}
}
測試結果如下: