第五十八天 --- 力扣109. 有序链表转换二叉搜索树
题目要求:
算法正确性证明
为了构造出高度平衡BST树,我们采用分治法,先选择中间节点构造,然后再构造他的左右子树,这样可以保证是BST且高度平衡,正确性证明详见->正确性证明过程
整体思路一:链表变数组 数组加分治
最简单的思路,链表最大的缺点就是难以随机访问一个元素,只能按照某种特定的方法(看是咋构造出来的)遍历一遍,所以把链表变成数组,再用分治法即可。
具体代码
class Solution {
public:
int num = 0;//数组元素数
int arrays[105000];//目标数组
TreeNode* sortedListToBST(ListNode* head) {
ListNode* p = head;
while (p != nullptr) {
arrays[num++] = p->val;//变换过程
p = p->next;
}
return makeBST(0, num - 1);
}
TreeNode* makeBST(int left, int right) {//分治法
if (left > right) {
return nullptr;
}
int mid = (left + right) / 2;//先构造中点,再构造左右子树
TreeNode* root = new TreeNode(arrays[mid]);
root->left = makeBST(left, mid - 1);
root->right = makeBST(mid + 1, right);
return root;
}
};
(所有代码均已在力扣上运行无误)
经测试,该代码运行情况是(经过多次测试所得最短时间):
整体思路二:链表+分治
我们还是采用链表结构,思路和上面是一样的,但是还是需要注意两个事情。
具体代码
注:
1、快速求中位节点:快慢指针法。一个指针一次走两个节点,一个指针一次走一个节点。但是需要注意的是区间节点个数奇偶问题,具体如下图所示:
所以停止条件:fast != right && fast->next != right
2、还有就是左右子树区间问题,因为构造方式的原因,只能从左向右访问,反向不行,所以我们知道mid节点,不方便知道他的前序节点,所以在写范围的时候就包括Mid但是不取用它,即左闭右开
class Solution {
public:
TreeNode* sortedListToBST(ListNode* head) {
return makeBST(head, nullptr);//左闭右开初始化
}
TreeNode* makeBST(ListNode* left, ListNode* right) {
if (left == right) {
return nullptr;
}
ListNode* mid = getMidList(left, right);//分治法
TreeNode* root = new TreeNode(mid->val);
root->left = makeBST(left, mid);
root->right = makeBST(mid->next, right);
return root;
}
ListNode* getMidList(ListNode* left, ListNode* right) {//快慢指针寻找中位节点
ListNode* fast = left;
ListNode* low = left;
while (fast != right && fast->next != right) {//分奇偶个数
fast = fast->next;
fast = fast->next;
low = low->next;
}
return low;
}
};
(所有代码均已在力扣上运行无误)
经测试,该代码运行情况是(经过多次测试所得最短时间):
整体思路三:分治+中序遍历优化
1、上述方法在寻找中位节点的时候会浪费很多,所以我们先不着急寻找中位节点,我们先用空节点占位,等到了处理中间节点的时候,我们再把它放上去。
2、因为BST中序遍历得到的序列就是给的链表序列,所以任何一个节点都会在处理完他的左子树之后处理它,所以我们分治的同时再用上这个先占位后填东西思想就行了。
具体代码
注:
1、head在传参数的时候,必需给引用类型的,因为他需要每次改变head指向位置,是一个全局的,需要指向head真正存储的位置
class Solution {
public:
TreeNode* sortedListToBST(ListNode* head) {
int length = getListLength(head);
return makeBST(head, 0, length - 1);
}
private:
TreeNode* makeBST(ListNode*& head, int left, int right) {
if (left > right) {//左右指针是为了分割区间
return nullptr;
}
int mid = (left + right) / 2;
TreeNode* root = new TreeNode();//先占位
root->left = makeBST(head, left, mid - 1);//处理左二子
root->val = head->val;//按照BST中序性质,左面处理完了,就可以处理根节点了
head = head->next;
root->right = makeBST(head, mid + 1, right);
return root;
}
int getListLength(ListNode* head) {//获得长度,便于分治
int ans = 0;
while (head != nullptr) {
ans++;
head = head->next;
}
return ans;
}
};
(所有代码均已在力扣上运行无误)
经测试,该代码运行情况是(经过多次测试所得最短时间):