原题地址: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.
给出一个有序的单链表(递增),构造出一个平衡二叉查找树。
解题思路
简单的动态规划,跟 Leetcode 108 Convert Sorted Array to Binary Search Tree 类似,不同的地方是,这里给出的单链表,不知道链表长度而且不能快速随机访问到元素节点。为此我们需要简单修改代码逻辑,我们遍历一次单链表,获取其节点个数n,然后使用head和n作为输入来构造二叉树,取n/2为中间节点,从head开始找到这个点p,构造根节点,然后使用p之前的n/2个(大概值,后面再精确确定)节点构造左子树,使用p之后的n/2个(大概值,后面再精确确定)节点构造右子树。
由于每次找到中间点的时候都需要从head开始遍历一半元素,且大概共需递归调用O(log(n))次,因此时间复杂度为O(nlog(n))。
算法描述
- 遍历单链表,获取链表长度n;
- 计算中间节点n/2,从head开始找到中间节点,构造根节点;
- 使用中间节点以左的n/2个节点,递归构造左子树;
- 使用中间节点以右的n/2个节点,递归构造右子树。
对于选取中间节点之后,左侧节点个数和右侧节点个数的精确值问题,我们不妨举几个实例来求得其通用的表达式,我们以[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]为例,按照递归调用的层级分析:
1. 第一层:链表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],n=10, n/2=5, 所以选择第5个元素5,这时候其左侧有5个数,右侧有4个数;5=10-5,即nLeft=n-n/2, 4=10-5-1,即nRight=n-n/2-1;
2. 第二层(左侧为例):链表[0, 1, 2, 3, 4],n=5,n/2=2,所以选择第2个元素2,这时候其左侧有2个数,右侧有2个数;2=5-2-1,即nLeft=nRight=n-n/2-1;
3. 第三层(左侧为例):链表[0, 1],n=2,n/2=1,所以选择第1个元素1,这时其左侧有1个数,右侧有0个数;1=2-1,即nLeft=n-n/2,0=2-1-1,即nRight=n-n/2-1;
4. 第四层(左侧):链表[0],n=1,n/2=0,所以选择第0个元素0,这时其左侧有0个数,右侧有0个数;0=1-1,即nLeft=nRight=n-n/2-1。
总结如上规律,当有n个数时,先选择第n/2个数作为根节点的值,然后其左侧元素的个数与n是否为奇数有关,nLeft = n & 1 ? n - n / 2 - 1 : n - n / 2 或者 nLeft = n - n / 2 - (n & 1);其右侧元素的个数恒等于 n - n / 2 - 1。
代码 c
/**
* 根据有序(递增)链表构造平衡二叉查找树
* input head : 链表首节点
* return : 平衡二叉查找树
*/
struct TreeNode* sortedListToBST(struct ListNode* head) {
struct TreeNode* sortedListSeqToBST(struct ListNode*, int);
if (head == NULL) return NULL;
/* 统计链表节点个数 */
int count = 0;
struct ListNode* p = head;
while (p) {
++count;
p = p->next;
}
return sortedListSeqToBST(head, count);
}
/**
* 根据有序(递增)链表片段构造平衡二叉树
* input head : 链表起始节点
* input count : 链表片段节点的个数
* return : 平衡二叉查找树
*/
struct TreeNode* sortedListSeqToBST(struct ListNode* head, int count) {
/* 如果链表片段中没有节点,直接返回NULL */
if (count <= 0) return NULL;
/* 找到当前要作为TreeNode根节点的值的ListNode */
int center = count / 2, i = 0;
struct ListNode* p = head;
while (i++ < center) p = p->next; // p最终指向的节点的值作为根节点的值
/* 创建根节点 */
struct TreeNode* node = (struct TreeNode*)malloc(sizeof(struct TreeNode));
node->val = p->val; // 记录根节点的值
/* 利用p之前的节点,构造左子树 */
node->left = sortedListSeqToBST(head, count - center - (count & 1));
/* 利用p之后的节点,构造右子树 */
node->right = sortedListSeqToBST(p->next, count - center - 1);
return node;
}
完整代码:包含判定结果是否为平衡二插查找数的方法
https://github.com/Orange1991/leetcode/blob/master/109/c/main.c
运行情况
Status:Accept
Time:8ms
代码 cpp
与c版本基本相同。
class Solution {
public:
/**
* 根据有序(递增)链表构造平衡二叉查找树
* input head : 链表首节点
* return : 平衡二叉查找树
*/
struct TreeNode* sortedListToBST(struct ListNode* head) {
if (head == NULL) return NULL;
/* 统计链表节点个数 */
int count = 0;
struct ListNode* p = head;
while (p) {
++count;
p = p->next;
}
return sortedListSeqToBST(head, count);
}
private:
/**
* 根据有序(递增)链表片段构造平衡二叉树
* input head : 链表起始节点
* input count : 链表片段节点的个数
* return : 平衡二叉查找树
*/
struct TreeNode* sortedListSeqToBST(struct ListNode* head, int count) {
/* 如果链表片段中没有节点,直接返回NULL */
if (count <= 0) return NULL;
/* 找到当前要作为TreeNode根节点的值的ListNode */
int center = count / 2, i = 0;
struct ListNode* p = head;
while (i++ < center) p = p->next; // p最终指向的节点的值作为根节点的值
/* 创建根节点 */
TreeNode* node = new TreeNode(p->val); // 记录根节点的值
/* 利用p之前的节点,构造左子树 */
node->left = sortedListSeqToBST(head, n & 1 ? count - center - 1 : count - center);
/* 利用p之后的节点,构造右子树 */
node->right = sortedListSeqToBST(p->next, count - center - 1);
return node;
}
};
完整代码,包含判定是否为平衡二插查找树的方法
https://github.com/Orange1991/leetcode/blob/master/109/cpp/main.cpp)
运行情况
Status:Accept
Time:28ms
// sfg1991@163.com
// 2015/6/5