数据结构与算法练习-二叉树

这篇博客详细介绍了二叉树的各种遍历方法,包括深度优先遍历(先序、中序、后序)、广度优先遍历以及如何通过先序和中序遍历结果重建二叉树。同时,讲解了判断子结构、构造二叉树镜像和寻找特定路径的问题,提供了相应的代码分析和实现。
摘要由CSDN通过智能技术生成

深度优先遍历二叉树

描述

使用递归可以轻易的遍历一颗二叉树,根据访问顺序的不同分为

  • 先序遍历 中-左-右
  • 中序遍历 左-中-右
  • 后序遍历 左-右-中

代码

    /**
     * 深度优先 先序遍历
     */
    public static void scanPreTree(TreeNode root, ArrayList<Integer> list) {
        if (root == null) {
            return;
        }
        list.add(root.val);
        scanPreTree(root.left, list);
        scanPreTree(root.right, list);
    }

    /** 中序 */
    public static void scanInTree(TreeNode root, ArrayList<Integer> list) {
        if (root == null) {
            return;
        }
        scanInTree(root.left, list);
        list.add(root.val);
        scanInTree(root.right, list);
    }

    /** 后序 */
    public static void scanBackTree(TreeNode root, ArrayList<Integer> list) {
        if (root == null) {
            return;
        }
        scanBackTree(root.left, list);
        scanBackTree(root.right, list);
        list.add(root.val);
    }

广度优先遍历二叉树(层次遍历)

描述

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

分析

使用一个队列Queue,利用先进先出的特性来辅助遍历

代码

    /** 广度优先遍历 从上往下打印出二叉树的每个节点,同层节点从左至右打印。 */
    public static ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        if (root != null) {
            queue.offer(root);
        }
        while (queue != null && queue.size() != 0) {
            TreeNode node = queue.poll();
            list.add(node.val);
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
        }
        return list;
    }

求二叉树深度

描述

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

分析

求一个二叉树的深度可以分治为求他的左右最大(深)子二叉树加1

代码

构造二叉树结构

package com.example.bean;

public class TreeNode {

    public int val = 0;
    public TreeNode leftNode = null;
    public TreeNode rightNode = null;

    public TreeNode(int val) {
        super();
        this.val = val;
    }
}

查找

public class TreeHelper {

    public static int treeDeep(TreeNode node) {
        if (node == null) {
            return 0;
        }
        int leftDeep = treeDeep(node.leftNode);
        int rightDeep = treeDeep(node.rightNode);
        return leftDeep > rightDeep ? leftDeep + 1 : rightDeep + 1;
    }
}

测试

public class TestTree extends TestCase {

    TreeNode node, rightNode;

    private void init() {
        node = new TreeNode(1);
        TreeNode leftNode = new TreeNode(2);
        node.leftNode = leftNode;
        TreeNode lLeftNode = new TreeNode(3);
        node.leftNode.leftNode = lLeftNode;
        rightNode = new TreeNode(4);
        node.rightNode = rightNode;
    }

    public void testTreeDeep() throws Exception {
        init();
        assertEquals(TreeHelper.treeDeep(node), 3);
        assertEquals(TreeHelper.treeDeep(rightNode), 1);
    }
}

由先序遍历 中序遍历重建二叉树

描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

分析

  1. 知道先序遍历 可以求得二叉树的根节点
  2. 知道二叉树的根节点和中序遍历 可以求得根节点的左右子树的中序遍历
  3. 知道中序遍历的左右子树 可以知道左右子树的长度 从而求得先序遍历中左右子树的顺序
  4. 知道左右子树的先序遍历顺序 和 中序遍历顺序 重建左右子树。又转换成最开始的问题-由先序遍历 中序遍历重建二叉树 由此可知:这里可以递归实现

代码

    public static TreeNode reConstructBinaryTree(int[] pre, int[] in) {
        TreeNode rootNode = reConstructBinaryTree(pre, 0, pre.length - 1, in,
                0, in.length - 1);
        return rootNode;
    }

    private static TreeNode reConstructBinaryTree(int[] pre, int pStart,
            int pEnd, int[] in, int inStart, int inEnd) {
        if (pStart > pEnd || inStart > inEnd) {
            return null;
        }
        TreeNode node = new TreeNode(pre[pStart]);
        for (int i = inStart; i <= inEnd; i++) {
            if (pre[pStart] == in[i]) {
                node.left = reConstructBinaryTree(pre, pStart + 1, pStart + i
                        - inStart, in, inStart, i - 1);
                node.right = reConstructBinaryTree(pre, pStart + i - inStart
                        + 1, pEnd, in, i + 1, inEnd);
            }
        }
        return node;
    }

树的子结构

题目描述

输入两颗二叉树A,B,判断B是不是A的子结构

分析

1.首先需要递归pRoot1树,找到与pRoot2根一样的节点,这需要一个遍历
2.找到相同的根节点后,要判断是否子树,仍需要一个一个遍历对比
树的遍历我们一般就用递归来做,那么根据分析需要两个递归函数如下:

代码

public static boolean HasSubtree(TreeNode root1, TreeNode root2) {
        // 注意为空 不是子树
        if (root2 == null) {
            return false;
        }
        if (root1 == null && root2 != null) {
            return false;
        }
        boolean flag = isSubtree(root1, root2);
        if (!flag) {
            flag = HasSubtree(root1.left, root2);
            if (!flag) {
                flag = HasSubtree(root1.right, root2);
            }
        }
        return flag;
    }

    public static boolean isSubtree(TreeNode root1, TreeNode root2) {
        // 这里是判断路径上所有值都判断完了,不是判断子树
        if (root2 == null) {
            return true;
        }
        if (root1 == null && root2 != null) {
            return false;
        }
        if (root1.val == root2.val) {
            return isSubtree(root1.left, root2.left)
                    && isSubtree(root1.right, root2.right);
        }
        return false;
    }

测试

TreeNode node1, node2;

    @Override
    protected void setUp() throws Exception {
        node1 = new TreeNode(1);
        TreeNode s1 = new TreeNode(2);
        TreeNode s2 = new TreeNode(3);
        node1.left = s1;
        node1.right = s2;
        TreeNode ss1 = new TreeNode(3);
        s1.left = ss1;

        node2 = new TreeNode(2);
        node2.left = ss1;
        super.setUp();
    }

    public void testHasSubtree() {
        assertEquals(true, TreeSolution.HasSubtree(node1, node2));
        assertEquals(false, TreeSolution.HasSubtree(node1, null));
        node2.right = node1;
        assertEquals(false, TreeSolution.HasSubtree(node1, node2));
    }

二叉树的镜像

题目描述

操作给定的二叉树,将其变换为源二叉树的镜像。

二叉树的镜像定义:源二叉树
8
/ \
6 10
/ \ / \
5 7 9 11
镜像二叉树
8
/ \
10 6
/ \ / \
11 9 7 5

分析

每个节点的左右子结点互换,子节点的左右子节点互换,也就是子节点的问题和他的父节点是同一个问题,递归实现

代码

public static void Mirror(TreeNode root) {
        if (root == null) {
            return;
        }
        Mirror(root.left);
        Mirror(root.right);
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
    }

单元测试

public class test extends TestCase {

    int[] preArray = { 1, 2, 4, 7, 3, 5, 6, 8 };
    int[] inArray = { 4, 7, 2, 1, 5, 3, 8, 6 };

    int[] AfterArray = { 7, 4, 2, 5, 8, 6, 3, 1 };
    int[] levelArray = { 1, 2, 3, 4, 5, 6, 7, 8 };

    @Test
    public void testPrintFromTopToBottom() {
        // 测试广度优先遍历函数 正确性
        TreeNode node = TreeSolution.reConstructBinaryTree(preArray, inArray);
        ArrayList<Integer> list = TreeSolution.PrintFromTopToBottom(node);
        assertTrue(equal(levelArray, list));
    }

    @Test
    public void testReConstructBinaryTree() {
        TreeNode node = TreeSolution.reConstructBinaryTree(preArray, inArray);
        ArrayList<Integer> list = new ArrayList<Integer>();
        // 测试重建二叉树正确性
        // 同时测试先序遍历函数
        TreeSolution.scanPreTree(node, list);
        assertTrue(equal(preArray, list));
        // 同时测试中序遍历函数
        list.clear();
        TreeSolution.scanInTree(node, list);
        assertTrue(equal(inArray, list));
        // 同时测试后序遍历函数
        list.clear();
        TreeSolution.scanBackTree(node, list);
        assertTrue(equal(AfterArray, list));
    }

    private static boolean equal(int[] source, ArrayList<Integer> targ) {
        if (source == null || targ == null || source.length != targ.size()) {
            return false;
        }
        for (int i = 0; i < source.length; i++) {
            if (source[i] != targ.get(i)) {
                return false;
            }
        }
        return true;
    }
}

遍历二叉树每个子节点的路径

描述

输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
例如:
这里写图片描述
{10,5,12,4,7}, 寻找路径和为22的所有子节点路径

分析

先序遍历二叉树所有路径,如果一个节点,是叶子节点,而且节点路径和为所求值,就把它存储起来。

代码

    private ArrayList<ArrayList<Integer>> listAll = new ArrayList<ArrayList<Integer>>();
    private ArrayList<Integer> list = new ArrayList<Integer>();

    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target) {
        if (root == null) {
            return listAll;
        }
        list.add(root.val);
        target -= root.val;
        if (target == 0 && root.left == null && root.right == null) {
            listAll.add(new ArrayList<Integer>(list));
        }
        FindPath(root.left, target);
        FindPath(root.right, target);
        // 深度遍历,每遍历完一条路径需要回退一步,不然会把上一个路径的值加入当前路径
        list.remove(list.size() - 1);
        return listAll;
    }

测试

    TreeNode node1;
    TreeSolution solution;
    ArrayList<ArrayList<Integer>> array, arrayCorr;

    @Override
    protected void setUp() throws Exception {
        solution = new TreeSolution();
        // {10,5,12,4,7},22
        node1 = new TreeNode(10);
        TreeNode s1 = new TreeNode(5);
        TreeNode s2 = new TreeNode(12);
        node1.left = s1;
        node1.right = s2;
        TreeNode ss1 = new TreeNode(4);
        TreeNode ss2 = new TreeNode(7);
        s1.left = ss1;
        s1.right = ss2;

        array = solution.FindPath(node1, 22);
        arrayCorr = new ArrayList<ArrayList<Integer>>();
        ArrayList<Integer> arraySub1 = new ArrayList<Integer>();
        arraySub1.add(10);
        arraySub1.add(12);
        ArrayList<Integer> arraySub2 = new ArrayList<Integer>();
        arraySub2.add(10);
        arraySub2.add(5);
        arraySub2.add(7);
        arrayCorr.add(arraySub2);
        arrayCorr.add(arraySub1);

        super.setUp();
    }

    public void testFindPath() {
        assertEquals(arrayCorr, array);
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值