思考过程
需要将链表转成高度平衡的二叉搜索树,找到链表的中间节点,作为二叉树的根,前部分作为左节点,后部分作为右节点。然后递归,直到链表长度为1或者为空时,可以返回确定的二叉树
可以使用快慢双指针,找到链表的中间节点,后部分链表和前部分链表的长度
前部分链表可以从head开始构建链表(已知长度),然后再翻转
代码实现:
链表翻转:(LeetCode有原题)
从链表头head开始构建链表
代码如下:
private ListNode reverse(ListNode head) {
ListNode l1 = null;
ListNode curr = head;
while (curr != null) {
ListNode tem = new ListNode(curr.val);
tem.next = l1;
l1 = tem;
curr = curr.next;
}
return l1;
}
链表转二叉树,代码如下:
public TreeNode sortedListToBST(ListNode head) {
if (head == null) {
return null;
}
if (head.next == null) {
return new TreeNode(head.val);
}
ListNode tem = head;
ListNode right = head;
int len = 0, leftLen, rightPosition = 0;
while (tem != null) {
tem = tem.next;
len++;
if (tem != null) {
tem = tem.next;
len++;
}
right = right.next;
rightPosition++;
}
leftLen = len - 1 - (len-rightPosition);
tem = head;
int p = 0;
int value = -1;
ListNode left = null;
while (tem != null) {
if (p < leftLen) {
ListNode l = new ListNode(tem.val);
l.next = left;
left = l;
}
if (p == rightPosition - 1) {
value = tem.val;
}
tem = tem.next;
p++;
}
left = reverse(left);
return new TreeNode(value, sortedListToBST(left), sortedListToBST(right));
}
执行结果:
性能明显不够好。
算法复杂度分析
时间复杂度:O(n²),其中 n是链表的节点个数。
空间复杂度:O(n),其中 n 是链表的节点个数。
分治思想和我个人解题思路差不多,找中间节点,也同样使用的是快慢双指针思路,但是在代码实现上,效率更好,更简洁。
代码如下:
public TreeNode sortedListToBST(ListNode head) {
return buildTree(head, null);
}
public TreeNode buildTree(ListNode left, ListNode right) {
if (left == right) {
return null;
}
ListNode mid = getMedian(left, right);
TreeNode root = new TreeNode(mid.val);
root.left = buildTree(left, mid);
root.right = buildTree(mid.next, right);
return root;
}
public ListNode getMedian(ListNode left, ListNode right) {
ListNode fast = left;
ListNode slow = left;
while (fast != right && fast.next != right) {
fast = fast.next;
fast = fast.next;
slow = slow.next;
}
return slow;
}
其他解答:
public TreeNode sortedListToBST3(ListNode head) {
//边界条件的判断
if (head == null)
return null;
if (head.next == null)
return new TreeNode(head.val);
//这里通过快慢指针找到链表的中间结点slow,pre就是中间
//结点slow的前一个结点
ListNode slow = head, fast = head, pre = null;
while (fast != null && fast.next != null) {
pre = slow;
slow = slow.next;
fast = fast.next.next;
}
//链表断开为两部分,一部分是node的左子节点,一部分是node
//的右子节点
pre.next = null;
//node就是当前节点
TreeNode node = new TreeNode(slow.val);
//从head节点到pre节点是node左子树的节点
node.left = sortedListToBST3(head);
//从slow.next到链表的末尾是node的右子树的结点
node.right = sortedListToBST3(slow.next);
return node;
}
思路是一致的,找前链表的方式比较高效,代码比较简洁,值得借鉴。
总结:
读完题后,马上想到解题思路,但是在代码实现时卡住了(如何找到前部分链表),对链表的操作还是不太熟练。另外,算法复杂度分析时,也卡住了。所以在链表操作,代码实现,算法复杂度分析上,还有待提高