二叉樹
TreeNode定義:
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
中序遍歷
左子樹——根——右子樹的順序
public void inOrder(TreeNode r) {
if (r != null) {
inOrder(r.left);
System.out.println(x.key);
inOrder(r.right);
}
}
層次遍歷
意思是:從上到下按層次訪問該二叉樹(每一層單獨輸出一行)
public static void levelOrder(TreeNode r) {
Queue<TreeNode> q = new ArrayDeque<TreeNode>();
q.add(r);
while(!q.isEmpty()) {
TreeNode tmp = q.poll();
System.out.println(tmp.val);
if (tmp.left != null) {
q.add(tmp.left);
}
if (tmp.right != null) {
q.add(tmp.right);
}
}
}
求樹高
public static int depth(TreeNode root) {
if (r == null)
return 0;
int l = depth(root.left);
int r = depth(root.right);
return (l > r ? l : r) + 1;
}
總結點數
public static int nodeNumbers(TreeNode r) {
if (r == null)
return 0;
return nodeNumbers(r.left) + nodeNumbers(r.right) + 1;
}
葉結點數
public static int leafNumbers(TreeNode r) {
if (r == null)
return 0;
if (r.left == null && r.right == null) {
return 1;
}
return leafNumbers(r.left) + leafNumbers(r.right) + 1;
}
判斷二叉排序樹
public static boolean isBST(TreeNode root) {
return isBST(root, Integer.MIN_VALUE, Integer.MAX_VALUE);
}
public static boolean isBST(TreeNode root, int left, int right) {
if(root == null) return true;
return (left < root.val && root.val < right && isBST(root.left, left ,root.val) && isBST(root.right, root.val, right));
}
求二叉樹的最長距離
對於二叉樹,若要兩個節點U,V相距最遠,有兩種情況:
1,從U節點到V節點之間的路徑經過根節點
2,從U節點到V節點之間的路徑不經過根節點,這種情況下,U,V節點必定在根節點的左子樹或者右子樹上,
這樣就轉化爲求以根節點的孩子節點爲根節點的二叉樹中最遠的兩個節點間的距離
上面所說的經過根節點,是指路徑中包含根節點,例如:假如上圖中只有左子樹FGHA,
那麼最長距離的兩個節點是F, A,該路徑中包含根節點F,也稱爲經過根節點。
/*
* 思想就是使用動態規劃的方式每次求得節點左右子樹的最大路徑長度,
* 分別記錄在leftLen與rightLen中,這樣就記錄了每個節點左右分支的最大深度
* 後序,dfs
*/
public int findMaxLen2(TreeNode root) {
if(root == null) {
return 0;
}
if(root.left != null)
{
root.leftMaxValue = findMaxLen2(root.left) + 1;
} else {
root.leftMaxValue = 0;
}
if(root.right != null)
{
root.rightMaxValue = findMaxLen2(root.right) + 1;
} else {
root.rightMaxValue = 0;
}
maxLen = Math.max(maxLen, root.leftMaxValue + root.rightMaxValue);
return root.rightMaxValue > root.leftMaxValue ? root.rightMaxValue : root.leftMaxValue;
}
求二叉樹寬度
就是層次遍歷。在上一層遍歷完成後,下一層的所有節點已經放到隊列中,此時隊列中的元素個數就是下一層的寬度。以此類推,依次遍歷下一層即可求出二叉樹的最大寬度。
public static int levelOrder(TreeNode r) {
if (r == null) {
return 0;
}
Queue<TreeNode> q = new ArrayDeque<TreeNode>();
int maxWidth = 1;
q.add(r);
while(!q.isEmpty()) {
int size = q.size();
while (size > 0) {
TreeNode tmp = q.poll();
if (tmp.left != null) {
q.add(tmp.left);
}
if (tmp.right != null) {
q.add(tmp.right);
}
size--;
}
maxWidth = Math.max(maxWidth, q.size());
}
return maxWidth;
}
打印二叉樹兩個葉子節點之間的路徑
最近公共祖先
第一遍從節點1開始回溯到根,第二遍從節點2回溯到visited爲true的點(temp2.father爲公共祖先)。用stack保存第二遍回溯的結果以便輸出
再遍歷一遍輸出就行了
public static void printPath(TreeNode leaf1, TreeNode leaf2) {
Stack<Integer> stack = new Stack<Integer>();
TreeNode temp1 = leaf1;
temp1.visited = true;
while (temp1.father != null) {
temp1 = temp1.father;
temp1.visited = true;
}
TreeNode temp2 = leaf2;
temp2.visited = true;
while (!temp2.father.visited) { // temp2.father爲公共祖先
stack.add(temp2.val);
temp2 = temp2.father;
temp2.visited = true;
}
print(leaf1, temp2, stack);
}
public static void print(TreeNode temp1, TreeNode temp2, Stack<Integer> stack) {
while (temp1.father != temp2.father) {
System.out.println(temp1.val);
temp1 = temp1.father;
temp1.visited = true;
}
System.out.println(temp1.father.val);
while (!stack.isEmpty()) {
System.out.println(stack.pop());
}
}
public class TreeNode {
int val;
boolean visited;
TreeNode left;
TreeNode right;
TreeNode father;
public TreeNode (int val) {
this.val = val;
}
}
二叉搜索樹
定義:對任何結點,其左子樹中的關鍵字最大不超過x.key,右子樹的關鍵字最小不低於x.key(算法導論)
給定關鍵字,查找結點
public TreeNode search(TreeNode r,int k) {
while(r != null && r.key != k) {
if (k < r.key) {
r = r.left;
} else{
r = r.right;
}
}
return r;
}
最小關鍵字和最大關鍵字
public TreeNode findMinimum(TreeNode x) {
while (x.left != null) {
x = x.left;
}
return x;
}
public TreeNode findMaximum(TreeNode x) {
while(x.right != null){
x = x.right;
}
return x;
}
後繼
public TreeNode successor(TreeNode x) {
//(1)如果節點右子樹非空,則x的後繼是x右子樹中的最左節點
if(x.right != null) {
return findMinimum(x.right);
}
//(2)若右子樹爲空且有一個後繼y,則y是x的最底層祖先
TreeNode y = x.parent;
while (y != null && x == y.right) {
x = y;
y = y.parent;
}
return y;
}
插入結點
注意:插入的點一定在樹的最底分支
public TreeNode insert(TreeNode x,TreeNode z) {
TreeNode y = null;
TreeNode root = x;
if (x == null) {
x = z;//tree T was empty
return x;
}
while(x != null) {
y = x; //important step
if(z.key < y.key) {
x = x.left;
}
else{x = x.right;}
}
z.parent = y; // 若TreeNode結構無parent,直接刪去此行即可
if(z.key < y.key){
y.left = z;
}
else{
y.right = z;
}
return root;
}
刪除結點
定義子過程transplant,完成用另一個子樹替換子樹併成爲雙親的孩子節點
用一個以v爲根的子樹替換以u爲根的子樹,u的雙親變爲v的雙親,v成爲u的雙親的相應孩子
public void transplant(TreeNode r,TreeNode u,TreeNode v) {
if (u.parent == null) {
r = v;
}
else if (u == u.parent.left) {
u.parent.left = v;
}
else {
u.parent.right = v;
}
if (v != null) {
v.parent = u.parent;
}
}
public void delete(TreeNode r,TreeNode z) {
if (z.left == null) {
transplant(r, z, z.right);
}
else if (z.right == null) {
transplant(r, z, z.left);
}
else{
TreeNode y = findMinimum(z.right);
if (y.parent != z) {
transplant(r, y, y.right);
y.right = z.right;
y.right.parent = y;
}
transplant(r,z,y);
y.left = z.left;
y.left.parent = y;
}
}
合併兩個排序二叉樹
很容易想到把一顆樹的每一個結點依次添加到另一顆樹中,每次插入的平均時間複雜度爲O(logn),在最壞情況下的插入時間複雜度爲O(m + logn)。合併爲一棵樹的平均時間複雜度爲O(mlog(m + n))。
第二種思路:先將二叉樹轉爲鏈表,合併鏈表之後,再將鏈表轉爲二叉樹
轉二叉樹爲雙向鏈表:
TreeNode change(TreeNode root) {
if (root == null) {
return null;
}
//轉換左子樹,連接到根節點
if (root.left != null) {
TreeNode left = change(root.left);
for(;left.right != null; left =left.right);
left.right = root;
root.left =left;
}
//轉換右子樹,根節點連接到右子樹
if (root.right!= null) {
TreeNode right= change(root.right);
for(;left.left!= null; right=right.left);
right.left = root;
root.right=right;
}
return root;
}
需要o(1)空間的方法:
public TreeNode prev = null;
// 按中序(左中右)順序轉二叉樹爲list(適合二叉搜索樹,根爲4,結果:1234567)
public void BSTtoList(TreeNode root) {
if (root == null) {
return;
}
BSTtoList(root.left);
if (prev != null) {
prev.right = root;
prev.left = null;
}
prev = root;
BSTtoList(root.right);
}
// 前序(根左右)轉二叉樹爲list,根爲1,結果:1234567
/*樹:
1
2 5
3 4 6 7
*/
public void flatten(TreeNode root) {
if (root == null)
return;
flatten(root.right);
flatten(root.left);
root.right = prev;
root.left = null;
prev = root;
}
鏈表轉二叉樹(leetcode 109):
方法1:(in place)
public TreeNode LinkedListToBalancedBST(TreeNode root) {
int num = 0;
TreeNode r = root;
while (r != null) {
num++;
r = r.right;
}
return ListToBST(root, num);
}
public TreeNode cur = null;
public TreeNode ListToBST(TreeNode root, int num) {
if (num <= 0) return null;
if (cur == null) cur = root;
TreeNode left = ListToBST(root, num / 2);
TreeNode temp = cur;
cur = cur.right;
temp.right = ListToBST(cur, num - 1 - num / 2);
temp.left = left;
return temp;
}
方法2:
private ListNode node;
public TreeNode sortedListToBST(ListNode head) {
if(head == null){
return null;
}
int size = 0;
ListNode runner = head;
node = head;
while(runner != null){
runner = runner.next;
size ++;
}
return inorderHelper(0, size - 1);
}
public TreeNode inorderHelper(int start, int end){
if(start > end){
return null;
}
int mid = start + (end - start) / 2;
TreeNode left = inorderHelper(start, mid - 1);
TreeNode treenode = new TreeNode(node.val);
treenode.left = left;
node = node.next;
TreeNode right = inorderHelper(mid + 1, end);
treenode.right = right;
return treenode;
}
方法3:
public TreeNode sortedListToBST(ListNode head) {
if(head==null) return null;
return toBST(head,null);
}
public TreeNode toBST(ListNode head, ListNode tail){
ListNode slow = head;
ListNode fast = head;
if(head==tail) return null;
while(fast!=tail&&fast.next!=tail){
fast = fast.next.next;
slow = slow.next;
}
TreeNode thead = new TreeNode(slow.val);
thead.left = toBST(head,slow);
thead.right = toBST(slow.next,tail);
return thead;
}