二叉树
二叉排序树(也叫二叉搜索树,它们是一回事)
根据后序数组重建搜索二叉树
【题目】
给定一个整型数组arr,已知其中没有重复值,判断arr是否可能是节
点值类型为整型的搜索二叉树后序遍历的结果。
进阶:如果整型数组arr中没有重复值,且已知是一棵搜索二叉树的后
序遍历结果,通过数组arr重构二叉树。
分析
一个二叉搜索树后序遍历的结果数组满足怎样的特征?
public class PosArrayToBST {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int value) {
this.value = value;
}
}
public static boolean isPostArray(int[] arr) {
if (arr == null || arr.length == 0) {
return false;
}
return isPost(arr, 0, arr.length - 1);
}
//是否是BST
public static boolean isPost(int[] arr, int start, int end) {
if (start == end) {
return true;
}
//小于部分的最右下标 大于区域的最左下标 先赋无效值
int lessRight = -1;
int moreLeft = end;
for (int i = start; i < end; i++) {
if (arr[end] > arr[i]) {//求小于区域的最右下标
lessRight = i;
} else {
//大于区域的最左下标 如果大于区域的值没有变过 说明大于区域不存在(右子树为空) 那么大于区域的最左下标等于i 如果大于区域最左下标的值变过,说明大于区域存在,维持下标不变
moreLeft = moreLeft == end ? i : moreLeft;
}
}
if (lessRight == -1 || moreLeft == end) {//值没有变过,属于右子树为空(根节点在数组中最大)的情况,继续递归
return isPost(arr, start, end - 1);
}
if (lessRight != moreLeft - 1) {//小于区域必定和大于区域相邻
return false;
}
//BST的左右子树也必为BST &&运算合并
return isPost(arr, start, lessRight) && isPost(arr, moreLeft, end - 1);
}
//重构BST
public static Node posArrayToBST(int[] posArr) {
if (posArr == null) {
return null;
}
return posToBST(posArr, 0, posArr.length - 1);
}
public static Node posToBST(int[] posArr, int start, int end) {
if (start > end) {
return null;
}
Node head = new Node(posArr[end]);//后序遍历最后一个节点是根节点
//一开始给它们两个无效值
int lessRight = -1;//小于区域的下标
int moreLeft = end;
for (int i = start; i < end; i++) {
if (posArr[end] > posArr[i]) {//如果有小于区域的话,小于区域最右的下标
lessRight = i;
} else {//有大于区域的话返回大于区域最左的下标
moreLeft = moreLeft == end ? i : moreLeft;
//more的值有没有变过,如果没变过那么说明它只有小于部分 根 这个部分
//那么让它等于i 如果它的值变过 那么值维持不变
//只包含 大于部分 根也考虑进入了
}
}
//它之所以不需要中序是因为他是一课BST,搜索二叉树,它的后序遍历的结果
// 小于根的部分 大于根的部分 根
head.left = posToBST(posArr, start, lessRight);//如果它是搜索二叉树,那么它的子树也必都为搜索二叉树
head.right = posToBST(posArr, moreLeft, end - 1);
return head;
}
//后序遍历验证结果
public static void pre(Node head){
if (head==null) {
return;
}
pre(head.left);
pre(head.right);
System.out.print(head.value+" ");
}
public static void main(String[] args) {
int[] arr = { 2, 1, 3, 6, 5, 7, 4 };
boolean flag=isPost(arr, 0, arr.length -1 );
System.out.println(flag);
//printTree(posArrayToBST(arr));
if (flag) {
Node head=posArrayToBST(arr);
pre(head);
}else {
System.out.println("isn't BST pro_array");
}
}
}
这类题目的延伸
比如2010年浙江大学计算机及软件工程研究生机试真题 二叉搜索树
给定了一个BST的遍历序列,让你判断其他序列是否和给定的BST是同一棵树。只要重构这棵树,然后比较遍历结果就能解决。
在上一篇文章中,二叉树的重构必须借助中序+其他一种序列才能完成,BST为何仅凭借一种遍历结果就可以完成二叉树的重构呢。
之前普通二叉树的遍历,是因为仅凭借先序或者后序是无法区分根和左右子树的。必须借助中序才能确定左右子树的范围。而二叉排序树的先天性质可以让我们仅凭一种遍历结果就能区分左右子树。(题目中注释写的很详细了)对于所有的重构也都一样,只要能找到区分左右子树的方法,就可以重构。
二叉搜索树转双向链表
其实二叉树的左右孩子可以看做是双向链表的头尾指针
import java.util.LinkedList;
import java.util.Queue;
public class BSTtoDoubleLinkedList {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
public static Node convert1(Node head) {
Queue<Node> queue = new LinkedList<Node>();
inOrderToQueue(head, queue);
if (queue.isEmpty()) {
return head;
}
head = queue.poll();
Node pre = head;
pre.left = null;
Node cur = null;
while (!queue.isEmpty()) {
cur = queue.poll();
pre.right = cur;
cur.left = pre;
pre = cur;
}
pre.right = null;
return head;
}
public static void inOrderToQueue(Node head, Queue<Node> queue) {
if (head == null) {
return;
}
inOrderToQueue(head.left, queue);
queue.offer(head);
inOrderToQueue(head.right, queue);
}
public static Node convert2(Node head) {
if (head == null) {
return null;
}
Node last = process(head);
head = last.right;
last.right = null;
return head;
}
public static Node process(Node head) {
if (head == null) {
return null;
}
Node leftE = process(head.left); // left end
Node rightE = process(head.right); // right end
Node leftS = leftE != null ? leftE.right : null; // left start
Node rightS = rightE != null ? rightE.right : null; // right start
if (leftE != null && rightE != null) {
leftE.right = head;
head.left = leftE;
head.right = rightS;
rightS.left = head;
rightE.right = leftS;
return rightE;
} else if (leftE != null) {
leftE.right = head;
head.left = leftE;
head.right = leftS;
return head;
} else if (rightE != null) {
head.right = rightS;
rightS.left = head;
rightE.right = head;
return rightE;
} else {
head.right = head;
return head;
}
}
public static void printBSTInOrder(Node head) {
System.out.print("BST in-order: ");
if (head != null) {
in(head);
}
System.out.println();
}
public static void in(Node head) {
if (head == null) {
return;
}
in(head.left);
System.out.print(head.value + " ");
in(head.right);
}
public static void printDoubleLinkedList(Node head) {
System.out.print("Double Linked List: ");
Node end = null;
while (head != null) {
System.out.print(head.value + " ");
end = head;
head = head.right;
}
System.out.print("| ");
while (end != null) {
System.out.print(end.value + " ");
end = end.left;
}
System.out.println();
}
public static void main(String[] args) {
Node head = new Node(5);
head.left = new Node(2);
head.right = new Node(9);
head.left.left = new Node(1);
head.left.right = new Node(3);
head.left.right.right = new Node(4);
head.right.left = new Node(7);
head.right.right = new Node(10);
head.left.left = new Node(1);
head.right.left.left = new Node(6);
head.right.left.right = new Node(8);
printBSTInOrder(head);
head = convert1(head);
printDoubleLinkedList(head);
head = new Node(5);
head.left = new Node(2);
head.right = new Node(9);
head.left.left = new Node(1);
head.left.right = new Node(3);
head.left.right.right = new Node(4);
head.right.left = new Node(7);
head.right.right = new Node(10);
head.left.left = new Node(1);
head.right.left.left = new Node(6);
head.right.left.right = new Node(8);
printBSTInOrder(head);
head = convert2(head);
printDoubleLinkedList(head);
head = getDoubleLinklistHead(head);
System.out.println(head.right);
}
public static Node getDoubleLinklistHead(Node head){
if (head==null) {
return null;
}
return process2(head)[0];//
}
/*
* 完成二叉树的套路,用递归搞,递归只要run就可以,让父选择自己需要的信息
* 然后再考虑base case 叶节点/null
*/
//Node[] 长度为2 返回的这个数组内包含的都是每个子树中最左的节点和最右的节点
//Node[0] 最左
//Node[1] 最右
public static Node[] process2(Node head) {
if (head==null) {
return new Node[]{null,null};
}
head.left=null;
head.right=null;
//假设当前节点为5 要获取4和6
//左子树最右的节点和右子树最左的节点 用这样一个数组表示可以省去遍历行为
Node[] leftHeads=process2(head.left);//1 2 3 4
Node[] rightHeads=process2(head.right);//5 6 7 8
if (leftHeads[1]!=null) {//链表 左边有东西
leftHeads[1].right=head;
head.left=leftHeads[1];
}
if (rightHeads[0]!=null) {//右边有东旭
rightHeads[0].left=head;
head.right=rightHeads[0];
}
Node left=leftHeads[0]!=null?leftHeads[0]:head;//左子树有最左节点
Node right=rightHeads[1]!=null?rightHeads[1]:head;//右子树有最右节点
return new Node[]{left,right};
}
}
二叉搜索树转平衡搜索二叉树
通过有序数组生成平衡搜索二叉树
【题目】
给定一个有序数组sortArr,已知其中没有重复值,用这个有序
数组生成一棵平衡搜索二叉树,并且该搜索二叉树中序遍历的
结果与sortArr一致。
平衡二叉树 Balanced Binary Tree 左右子树的高度差不能超过1
思路:因为这棵树本来就是有序的,所以我们仅需要考虑如何使它变成一颗平衡二叉树
我们仅需要每次取数组最中间的节点作为根节点,那么它左右子树高度差必然不会超过1
递归这个过程,就可以得到平衡二叉树。很好理解,图就不画了,看代码理解。
public class SortedArrayToBalancedBST {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
public static Node generateTree(int[] sortArr) {
if (sortArr == null) {
return null;
}
return generate(sortArr, 0, sortArr.length - 1);
}
public static Node generate(int[] sortArr, int start, int end) {
if (start > end) {
return null;
}
int mid = (start + end) / 2;
Node head = new Node(sortArr[mid]);
head.left = generate(sortArr, start, mid - 1);
head.right = generate(sortArr, mid + 1, end);
return head;
}
public static void in(Node head){
if (head==null) {
return;
}
in(head.left);
System.out.print(head.value+" ");
in(head.right);
}
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Node head=generateTree(arr);
in(head);
}
}