二叉树笔试题(二)

1、采用前序遍历的方式,将一个二叉树转换成一个由括号和整数组成的字符串。空节点则用一对空括号 "()" 表示。省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对。
示例:
在这里插入图片描述
在这里插入图片描述
思路:
如果根节点不为空的话,在字符串后边先打印一个左括号,然后再打印根节点的值。
如果左子树为空,而右子树不为空,像示例2的那样,就必须打印一对括号出来,不可以省略(如果左右子树都为空,可以省略括号)。
其余情况就递归遍历根节点的左子树,然后再递归遍历根节点的右子树,最后在字符串的后面加上右括号。
用上面的示例1具体来说就是:

  • 先打印一个左括号,然后打印根节点1,判断它的左子树不为空,所以接下来要递归遍历1的左子树,至此,已打印的字符串为(1
  • 以2为根节点,先打印一个左括号,然后打印2,因为2的左子树也不为空,所以再递归遍历2的左子树,已打印的字符串为(1(2
  • 以4为根节点,先打印一个左括号,再打印4,由于4的左右子树都为空,直接在字符串后边加上右括号,已打印的字符串为(1(2(4)
  • 现在2的左子树已经遍历完了,接着遍历它的右子树,为空,所以打印一个右括号,已打印的字符串为(1(2(4))
  • 1的左子树也遍历完了,现在去遍历1的右子树,以3为根节点,先打印一个左括号,再打印3,然后去遍历3的左右子树,都为空,所以打印一个右括号,已打印的字符串为(1(2(4))(3)
  • 1的左右子树都遍历完了,在字符串最后加上一个右括号,整棵二叉树遍历完毕,打印出的字符串为(1(2(4))(3))
    但是打印出来的结果与题目不符,题目要求的是没有最外面这层括号的,很简单,我们可以在打印完所有字符串之后再删掉最外面这层括号就行。
    具体代码如下:
private static void preOrderTree2Str(TreeNode root, StringBuilder sb) {
        if (root != null) {
            sb.append('(');
            sb.append(root.val);
            if (root.left == null && root.right != null) {
                sb.append("()");
            } else {
                preOrderTree2Str(root.left, sb);
            }
            preOrderTree2Str(root.right, sb);
            sb.append(')');
        }
    }

    public static String tree2str(TreeNode root) {
        if (root == null) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        preOrderTree2Str(root, sb);
        sb.delete(0, 1);
        sb.delete(sb.length() - 1, sb.length());
        return sb.toString();
    }

2、给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
示例:
在这里插入图片描述
思路:
首先判断要比较的两个节点是否有一个是这棵树的根节点,如果其中一个是根节点,那么最近的公共祖先就是根节点。
其次再去查找这两个节点到底在左子树还是右子树,如果都在左子树,就递归遍历左子树,查找最近公共祖先,如果都不在左子树,就去递归遍历右子树查找最近公共祖先,如果一个在左子树一个在右子树,那么最近公共祖先就是根节点。
直接看代码:

private boolean find(TreeNode root, TreeNode t) {
        if (root == null) {
            return false;
        }

        if (root == t) {
            return true;
        }

        if (find(root.left, t)) {
            return true;
        }

        return find(root.right, t);
    }

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == p || root == q) {
            return root;
        }
        boolean pInleft = find(root.left, p);
        boolean qInleft = find(root.left, q);
        if (pInleft && qInleft) {
            return lowestCommonAncestor(root.left, p, q);
        }

        if (!pInleft && !qInleft) {
            return lowestCommonAncestor(root.right, p, q);
        }

        return root;
    }

3、如何实现层序遍历
思路:
首先创建一个队列,把根节点尾插到队列中。只要这个队列不为空,先出掉队列中的第一个节点,打印它的值,如果出掉的这个节点的左孩子不为空,就把左孩子尾插到队列中,如果右孩子也不为空,也尾插到队列中,一直循环,直到队列为空。
图解过程:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
至此,队列为空,层序遍历结束。
代码如下:

public void levelOrder(TreeNode root) {
        if (root == null) {
            return;
        }

        LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
        //启动
        queue.addLast(root);

        //拉下线整个过程
        while (!queue.isEmpty()) {
            TreeNode front = queue.pollFirst();
            System.out.print(front);

            //拉下线,有要求,空的不要
            if (front.left != null) {
                queue.addLast(front.left);
            }
            if (front.right != null) {
                queue.addLast(front.right);
            }
        }
    }

4、给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。
思路:
这是一道层序遍历的变型题,本质还是层序遍历。与层序遍历不同的是,这次不仅需要返回一个节点,还要返回节点所在的层数。也就是说,又要返回两个值,在Java中返回两个值需要定义一个类,把要返回的这两个值放到同一个类中,然后返回这个类。
具体操作是,新建一个队列,先把根节点尾插到队列中,层数为0,只要队列不为空,先出掉队列中的第一个元素,出掉的这个元素包含两部分内容,一个是节点,一个是节点所在的层数。
如果出掉的节点的左孩子不为空,就将它的左孩子入队列,当前层数加1,右孩子不为空的话也入队列,当前层数加1。
上面的是层序遍历的过程,接下来要处理返回值。如果根节点为空,直接返回空的list。如果当前list的大小等于层数,说明list里一个元素都没有,就像下面这样:
在这里插入图片描述
我们需要做的,就是在list里边新建一个arrayList,用来保存每一层的元素。
在这里插入图片描述
然后将当前这一层的节点的值放入arrayList中,也就是将第0层的A放入arrayList中。
在这里插入图片描述
现在list的大小为1,而B的层数也为1,所以新建一个arrayList,将第1层的B放入新的arrayList中。
在这里插入图片描述
list大小变为2,而C的层数为1,所以直接将C放入刚刚的arrayList中。
在这里插入图片描述
DEF与上述操作一样,最后返回list。直接看代码:

public List<List<Integer>> levelOrder2(TreeNode root) {
        List<List<Integer>> list = new ArrayList<>();
        if (root == null) {
            return list;
        }

        class NodeLevel {
            TreeNode node;
            int level;

            public NodeLevel(TreeNode node, int level) {
                this.node = node;
                this.level = level;
            }
        }

        LinkedList<NodeLevel> queue = new LinkedList<NodeLevel>();
        queue.addLast(new NodeLevel(root, 0));

        while (!queue.isEmpty()) {
            NodeLevel front = queue.pollFirst();
            TreeNode node = front.node;
            int level = front.level;
            //中间的遍历
            if (list.size() == level) {
                list.add(new ArrayList<Integer>());
            }
            list.get(level).add(node.val);

            if (node.left != null) {
                queue.addLast(new NodeLevel(node.left, level + 1));
            }
            if (node.right != null) {
                queue.addLast(new NodeLevel(node.right, level + 1));
            }
        }
        return list;
    }

5、判断一棵树是否是完全二叉树
思路:
这道题也是利用层序遍历的思想,带着空节点去层序遍历。如果遇到空之后所有的节点都是空,那么它就是一棵完全二叉树。而如果遇到空之后,又遇到非空,那么它就不是完全二叉树。
具体操作是,首先判断根节点是否为空,如果为空,说明这是一棵空树,空树就是一棵完全二叉树。否则的话创建一个队列,将根节点入队列。
接下来从队列里出掉第一个元素,然后将这个元素的左右孩子都入队列,一直重复这个操作。如果遇到空,就去判断队列中剩余节点是否全为空。
在这里插入图片描述
遇到空节点而队列不为空的时候,出掉队列中的第一个节点,判断是否为空,如果不为空,那么这就不是一棵完全二叉树,反之就是完全二叉树。
在这里插入图片描述
具体代码如下:

private static boolean isComplete(TreeNode root){
        if (root==null){
            return true;
        }

        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.addLast(root);

        while (true){
            TreeNode front = queue.pollFirst();
            if (front==null){
                //遇到空,下一步去判断队列中剩余的节点,是否全是空
                break;
            }
            queue.addLast(front.left);
            queue.addLast(front.right);
        }

        //判断剩下所有节点是否是非空
        while (!queue.isEmpty()){
            TreeNode front = queue.pollFirst();
            //意味着空遇到非空
            if (front!=null){
                return false;
            }
        }
        return true;
    }

6、输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
思路:

搜索树中只有中序遍历是有序的。

具体操作是采用递归的方法,中序遍历这棵树,每拿到一个节点就串到双向链表上。

需要考虑两点:
1)每次拿到一个节点,如何将节点串成双向链表
2)如何有序的拿到一个节点(对于搜索树)
第二个问题很好解决,中序遍历这棵树,拿到的节点就是有序的。
第一个问题就和之前我们创建双向链表的方法一样,不过是将前驱节点prev改成树的left节点,next节点改成树的right节点。

用下面这棵树来详细说明
在这里插入图片描述
首先进行中序遍历,先遍历左子树,然后根节点,最后遍历右子树。
递归到A节点,把A串到链表中。A的left引用指向prev,也就是null。然后将head和prev引用都指向A节点。
在这里插入图片描述
再根据中序遍历拿到B节点,B的left引用指向prev,也就是A,因为prev引用不为空,所以将prev的right引用指向B,prev也指向B。
在这里插入图片描述
然后拿到C节点,C的left指向prev,也就是B,prev的right指向C,prev也指向C。
在这里插入图片描述
以此类推,最后返回head节点,就可以拿到已经转换好的双向链表。
在这里插入图片描述
具体代码如下:

private static TreeNode prev = null;
    private static TreeNode head = null;
    private static void buildDList(TreeNode node){
        node.left = prev;
        if (prev!=null){
            prev.right = node;head
        }else {
            head = node;
        }
        prev = node;
    }
    private static void inOrderTraversalSearchTree(TreeNode root){
        if (root!=null){
            inOrderTraversalSearchTree(root.left);
            buildDList(root);
            inOrderTraversalSearchTree(root.right);
        }
    }
    public TreeNode Convert(TreeNode pRootOfTree) {
        prev = null;
        head = null;
        inOrderTraversalSearchTree(pRootOfTree);
        return head;
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 把一个链表反向,递归,非递归都写一遍。 1.试编写3个函数实现   (1)建立一个双向链表   (2)插入一个节点   (3)删除一个节点 2.自己定义数据结构,写出程序:二叉树的前序遍历。 3.实现双向链表删除一个节点P,在节点P后插入一个节点,写出这两个函数。 4.下面哪种排序法对12354最快 a quick sort b.buble sort c.merge sort 5.哪种结构,平均来讲,获取一个值最快 a. binary tree b. hash table c. stack 6.一个二叉树的三种遍历方法的输出结果 7.链表按升序打印每打印完一个节点就将该节点从链表删除 8.选择一种算法来整理出一个链接表。你为什么要选择这种方法?现在用o(n)时间来做。 9. 用一种算法在一个循环的链接表里插入一个节点,但不得穿越链接表。    10.给两个变量,如何找出一个带环单链表是什么地方出现环的? 11.哈希表和数组的定义,区别,优缺点。 12.链接表和数组之间的区别是什么? 任选一门语言,当场定义叉排序树数据结构,写出两个函数:初始化,删除一个节点,20分钟 13. 递归的折半查找算法[不限语言] 14. 解释一下什么是B+树,如何实现B+树的查找和插入.(用图示) 15.实现双向链表删除一个节点P,在节点P后插入一个节点,写出这两个函数。 13.排序方法比较 (intel) 排序方法 平均时间 最坏时间 辅助存储 直接插入排序 O(N2) O(N2) O(1) 起泡排序 O(N2) O(N2) O(1) 快速排序 O(Nlog2N) O(N2) O(Nlog2N) 简单选择排序 O(N2) O(N2) O(1) 堆排序 O(Nlog2N) O(Nlog2N) O(1) 归并排序 O(Nlog2N) O(Nlog2N) O(n) 基数排序 O(d(n+radix)) O(d(n+radix)) O(radix) 17.一个链表的操作,注意代码的健壮和安全性。要求: (1)增加一个元素; (2)获得头元素; (3)弹出头元素(获得值并删除)。 18.内排序算法 19.折半查找的复杂度,证明 20.sizeof()和strlen()的使用. 21.顺序存储结构的优点,散列法的思想是什么? 22.汉罗塔算法,不能递归... 23.一个链表的结点结构 struct Node { int data ; Node *next ; }; typedef struct Node Node ; (1)已知链表的头结点head,写一个函数把这个链表逆序 ( Intel) (2)已知两个链表head1 和head2 各自有序,请把它们合并成一个链表 依然有序。 (3)已知两个链表head1 和head2 各自有序,请把它们合并成一个链表 依然有序,这次要求用递归方法进行。 ( Autodesk) 24.编最优化Bubble(int *pIntArray,int L),要求:交换元素不能用临时变量,如果有序需要最优。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值