Java資料結構和演算法(二)樹的基本操作
阿新 • • 發佈:2018-12-11
Java資料結構和演算法(二)樹的基本操作
一、樹的遍歷
二叉樹遍歷分為:前序遍歷、中序遍歷、後序遍歷。即父結點的訪問順序
1.1 前序遍歷
基本思想:先訪問根結點,再先序遍歷左子樹,最後再先序遍歷右子樹即根—左—右。圖中前序遍歷結果是:1,2,4,5,7,8,3,6。
// 遞迴實現前序遍歷 public void preOrder() { System.out.printf("%s ", value); if (left != null) { left.preOrder1(); } if (right != null) { right.preOrder1(); } } // 非遞迴實現前序遍歷 public void preOrder1() { TreeNode<E> head = this; Stack<TreeNode<E>> stack = new Stack(); stack.push(head); while (!stack.isEmpty()) { TreeNode<E> pop = stack.pop(); System.out.printf("%s ", head.value); if (pop.right != null) { stack.push(pop.right); } if (pop.left != null) { stack.push(pop.left); } } }
1.2 中序遍歷
// 遞迴實現中序遍歷 public void midOrder() { if (left != null) { left.preOrder1(); } System.out.printf("%s ", value); if (right != null) { right.preOrder1(); } } // 非遞迴實現中序遍歷 public void midOrder1() { TreeNode<E> head = this; Stack<TreeNode<E>> stack = new Stack(); while (!stack.isEmpty() || head != null) { if (head != null) { // 先將左結點全部入棧 stack.push(head); head = head.left; } else { // 左結點全部入棧後就需要依次彈出,並處理右結點 head = stack.pop(); System.out.printf("%s ", head.value); head = head.right; } } }
1.3 後序遍歷
// 遞迴實現後序遍歷 public void postOrder() { if (left != null) { left.preOrder1(); } if (right != null) { right.preOrder1(); } System.out.printf("%s ", value); } // 非遞迴實現後序遍歷 public void postOrder2() { TreeNode<E> head = this; Stack<TreeNode<E>> stack1 = new Stack(); Stack<TreeNode<E>> stack2 = new Stack(); stack1.push(head); while (!stack1.isEmpty()) { TreeNode<E> tmp = stack1.pop(); stack2.push(tmp); if (tmp.left != null) { stack1.push(tmp.left); } if (tmp.right != null) { stack1.push(tmp.right); } } while (!stack2.isEmpty()) { TreeNode<E> tmp = stack2.pop(); System.out.printf("%s ", tmp.value); } }
1.4 層次遍歷
public void levelOrder() {
TreeNode<E> head = this;
Queue<TreeNode<E>> queue = new ArrayDeque<>();
queue.offer(head);
while (!queue.isEmpty()) {
for (int i = 0; i < queue.size(); i++) {
TreeNode<E> tmp = queue.poll();
System.out.printf(String.valueOf(tmp.value) + " ");
if (tmp.left != null) {
queue.offer(tmp.left);
}
if (tmp.right != null) {
queue.offer(tmp.right);
}
}
}
}
二、樹的深度
// 非遞迴求樹的最大和最小深度
public int maxLevel() {
int level = 0;
TreeNode<E> head = this;
Queue<TreeNode<E>> queue = new ArrayDeque<>();
queue.offer(head);
while (!queue.isEmpty()) {
for (int i = 0; i < queue.size(); i++) {
level++;
TreeNode<E> tmp = queue.poll();
if (tmp.left != null) {
queue.offer(tmp.left);
}
if (tmp.right != null) {
queue.offer(tmp.right);
}
}
}
return level;
}
public int minLevel() {
int level = 0;
TreeNode<E> head = this;
Queue<TreeNode<E>> queue = new ArrayDeque<>();
queue.offer(head);
while (!queue.isEmpty()) {
for (int i = 0; i < queue.size(); i++) {
level++;
TreeNode<E> tmp = queue.poll();
if (tmp.left == null && tmp.right == null) {
return level;
}
if (tmp.left != null) {
queue.offer(tmp.left);
}
if (tmp.right != null) {
queue.offer(tmp.right);
}
}
}
return 0;
}
// 遞迴求樹的最大和最小深度
public int minLevel(TreeNode head) {
if (head == null) {
return 0;
}
if (head.left == null && head.right == null) {
return 1;
}
if (head.left == null && head.right != null) {
return minLevel(head.left) + 1;
}
if (head.left != null && head.right == null) {
return minLevel(head.right) + 1;
}
return Math.min(minLevel(head.left), minLevel(head.right)) + 1;
}
三、求兩個節點的公共祖先
// 遞迴求兩個結點的公共祖先,一個結點可以是自己的祖先
public TreeNode ancestor(TreeNode root, TreeNode node1, TreeNode node2) {
if (root == node1 || root == node2) {
return root;
}
TreeNode left = ancestor(root.left, node1, node2);
TreeNode right = ancestor(root.right, node1, node2);
if (left == null || right == null) {
return root;
}
return left != null ? left : right;
}
每天用心記錄一點點。內容也許不重要,但習慣很重要!