五分钟玩转面试考点-数据结构-二叉搜索树(递归萌芽2-调用者模式)

引子:五分钟玩转面试考点-数据结构系列,不会像那种严肃、古板的教科书般的博客文章,而是将晦涩难懂的概念和知识点尽可能幽默的细说出来,或结合生活场景,或从零开始分析。带给大家一个严肃而不失风趣的数据结构。


咱们就从一段话来开始今天的学习吧:

人事小R:小左同学,听说你搬家了?
小左同学:是的,因为我的孩子出生了...balabala
人事小R:停停...我不想听这些,你把最新地址给我一下就好了。
小左同学:知道了。(内心:mmp)


上面这段话什么意思呀?不要着急,咱们开始今天的学习,二叉搜索树。

首先,我要给大家推荐一款神器,(PS:小胖大学没学好数据结构,就是因为没遇到它,哼~) 搬运工小胖——图形化数据结构工具

1. 什么叫做二叉搜索树

二叉搜索树(BST,Binary Search Tree)也称为二叉排序树或者二叉查找树

二叉搜索树:一棵二叉树,可以为null,但是不为null的情况下,需要满足:

  1. 非空左子树的所有键值小于根节点的键值;
  2. 非空右子树的所有键值大于根节点的键值;
  3. 左、右子树都是二叉搜索树

[小胖友情提醒:二叉搜索树的中序遍历为有序的(升序)]

如下图:

二叉搜索树.png


2. 二叉搜索树常用算法

这里先说一下,小胖遇到的坑:小胖不想写递归代码,无非就是两个原因:

  1. 返回结果:在我查找到结果之后,我不知道,要怎么结束递归,或者说,怎么把正确的值返回给用户。
  2. 参数条件:我也不是很清楚如何将 参数值 精确的传递到第n个方法中。

小胖冒着挨喷的风险,小心翼翼的把自己的总结贴出来(也就是针对上面这个问题的解决方案):

  • 递归返回值:也不是无根之水递归出口,就是在极端情况下的返回值,我们需要做的,就是让他在某一处返回我们需要的值,然后被上一个方法处理,或者直接返回给上上方法。而对于作为调用者来说,我并不关注你们底层的逻辑,我调用方法,我就想获取到最终结果
  • 递归参数:这里对说一次值传递引用传递。也有大神说都是值传递,这里咱们都不讨论了。Integer到底是值传递还是引用传递?小胖遇到的坑就是这个,开始方法的参数是int类型,但是递归调用的时候,会修改这个值,于是我就想将int转换为Integer类型。这样我就可以在后续的递归中每次都获取处理后的最新对象(美滋滋)。但是事实是残酷的,方法参数传递的Integer类型的参数,经过方法内处理之后,方法外没有变化!

2.1 二叉排序树的插入

二叉排序树的插入,我们是在根节点出发,若是比根节点小,左子树结构发生变化。若是比根节点大,那么右子树结构发生变化。
我(Root节点)并不关心,你发生了什么变化,只需将最新的地址赋予我就可以了。

    /**
     * 二叉排序树   插入算法
     * root:我比左子树最右的字段都要大,比右子树最左的字段都要大
     * key:我比你小,我要去你左边。
     * root:我知道你们要做出修改,我不关心你到底修改了啥。但是,你把最新地址给我下吧。
     * left:好哒
     * right:好哒
     *
     * @param root
     * @param val
     * @return
     */
    public TreeNode insertIntoBST(TreeNode root, int val) {
        //极致情况
        if (root == null) {
            return new TreeNode(val);
        }
        //开始逻辑,想要插入到那个节点里面
        //左子树(执行方法的后果:结构将被修改,但是我并不关心,我只要后续节点给我最新的地址。)
        if (val < root.val) {
            root.left = insertIntoBST(root.left, val);
        } else {
            root.right = insertIntoBST(root.right, val);
        }
        return root;
    }

2.2 二叉排序树的查找

给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素。
说明:
你可以假设 k 总是有效的,1 ≤ k ≤ 二叉搜索树元素个数。

这道题咱们应该怎么分析,查找第K个最小的元素。中序遍历 返回的是有序的数据集合。嗯,就用它 (我们的英雄小哪吒)。
什么,不是太了解 二叉树的遍历?那你必须来这了解一波了。名侦探小胖——花式遍历二叉树

总结来说,即使人之路径,根之输出。中序遍历是第二次获取根对象。去操作根对象。这里咱们采用的是进栈的时候第一次操作对象,出栈的时候第二次操作对象,在出栈的时候,判断是否到达次数。

 //搜索第k小的元素
    //你可以假设 k 总是有效的,1 ≤ k ≤ 二叉搜索树元素个数。
    public int kthSmallest(TreeNode root, int k) {
        //中序遍历有序的,倒数第K个元素,即第几次完成中序遍历(栈)
        //入栈是第一次操作元素,将数据入栈
        Stack<TreeNode> stack = new Stack<>();
        int data = -1;
        while (root != null || !stack.isEmpty()) {
            while (root != null) {
                stack.push(root);
                //第一次操作对象
                root = root.left;
            }

            //若是栈中有数据
            if (!stack.empty()) {
                root = stack.pop();
                //第二次操作对象
                k--;
                if (k == 0) {
                    data = root.val;
                    break;
                }
                root = root.right;  //取出最左元素
            }
        }
        return data;
    }

2.3 二叉排序树的删除

二叉树删除比较复杂,这里咱们需要考虑几种情况:

  1. 没有节点时,直接将该节点删除;
  2. 1个子树的情况下,删除节点就是,该节点的父节点指向孙子节点
  3. 2个子树的情况下:
  • 先将左子树的最右节点,或者右子树的最左节点仅此于根节点大小的节点)替换根节点本质是:删除根节点)。
  • 然后,删除左子树的最右节点(即:替换根节点的节点。(我们需要注意的是:一个节点若是最右节点,那么他一定没有右子树)。可以将其规划为情况1或者情况2进行删除;

咱们就分析下,如何递归写出来吧

  1. 首先要处理极端情况下的返回值,即root==null时;
  2. 作为调用者的我们,并不关心结构到底如何修改,要是我的左子树发生改变。那么只需将变化后的位置给我就可以了(各扫门前雪)。右子树同上。
  3. 要是我(Root节点)要被删除,那么我就需要考虑,若是我的左子树为空那么我将右子树返回,或者右子树为空,我将左子树返回。返回的是新的地址我直接返回给调用者新的数据结构地址就可以了。可以想下,2个节点,删除父节点,返回的结果,就是左子树或者右子树地址。一个节点,删除该节点,直接返回null
  4. 要是删除节点含有左右子树。我们需要执行下面几步:
    4.1 保存父节点信息; TreeNode tempMode = root;
    4.2 定位到父节点左子树; root = root.left;
    4.3 定位左子树的最右节点; while (root.right != null)
    4.4 最右节点替换父节点;tempMode.val = root.val;
    4.5 删除最右节点,本质上就是根节点(tempMode)左子树(左子树结构发生变化)删除节点值为key的数据。tempMode.left = deleteNode(tempMode.left, root.val);继续递归。
    4.6 将临时根节点(tempMode)信息保存到root节点中。
public static TreeNode deleteNode(TreeNode root, int key) {
        if (root == null) {
            return null;
        }
        //操作左子树
        if (key < root.val) {
            root.left = deleteNode(root.left, key);
        } else if (key > root.val) {
            root.right = deleteNode(root.right, key);
        } else {
            //找到的情况,第1,2种情况
            if (root.left == null) {
                root = root.right;
            } else if (root.right == null) {
                root = root.left;
            } else {
                //1. 保存将要删除的根节点坐标
                TreeNode tempMode = root;
               //2. 我们的操作本质是在左子树上找到最右元素
               //2.1  定位左子树
                root = root.left;   
               //2.1  定位左子树最右元素
                while (root.right != null) {
                    root = root.right;
                }
              //3. 元素替换(删除根元素)
                tempMode.val = root.val;  
                //4. 删除替换根元素的节点
                //问题?root tempNode的左子树中,(一棵树中,删除节点key)
                tempMode.left = deleteNode(tempMode.left, root.val);
                root = tempMode;
            }
        }
        return root;
    }

递归方法到底怎么书写:比较难捉摸的就是返回值。首先在我写这个方法之前,已经这个方法本质已经存在了,且调用方法一定会返回给我一个(我想要)结果,我无需关注其内部的逻辑处理。我只需关注本次方法如何处理。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容