1.题目
-
108题
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
-
109题
给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
2.解法
-
108题——递归
解题思路:
- 类似于二分法,数组两边边界下标,取中值mid,mid下标所在的数值及为二叉树的根节点。
- 取mid下标左段和右段数组段进行递归计算。
注意:
-
若 mid = (left + right) / 2 可能会造成数值越界(数组中出现 int 型的最大值最小值的情况),所以取mid = left + (right - left) / 2 会更为安全。
-
该题答案不唯一。当数组段长度为偶数时,mid = left + (right - left) / 2 默认取下界,取其上界作为root节点的值也是满足二叉搜索树要求的。
代码实现:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public TreeNode sortedArrayToBST(int[] nums) { return convertToBST(nums, 0, nums.length-1); } public TreeNode convertToBST(int[] nums, int left, int right){ if(left > right) return null; int mid = left + (right - left) / 2; TreeNode root = new TreeNode(nums[mid]); root.left = convertToBST(nums, left, mid-1); root.right = convertToBST(nums, mid+1,right); return root; } }
时间复杂度:O(N) ,每个元素只访问一次。
空间复杂度:O(log N) ,二叉搜索树空间 O(N) ,递归栈深度 O(log N)。
-
109题——快慢指针
解题思路:
- 定义一个快指针 fast 和一个慢指针 slow,慢指针一次走一步,快指针一次走两步,这样当快指针走到链表末尾,慢指针刚好在链表的中间位置,取slow所在链表的值为根节点的值。
- 以 slow 所在节点为分界点,链表起点到 slow 前一个节点为左段,slow 后一个节点到链表末尾为右段,左右两段再进行递归计算。
代码实现:
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ /** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public TreeNode sortedListToBST(ListNode head) { if(head == null) return null; if(head.next == null) return new TreeNode(head.val); ListNode slow = head; ListNode fast = head; ListNode pre = head; while(fast != null && fast.next != null){ fast = fast.next.next; slow = slow.next; } while(pre.next != slow){ pre = pre.next; } TreeNode root = new TreeNode(slow.val); ListNode headRight = slow.next; //对slow左段进行分割,即断开slow前一个节点pre对slow节点的引用 pre.next = null; root.left = sortedListToBST(head); root.right = sortedListToBST(headRight); return root; } }
时间复杂度:O(N log N) ,假设每个链表含有 N 个节点,要得到其中间节点,需要 N / 2 步;将链表分为两个子链表时,找到两个子链表的中间节点则需 2 *(N / 4)步,依次类推,每次需要的步数总和为:
N 2 + 2 ⋅ N 4 + 4 ⋅ N 8 + 8 ⋅ N 16 + . . . \frac{N}{2}+2\cdot\frac{N}{4}+4\cdot\frac{N}{8}+8\cdot\frac{N}{16}+... 2N+2⋅4N+4⋅8N+8⋅16N+...
而每次将列表分成一半,需要 log N 的时间结束,因此上面的式子可以写成:
∑ i = 1 l o g N 2 i − 1 ⋅ N 2 i = ∑ i = 1 l o g N N 2 = N 2 l o g N = O ( N l o g N ) \sum_{i=1}^{log N}{2^{i-1}\cdot}\frac{N}{2^i}=\sum_{i=1}^{log N}{\frac{N}{2}}=\frac{N}{2} logN=O(NlogN) i=1∑logN2i−1⋅2iN=i=1∑logN2N=2NlogN=O(NlogN)空间复杂度:O(log N)
-
109题——将链表转化为数组,再递归
解题思路:
- 将链表转化为数组,后递归实现二叉搜索树。
代码实现:
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ /** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public TreeNode sortedListToBST(ListNode head) { List<Integer> list = convertToArray(head); return listToBST(list, 0, list.size()-1); } public TreeNode listToBST(List<Integer> list, int left, int right){ if(left > right) return null; int mid = left + (right - left) / 2; TreeNode root = new TreeNode(list.get(mid)); root.left = listToBST(list, left, mid-1); root.right = listToBST(list, mid+1, right); return root; } public List<Integer> convertToArray(ListNode head){ List<Integer> list = new ArrayList<>(); while(head != null){ list.add(head.val); head = head.next; } return list; } }
时间复杂度:O(N) ,因为将链表转化为数组,取中间元素的开销变成了 O(1) ,整体时间复杂度下降。
空间复杂度:O(N) ,因为创建数组的开销,以空间换时间,相较于之前的空间复杂度 O(log N) 有所提升。