给定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;
}