二叉搜索树与双向链表

面试知乎,让我把二叉树做中序遍历,改成链表。
我给出了一种写法,但复杂度较高,面试官不满意。
回来搜索了一下,发现竟是《剑指Offer》中的第27题,但我一点印象都没有了。
其实思路很好理解,关键是怎么实现。书中的代码是C++,有指针的指针,不好理解。网上很多Java版的代码都是书中C++的简单替换,结果都是错的。

static class LastNode {
    BinaryTreeNode node;
}

public BinaryTreeNode convert(BinaryTreeNode root, LastNode lastNode) {
    if (root == null)
        return null;
    if (root.left == null && root.right == null) {
        return lastNode.node = root;
    }

    BinaryTreeNode leftNode = convert(root.left, lastNode);
    if (lastNode.node != null) {
        lastNode.node.right = root;
        root.left = lastNode.node;
    }
    lastNode.node = root;
    BinaryTreeNode rightNode = convert(root.right, lastNode);
    if (rightNode != null) {
        root.right = rightNode;
        rightNode.left = root;
    }
    return leftNode != null ? leftNode : root;
}

LastNode 是一个全局变量,因为上层递归要用到下层的结果。这里不能使用BinaryTreeNode,因为参数传递是值传递。
值得一提的是,还可以在类中定义一个成员变量,这样就不需要引用了。
2019-03-19阅:
当时写的博客太简单了,题目都没有介绍。最要紧的是缺少关键注释。

BinaryTreeNode tail;

BinaryTreeNode convert(BinaryTreeNode root) {
    if (root == null) {
        return null;
    }
    BinaryTreeNode leftNode = convert(root.left);
    if (tail != null) {
        tail.right = root;
    }
    root.left = tail;
    tail = root; // tail可能是root,因为右子树是空
    BinaryTreeNode rightNode = convert(root.right);
    if (rightNode != null) {
        rightNode.left = root;
    }
    root.right = rightNode;
    return leftNode != null ? leftNode : root;
}

这种思路是我临场所想,就是把二叉树分成三个部分:左子树、根和右子树。把左右子树都变成链表,然后再和根节点连接起来,相当于后续遍历。当时遇到的问题是方法只能返回一个数值,但是我需要的是左子树的最后一个节点和右子树的第一个节点,所以必须还得有一个全局变量。
在递归中改变全局变量是非常危险的,因为下层的调用可能改变上层调用的值。tail指向某棵子树的最后一个节点,可能指向root,也可能指向右子树的最后一个节点。
又发现了其他的一个思路:

    BinaryTreeNode tail;
    BinaryTreeNode head;
    public void convert2(BinaryTreeNode root) {
        if (root == null)
            return;
        convert2(root.left);
        if (tail == null) {
            head = root;
            tail = root;
        } else {
            tail.right = root;
            root.left = tail;
            tail = root;
        }
        convert2(root.right);
    }

这是一个中序遍历。
递归的本质是一棵解答树。二叉树的单元:单节点,二节点,三节点。如果这些树都没有问题,才能保证无误。
参考: 二叉搜索树与双向链表

2019-03-20阅:
这种需要返回多个值的题目,可以把多个返回值封装起来,似乎更优雅。

class Result {
    BinaryTreeNode head;
    BinaryTreeNode tail;

    public Result() {
    }

    public Result(BinaryTreeNode head, BinaryTreeNode tail) {
        this.head = head;
        this.tail = tail;
    }
}

public class Script {

    public Result convert(BinaryTreeNode root) {
        if (root == null) {
            return new Result();
        }
        Result leftTree = convert(root.left);
        if (leftTree.tail != null) {
            leftTree.tail.right = root;
        }
        root.left = leftTree.tail;
        Result rightTree = convert(root.right);
        if (rightTree.head != null) {
            rightTree.head.left = root;
        }
        root.right = rightTree.head;
        BinaryTreeNode head = leftTree.head == null ? root : leftTree.head;
        BinaryTreeNode tail = rightTree.tail == null ? root : rightTree.tail;
        return new Result(head, tail);
    }

    void print(BinaryTreeNode root) {
        if (root != null) {
            print(root.left);
            System.out.println(root.value);
            print(root.right);
        }
    }


    public static void main(String[] args) {
        BinaryTreeNode node4 = new BinaryTreeNode(4);
        BinaryTreeNode node3 = new BinaryTreeNode(3, null, node4);
        BinaryTreeNode node1 = new BinaryTreeNode(1);
        BinaryTreeNode node2 = new BinaryTreeNode(2, node1, node3);

        BinaryTreeNode node8 = new BinaryTreeNode(8);
        BinaryTreeNode node9 = new BinaryTreeNode(9, node8, null);
        BinaryTreeNode node6 = new BinaryTreeNode(6);
        BinaryTreeNode node7 = new BinaryTreeNode(7, node6, node9);

        BinaryTreeNode root = new BinaryTreeNode(5, node2, node7);

        Script script = new Script();
        script.print(root);

        Result result = script.convert(root);

        BinaryTreeNode head = result.head;

        while (head != null) {
            System.out.print(head.value + "\t");
            head = head.right;
        }
        System.out.println();
        BinaryTreeNode tail = result.tail;
        while (tail != null) {
            System.out.print(tail.value + "\t");
            tail = tail.left;
        }
    }
}

后序遍历

2020-02-23

public Result convert(BinaryTreeNode root) {
    if (root == null) {
        return new Result();
    }
    Result r1 = convert(root.left);
    Result r2 = convert(root.right);
    BinaryTreeNode head = root, tail = root;
    if (r1.head != null) {
        head = r1.head;
        r1.tail.right = root;
        root.left = r1.tail;
    }
// 当时没有考虑 r1.head == null的时候,是否需要root.left = r1.tail(并不需要),惊险运行成功
    if (r2.tail != null) {
        tail = r2.tail;
        r2.head.left = root;
        root.right = r2.head;
    }

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

推荐阅读更多精彩内容