【算法之LeetCode系列(6)】—— 二叉搜索树(BST)

LeetCode之二叉搜索树系列

昨天我的二叉树系列突然上榜了(《数据结构与算法领域内容榜》),最高18名,很意外,感谢官方大大 #CSDN 的支持。

计划着按照la哥的建议,先干二叉树,所以前面做了一下简单的二叉树,捡回了一些知识,直观感受就是做这类题至少有了一个大致的思路,普通的二叉树、平衡二叉树了解了一些,今天来干二叉搜索树。

二叉搜索树( BST )是神马东东?

我的理解就是,一颗二叉树的中序遍历结果是一个递增序列,这就是二叉搜索树

三道题
1. 有序数组转高度平衡的 BST
2. 有序链表转高度平衡的 BST
3. BST 中的搜索

题目描述移步力扣官网哈

1. 有序数组转高度平衡的 BST

给一个严格递增的数组(符合我上面说的,中序遍历后是一个递增序列,现在就是把这个序列给变回去,变成BST),我当时的第一想法是,既然要满足二叉搜索树的定义,那么数组的最中间的元素一定是根节点(或者与中间元素的距离等于1),因为还要满足高度平衡,高度差不能超过1,这样根节点有了。
左子树和右子树怎么办?
左右子树不还是一样,一样要找根节点,只要是根节点,那就和上面的找法没区别,所以相当于是数组不断的被分隔成两部分,这不就是递归该出场了吗?(分治也可以)

var sortedArrayToBST = function(nums) {
    function insertNode(nums,left,right){
    	//左边大于右边,说明已经没有后续元素需要插入了
        if(left > right){
            return null;
        }
        //取中间位置的做法还有 mid = (left + right) >>> 1;
        let mid = Math.floor((left + right) / 2);
        //如上所说,只要是根节点,就以中间元素作为它的值
        let root = new TreeNode(nums[mid]);
        //划分数组左区间构建左子树
        root.left = insertNode(nums,left,mid - 1);
        //划分数组右区间构建右子树
        root.right = insertNode(nums,mid + 1,right);
        return root;
    }
    return insertNode(nums,0,nums.length - 1);
};
2. 有序链表转高度平衡的 BST

我相信看到这个题,大家一定和我一样聪明(我不是)想着直接把有序链表转成有序数组,就神奇的变成上一个题了,但是呢,这确实有点投机取巧,所以当然还有其他办法呀
提前剧透:方法二:快慢指针,方法三:中序遍历
(我怎么可能想出来呢,当然是各位大佬的意思,我只是来说说我对这两种方法的理解的)
方法二三来自
作者:xiao_ben_zhu
链接:https://leetcode-cn.com/problems/convert-sorted-list-to-binary-search-tree/solution/shou-hua-tu-jie-san-chong-jie-fa-jie-

//    方法一(转数组,就不解释了)
var sortedListToBST = function(head) {

	let nums= [];
    while(null !== head){
        nums.push(head.val);
        head = head.next;
    }
    let insertNode = (nums,low,high)=>{
        if(low > high)  return null;
        let mid = Math.floor((low + high) / 2);
        let root = new TreeNode(nums[mid]);
        root.left = insertNode(nums,low,mid - 1);
        root.right = insertNode(nums,mid + 1,high);
        return root;
    }
    return insertNode(nums,0,nums.length - 1)
};
// 方法二(快慢指针)
var sortedListToBST = function(head) {

	//如果是空树或者当前节点是叶子节点,返回null
    if (head == null) return null;
    //维护快慢指针,初始值是head
    let slow = head;
    let fast = head;
    // 保存slow的前一个节点,这个慢指针前驱很关键
    //作用就相当于是把链表切成左右两部分的刀,每次都是它切,每次slow找到中间节点,它就得切一次,保证区间再次化小,递归构建子树
    let preSlow;
	//快慢指针真的妙。你看啊,同样的距离,每次我走两步,你走一步,那等我走完,你是不是刚好在中间呢,妙!
    while (fast && fast.next) {//fast走到尾部就是找到中间节点的时刻
        preSlow = slow;        // 保存当前slow
        slow = slow.next;      // slow走一步
        fast = fast.next.next; // fast走两步
    }
    const root = new TreeNode(slow.val);     // 根据slow指向的节点值(就是链表的中间节点),构建节点
	
    if (preSlow != null) {   // 如果preSlow有值,即slow左边有节点,需要构建左子树
        preSlow.next = null;   // 切断preSlow和中点slow
        //这个preslow就相当于是切完了,然后再重新构建一个二叉搜索树,只不过呢,这棵树是刚刚根节点的左子树
        root.left = sortedListToBST(head);     // 然后递归构建左子树(就是重复构建后面每个节点的左子树)
    }
    root.right = sortedListToBST(slow.next); // 递归构建右子树,这个就是右半部分,用来重复构建每个节点的右子树
    return root;
};
//    方法三(中序遍历)

不需要找中间节点,遇到哪个节点,就把哪个节点当做根节点,就是一直构建一个只有三个节点的二叉树,然后,构建完一颗就连上下一个合适的根节点

var sortedListToBST = function(head) {
	if(head === null)return null;
    let h = head;//构建节点的关键指针
    let len = 0;
    while(head){
        len ++;
        head = head.next;
    }
    const buildBST = (start, end)=>{
        if(start > end) return null;//当不能再二分,就说明这是最底部的空节点
        let mid = (start + end) >>> 1;//只是单纯的二分一下,用于分别构建左右子树
        let left = buildBST(start,mid - 1);//按照中序遍历的顺序,先构建左子树
        let root = new TreeNode(h.val);//创建根节点
        h = h.next;//不断后移,为每棵小树创建节点
        root.left = left;//连上左子树
        root.right = buildBST(mid + 1,end);//连上右子树
        return root;
    }
    return buildBST(0,len - 1);
};
3. BST 中的搜索

这就是在一个有序序列里找数字的游戏,很简单,假如是递增序列,每次比较中间元素mid,比mid大的丢到后半区间找,比mid小的丢到前半区间找,思路应该很清晰

var searchBST = function(root, val) {
    while(root !== null){//只要当前进入的根节点不是空的,就说明我还有可能!!!
        if(root.val === val){
            return root;
        }else if(root.val > val){//目标值比根节点小,丢到左区间
            return searchBST(root.left, val);
        }else if(root.val < val){//目标值比根节点大,丢到右区间
            return searchBST(root.right, val);
        }
    }
    return null
};

本来还想再写两个个题的,但是能力有限,我自己都没明白呢,就不瞎写了,下次见(下次直接加在这里面,所以建议收藏本文哦)!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

summer·

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

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

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

打赏作者

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

抵扣说明:

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

余额充值