二叉查找树构建

给定n构建最大值为n的二叉查找树——树的形态

种类
https://leetcode.com/problems/unique-binary-search-trees/
Given n, how many structurally unique BST’s (binary search trees) that store values 1…n?
For example,
Given n = 3, there are a total of 5 unique BST’s.

    1         3     3      2      1
    \       /     /      / \      \      3     2     1      1   3      2
    /     /       \                 \    2     1         2                 3

其实也是给定n个节点,判断一个二叉树有多少种形态。
思路:其实相当于将节点排成一个有序数组,从开始到结束,以中间某个节点为根,将树抻成两半。本质是卡特兰数。
C(2n)^n/n+1。

@checked
public int numTrees(int n) {
    int[] arr = new int[n+1];
    arr[0] = 1;//0个数时记为1种
    for(int i = 1; i <= n; i++){//n个数
        for(int j = 1; j <= i; j++){//以j为根
            arr[i] += arr[j-1] * arr[i-j];//左边有j-1个数,右边有i-j个数
        }
    }
    return arr[n];
}

打印树的结构

https://leetcode.com/problems/unique-binary-search-trees-ii/
将上面的各种结构的树打印出来
思路
求(1,n)那么就循环以i为根求左子树(1,i-1)和右子树(i+1,n)
直接DFS递归会有很多重复计算,慢。 运用基本的DP,存储(i,j)内的树结构的list,每一个以其为基础的上层树将其左子或右子指向其即可,省空间省时。
注,遇到一种想法:
使用动态规划存储重复记录,觉得保存任意范围(i,j)内的树结构太浪费空间,因此仅保存(1,r)范围内的树结构,其中r = 1..n
其他范围内的可以由(1,r)内的加偏移推导出来。
但是注意!看上去节省了空间,但是实际上每次由(1,r)推导出的过程需要克隆一份,这样实际并未减少空间,且还增加了时间复杂度

public HashMap<String, List<TreeNode>> hm;
public List<TreeNode> generateTrees(int n) {
    hm = new HashMap<String, List<TreeNode>>();
    return get(1,n);
}
public List<TreeNode> get(int start, int end){
    String hstring = start +" "+ end;
    List<TreeNode> ls = new ArrayList<TreeNode>();
    if(hm.containsKey(hstring)) ls = hm.get(hstring);
    else{
        if(start > end) ls.add(null);
        else{
            for(int i = start; i <= end; i++){
                List<TreeNode> leftlist = get(start, i-1);
                List<TreeNode> rightlist = get(i+1, end);
                for(int j = 0; j < leftlist.size(); j++){
                    for(int t = 0; t < rightlist.size(); t++){
                        TreeNode tn = new TreeNode(i);
                        tn.left = leftlist.get(j);
                        tn.right = rightlist.get(t);
                        ls.add(tn);
                    }
                }
            }
        }
        hm.put(hstring, ls);
    }
    return ls;
}

将一个有序整数数组变成平衡二叉查找树

Convert Sorted Array to Binary Search Tree
https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/
Given an array where elements are sorted in ascending order, convert it to a height balanced BST.
思路:类似二分查找,不断取数组中间元素作为根。是树的前序遍历变形。时间复杂度是O(n),空间复杂度是O(logn)

@checked
public TreeNode sortedArrayToBST(int[] num) {
      return convert(num, 0, num.length-1);
}
public TreeNode convert(int[] num, int start, int end){
    if(start > end) return null;
    int mid = (start + end)/2;
    TreeNode root = new TreeNode(num[mid]);
    root.left = convert(num, start, mid-1);
    root.right = convert(num, mid+1, end);
    return root;
}

将一个有序链表变成平衡二叉查找树

Convert Sorted List to Binary Search Tree
https://leetcode.com/problems/convert-sorted-list-to-binary-search-tree/
Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST.
思路:可以像上题一样,不断取中间元素,但数组中取中间元素时间复杂度为O(1),链表中取中间元素(利用快慢指针的方法)时间复杂度是O(n/2),因此总的时间复杂度就是n/2+n/4*2+n/8*4+…=n/2*logn = O(n logn),即总共有logn层,每层需要n的时间。
换一个思路,若按照链表顺序进行遍历构造树,应该是树的中序遍历,利用mid来控制结束层次。因此可以用O(n)时间构造树。
两种方法的空间复杂度都是O(logn)

ListNode list;
public TreeNode sortedListToBST(ListNode head) {
    list = head;
    int count = -1;
    for(ListNode p = head; p != null; count++, p = p.next);
    return ConstructBinaryTree(0, count);
}
public TreeNode ConstructBinaryTree(int start, int end){
    if(start > end) return null;
    int mid = (start + end)/2;
    TreeNode leftChild = ConstructBinaryTree(start, mid-1);
    TreeNode root = new TreeNode(list.val);
    root.left = leftChild;
    list = list.next;
    root.right = ConstructBinaryTree(mid+1, end);
    return root;
}

二叉查找树的两个元素被交换,不改变树的结构重塑

https://leetcode.com/problems/recover-binary-search-tree/

/**Hard * Two elements of a binary search tree (BST) are swapped by mistake. * Recover the tree without changing its structure. * Note: * A solution using O(n) space is pretty straight forward. * Could you devise a constant space solution? * 思路:进行中序遍历,用数组存储遍历值再比较空间复杂度为O(n) * 若一个一个比,只保留一个值,这样空间就可以是常数了。 * 用一个tmp节点记录每个节点前的节点值,如果树中有两个节点被错放了,那一定是会出现这样的状况: * 7 * / \ * 2 4 5 6 9 * \ / * 3 * 正常遍历时前一节点值都应该小于后一节点值。当错放后遍历会出现两次前一节点值大于后一节点值。 * 注意:若是相邻两个放错了,只能检测到一个波峰。 * 2 4 9 * \ / * 3 * 因此,定义两个游标指向两个异常值并交换,若只有一 * */
 public void recoverTree(TreeNode root){
        TreeNode first = null, sec = null, tmp = null;
        Deque<TreeNode> deque = new ArrayDeque<TreeNode>();
        for(TreeNode t = root; t != null; deque.add(t), t = t.left);
        int i = 0;
        while(!deque.isEmpty()){
            TreeNode cur = deque.pollLast();
            if(tmp != null && tmp.val > cur.val){
                if(i++ == 0) {
                    first = tmp;
                    sec = cur;
                }//这样做可以保证也满足相邻两个节点放错的情况
                else sec = cur;
            }
            tmp = cur;
            for(TreeNode t = tmp.right; t != null; deque.add(t), t = t.left);
        }
        first.val = first.val ^ sec.val;
        sec.val = first.val ^ sec.val;
        first.val = first.val ^ sec.val;
 }
    原文作者:二叉查找树
    原文地址: https://blog.csdn.net/Vanilla_Chi/article/details/45973213
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞