力扣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;
	}
};

(所有代码均已在力扣上运行无误)

经测试,该代码运行情况是(经过多次测试所得最短时间):

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JLU_LYM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值