Java 数据结构之二叉树

二叉树的定义:

二叉树是n(n≥0)个结点的有限集,它或者是空集(n=0),或者由一个根结点及两棵互不相交的、分别称作这个根的左子树和右子树的二叉树组成。这是二叉树的递归定义。

二叉树的遍历:

所谓遍历,是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作依赖于具体的应用问题

对于二叉树的遍历,可进一步分为三种:

①前序遍历(根左右):对于每一棵子树,先访问该节点,然后是左子树,最后是右子树

②中序遍历(左根右):对于每一棵子树,先访问左子树,然后是该节点,最后是右子树

③后序遍历(左右根):对于每一棵子树,先访问左子树,然后是右子树,最后是该节点

其中递归是实现上三种遍历逻辑最简单的方式

第1关:二叉树的实现之前序遍历

1.任务描述

本关任务:完成用二叉链表存储的二叉树的前序遍历算法。

  • 补全preOrder(TreeNode root)方法,实现二叉树的前序遍历功能,并输出结点的值。

2.本关答案

        本关要求我们实现二叉树的前序遍历,我们根据递归来实现根左右的遍历方式

package step1;

/**
 * Created by zengpeng on 2018/2/9.
 */
public class BinaryTree {

    private TreeNode root;//根节点

    public BinaryTree() {
        root = null;
    }

    public void preOrder(TreeNode root) {
        /********** Begin *********/
        //递归实现  根左右
        if(root==null){
            return;
        }
        System.out.println(root.item);
        preOrder(root.leftChild);
        preOrder(root.rightChild);
        
        /********** End *********/
    }

    /**
     *以数组arr的数据,依次从上至下,从左至右构建一颗二叉树
     *
     * @param arr
     * @param n
     * @return
     */
    public TreeNode createTree(int arr[]) {
        TreeNode tmp[] = new TreeNode[arr.length + 1];
        for (int k = 1; k <= arr.length; k++) {
            TreeNode node = new TreeNode(arr[k - 1]);
            tmp[k] = node;
            if (k == 1) {
                root = node;
            } else {
                int j = k / 2;
                if (k % 2 == 0) {
                    tmp[j].leftChild = node;
                } else {
                    tmp[j].rightChild = node;
                }
            }
        }

        return root;
    }

    public static class TreeNode {
        private TreeNode leftChild;
        private TreeNode rightChild;
        private int item;

        public TreeNode(int item) {
            this(null, null, item);
        }

        public TreeNode(TreeNode leftChild, TreeNode rightChild, int item) {
            this.leftChild = leftChild;
            this.rightChild = rightChild;
            this.item = item;
        }
    }

}

第2关:二叉树的实现之中序遍历

1.任务描述

本关任务:实现以二叉链表存储的二叉树的中序遍历算法。

补全inOrder(TreeNode root)方法,实现二叉树的中序遍历功能,并输出结点的值

2.本关答案

        本关要求我们实现二叉树的中序遍历,我们根据递归来实现左根右的遍历方式

package step2;

/**
 * Created by zengpeng on 2018/2/12.
 */
public class BinaryTree {

    private TreeNode root;//根节点

    public BinaryTree() {
        root = null;
    }

    public void inOrder(TreeNode root) {
        /********** Begin *********/
        //递归实现  左根右
        if(root==null){
            return;
        }
        inOrder(root.leftChild);
        System.out.println(root.item);
        inOrder(root.rightChild);
       
        /********** End *********/
    }

    /**
     * 以数组arr的数据,依次从上至下,从左至右构建一颗二叉树
     *
     * @param arr
     * @param n
     * @return
     */
    public TreeNode createTree(int arr[]) {
        TreeNode tmp[] = new TreeNode[arr.length + 1];
        for (int k = 1; k <= arr.length; k++) {
            TreeNode node = new TreeNode(arr[k - 1]);
            tmp[k] = node;
            if (k == 1) {
                root = node;
            } else {
                int j = k / 2;
                if (k % 2 == 0) {
                    tmp[j].leftChild = node;
                } else {
                    tmp[j].rightChild = node;
                }
            }
        }

        return root;
    }

    public static class TreeNode {
        private TreeNode leftChild;
        private TreeNode rightChild;
        private int item;

        public TreeNode(int item) {
            this(null, null, item);
        }

        public TreeNode(TreeNode leftChild, TreeNode rightChild, int item) {
            this.leftChild = leftChild;
            this.rightChild = rightChild;
            this.item = item;
        }
    }
}

第3关: 二叉树的实现之后序遍历

1.任务描述

本关任务:实现以二叉链表存储的二叉树的后序遍历算法。

  • 补全postOrder(TreeNode root)方法,实现后序遍历功能,并输出结点值。

2.本关答案

        本关要求我们实现二叉树的后序遍历,我们根据递归来实现左右根的遍历方式

package step3;

/**
 * Created by zengpeng on 2018/2/12.
 */
public class BinaryTree {
    private TreeNode root;//根节点

    public BinaryTree() {
        root = null;
    }

    public void postOrder(TreeNode root) {
        /********** Begin *********/
        //递归实现  左右根
        if(root==null){
            return;
        }
        postOrder(root.leftChild);
        postOrder(root.rightChild);
        System.out.println(root.item);

        /********** End *********/
    }

    /**
     * 以数组arr的数据,依次从上至下,从左至右构建一颗二叉树
     *
     * @param arr
     * @param n
     * @return
     */
    public TreeNode createTree(int arr[]) {
        TreeNode tmp[] = new TreeNode[arr.length + 1];
        for (int k = 1; k <= arr.length; k++) {
            TreeNode node = new TreeNode(arr[k - 1]);
            tmp[k] = node;
            if (k == 1) {
                root = node;
            } else {
                int j = k / 2;
                if (k % 2 == 0) {
                    tmp[j].leftChild = node;
                } else {
                    tmp[j].rightChild = node;
                }
            }
        }

        return root;
    }

    public static class TreeNode {
        private TreeNode leftChild;
        private TreeNode rightChild;
        private int item;

        public TreeNode(int item) {
            this(null, null, item);
        }

        public TreeNode(TreeNode leftChild, TreeNode rightChild, int item) {
            this.leftChild = leftChild;
            this.rightChild = rightChild;
            this.item = item;
        }
    }
}

Java 数据结构之二叉搜索树

第1关:二叉搜索树的介绍与构建

1.任务描述

本关任务:构建一棵二叉搜索树,向其中添加结点。

  • 平台将创建用户补全后的BSTree类的对象;
  • 调用对象的insert(int key)方法,向树中添加结点;
  • 调用对象的preOrder()方法执行前序遍历;
  • 调用对象的inOrder()方法执行中序遍历;
  • 调用对象的postOrder()方法执行后序遍历;
  • 接着根据程序的输出判断程序是否正确。

2.相关知识

        二叉搜索树(binary search tree),又称二叉排序树,它或者是空树,或者是满足如下性质的二叉树:

  1. 若它的左子树非空,则左子树上所有结点的值均小于根结点的值;
  2. 若它的右子树非空,则右子树上所有结点的值均大于根结点的值;
  3. 左、右子树本身又各是一棵二叉排序树。

3.思路分析

        本关要求我们实现二叉树的插入操作insert

        首先我们来分析BSTree类中已经给我们了的方法

        首先就是节点类TreeNode:其中该类是BSTree的静态内部类,外部类可直接调用该内部类的成员变量和方法

//节点内部类
    public static class TreeNode {
        private TreeNode leftChild;//左孩子
        private TreeNode rightChild;//右孩子
        private int item;

        public TreeNode(int item) {
            this(null, null, item);
        }

        public TreeNode(TreeNode leftChild, TreeNode rightChild, int item) {
            this.leftChild = leftChild;
            this.rightChild = rightChild;
            this.item = item;
        }
    }

        然后就是BSTree的成员变量以及方法:一个成员变量root表示该树的根节点,空参的构造方法表示构建一个空树;然后是三种基本的遍历方式

        题目要求我们补全插入节点的代码

        我们根据二叉搜索树的性质,我们从根节点root开始一直向下寻找合适的插入位置,若插入的节点小于比较的节点,那么我们就向比较节点的左孩子处向下寻找;若插入的节点大于比较的节点,那么我们就向比较节点的右孩子处向下寻找,直到找到合适的位置

4.本关答案

package step1;

/**
 * Created by zengpeng on 2018/3/3.
 */
public class BSTree {

    private TreeNode root;//根结点

    public BSTree() {
        root = null;
    }

    /**
     * 向树root中插入key
     *
     * @param key 要插入的值
     */
    public void insert(int key) {
        /********** Begin *********/
       TreeNode newnode=new TreeNode(key);
       if(root==null){//树为空,要插入的节点即为根节点
           root=newnode;
       }else{
           TreeNode cur=root;
           while(cur!=null){
                if(cur.item==key){//已经存在
                    return;
                }else if(key<cur.item){//在该节点的左边
                    if(cur.leftChild==null){
                        cur.leftChild=newnode;
                    }else{//继续向左找
                        cur=cur.leftChild;
                    }
                }else if(key>cur.item){//在该节点的右边
                    if(cur.rightChild==null){
                        cur.rightChild=newnode;
                    }else{//继续向右找
                        cur=cur.rightChild;
                    }
                }
           }
        }
                 
        /********** End *********/
    }

    //前序遍历
    public void preOrder() {
        preOrder(root);
    }
    //中序遍历
    public void inOrder() {
        inOrder(root);
    }
    //后序遍历
    public void postOrder(){
        postOrder(root);
    }

    private void preOrder(TreeNode node) {
        if (node != null) {
            System.out.print(node.item + " ");
            preOrder(node.leftChild);
            preOrder(node.rightChild);
        }
    }

    private void inOrder(TreeNode node) {
        if (node != null) {
            inOrder(node.leftChild);
            System.out.print(node.item + " ");
            inOrder(node.rightChild);
        }
    }

    private void postOrder(TreeNode node) {
        if (node != null) {
            postOrder(node.leftChild);
            postOrder(node.rightChild);
            System.out.print(node.item + " ");
        }
    }

    //节点内部类
    public static class TreeNode {
        private TreeNode leftChild;
        private TreeNode rightChild;
        private int item;

        public TreeNode(int item) {
            this(null, null, item);
        }

        public TreeNode(TreeNode leftChild, TreeNode rightChild, int item) {
            this.leftChild = leftChild;
            this.rightChild = rightChild;
            this.item = item;
        }
    }
}

第2关:二叉搜索树的删除

1.任务描述

本关任务是:实现二叉搜索树的删除功能

  • 平台将创建用户补全后的BSTree类的对象;
  • 调用对象的insert(int key)()方法,构建一棵二叉搜索树;
  • 调用对象的遍历方法,输出删除元素前的各种遍历结果;
  • 调用对象的delete(int key)方法,删除元素key
  • 调用对象的遍历方法,输出删除元素后的各种遍历结果;
  • 接着根据程序的输出判断程序是否正确。

2.相关知识

从一棵二叉搜索树T中删除一个结点z的整个策略可分为三个基本情况:

  • 如果z没有孩子结点,那么只是简单地将它删除,并修改它的父结点;
  • 如果z只有一个孩子结点,那么将这个孩子提升到树中z的位置上,并修改z的父结点,使其指向z的孩子结点;
  • 如果z有两个孩子,那么找z右子树的最小节点代替z,最后删除替换后的最小节点

头歌SQL SERVER专题实验 视图的应用_数据结构

头歌SQL SERVER专题实验 视图的应用_java_02

头歌SQL SERVER专题实验 视图的应用_java_03

3.思路分析

        我们可以观察到,在第三种情况下(删除节点有左右两个节点),当我们将删除节点右子树的最小节点代替删除节点后,我们还需删除替换后的最小节点位置,但此时删除的情况就是情况一,二的一种,那么我们就可以从第三种情况开始,若存在第三种情况,那么我们就可以将其转化为第一二种情况的一种

        那么具体怎么操作呢?以下图为例:

头歌SQL SERVER专题实验 视图的应用_java_04

        首先要删除一个节点,就要知道它的父节点以及其孩子节点,因此首先我们要先找到删除节点的父节点

头歌SQL SERVER专题实验 视图的应用_java_05

        然后我们从第三种情况开始考虑:找到删除节点右子树的最小节点并与删除节点替换,将其转换为一二种情况

头歌SQL SERVER专题实验 视图的应用_算法_06

       

        那么此时要删除的节点以及其父节点就变了,我们要重新记录

头歌SQL SERVER专题实验 视图的应用_结点_07

        最后我们就要寻找删除节点的孩子节点,并将其父节点与其孩子节点相连,这样就完成了节点的删除,但我们需要考虑的是,若删除的是根节点,那么我们就直接返回其孩子节点即可,不用再更改其父节点的指针了(根节点的父节点为null)

头歌SQL SERVER专题实验 视图的应用_数据结构_08

        当然特殊情况(parent==null)不会包括第三种情况,因为经转化后删除节点必定有父节点;因此该特殊情况只存在于第一二中

4.本关答案

package step2;

/**
 * Created by zengpeng on 2018/3/14.
 */
public class BSTree {
    private TreeNode root;//根结点

    public BSTree() {
        root = null;
    }

    /**
     * 向树root中插入a
     *
     * @param key 要插入的值
     */
    public void insert(int key) {
        TreeNode x = root;
        TreeNode p = null;//始终指向x的父结点
        while (x != null) {
            p = x;
            if (key < x.item) {
                x = x.leftChild;
            } else {
                x = x.rightChild;
            }
        }
        if (null == p) {//空树
            root = new TreeNode(key);
        } else if (key < p.item) {
            p.leftChild = new TreeNode(key);
        } else {
            p.rightChild = new TreeNode(key);
        }
    }

    /**
     * 在树root中删除结点key
     *
     * @param key
     * @return
     */
    public void delete(int key) {
        root = delete(root, key);
    }

    private TreeNode delete(TreeNode x, int key) {
        /********** Begin *********/
        //三种情况
        TreeNode cur=x;
        TreeNode parent=null;
        while(x!=null && x.item!=key){
            parent=x;
            if(key<x.item){
                x=x.leftChild;
            }else{
                x=x.rightChild;
            }
        }
        //删除节点不存在
        if(x==null) return cur;
        //第三种情况:将删除节点与其右子树最小节点交换,并删除交换后的最小节点
        if(x.leftChild!=null && x.rightChild!=null){
            TreeNode mininode=x.rightChild;
            TreeNode miniparent=x;
            while(mininode.leftChild!=null){
                miniparent=mininode;
                mininode=mininode.leftChild;
            }
            //删除交换后的最小节点位置,转化为其他两种情况
            x.item=mininode.item;
            parent=miniparent;
            x=mininode;
        }
            //最后两种情况,找删除节点的孩子节点
            TreeNode child;
            if(x.rightChild!=null){
                child=x.rightChild;
            }else if(x.leftChild!=null){
                child=x.leftChild;
            }else{
                child=null;
            }
            //删除的是根节点
            if(parent==null) return child;
            //删除节点
            if(parent.leftChild==x){
                parent.leftChild=child;
            }else if(parent.rightChild==x){
                parent.rightChild=child;
            }
        
        return cur;



        /********** End *********/
    }

    /**
     * 删除树x中的最小结点
     *
     * @param x
     * @return
     */
    private TreeNode deleteMin(TreeNode x) {
        if (x.leftChild == null) return x.rightChild;
        x.leftChild = deleteMin(x.leftChild);
        return x;
    }

    /**
     * 查找树x中的最小结点
     *
     * @param x
     * @return
     */
    private TreeNode min(TreeNode x) {
        TreeNode p = x;
        while (p.leftChild != null) {
            p = p.leftChild;
        }
        return p;
    }

    public void preOrder() {
        preOrder(root);
    }

    private void preOrder(TreeNode node) {
        if (node != null) {
            System.out.print(node.item + " ");
            preOrder(node.leftChild);
            preOrder(node.rightChild);
        }
    }

    public void inOrder() {
        inOrder(root);
    }

    private void inOrder(TreeNode node) {
        if (node != null) {
            inOrder(node.leftChild);
            System.out.print(node.item + " ");
            inOrder(node.rightChild);
        }
    }

    public void postOrder() {
        postOrder(root);
    }

    private void postOrder(TreeNode node) {
        if (node != null) {
            postOrder(node.leftChild);
            postOrder(node.rightChild);
            System.out.print(node.item + " ");
        }
    }


    public static class TreeNode {
        private TreeNode leftChild;
        private TreeNode rightChild;
        private int item;

        public TreeNode(int item) {
            this(null, null, item);
        }

        public TreeNode(TreeNode leftChild, TreeNode rightChild, int item) {
            this.leftChild = leftChild;
            this.rightChild = rightChild;
            this.item = item;
        }
    }
}

第3关:二叉搜索树的查找

1.任务描述

本关任务:实现二叉搜索树的查找功能,如果指定的元素x存在树中则返回true,否则返回false

  • 平台将创建用户补全后的BSTree类的对象;
  • 调用对象的insert(int key)方法,构建一棵二叉搜索树;
  • 调用对象的preOrder()方法,输出删除操作前树中的结点;
  • 调用对象的delete(int key)方法,删除最先添加的结点和最后添加的结点;
  • 调用对象的preOrder()方法,输出删除操作后树中的结点;
  • 调用对象的search(int key)方法,测试原来的结点哪些还在树中;
  • 接着根据程序的输出判断程序是否正确。

2.思路分析

        根据二叉搜索树的性质,小了向左,大了向右,直到找到

3.本关答案

package step3;

/**
 * Created by zengpeng on 2018/3/14.
 */
public class BSTree {
    private TreeNode root;//根结点

    public BSTree() {
        root = null;
    }

    /**
     * 向树root中插入a
     *
     * @param key    要插入的值
     */
    public void insert(int key) {
        TreeNode x = root;
        TreeNode p = null;//始终指向x的父结点
        while (x != null) {
            p = x;
            if (key < x.item) {
                x = x.leftChild;
            } else {
                x = x.rightChild;
            }
        }
        if (null == p) {//空树
            root = new TreeNode(key);
        } else if (key < p.item) {
            p.leftChild = new TreeNode(key);
        } else {
            p.rightChild = new TreeNode(key);
        }
    }

    /**
     * 判断树root中是否包含key,包含则返回true,不包含返回false
     *
     * @param key
     * @return
     */
    public boolean search(int key) {
        /********** Begin *********/
        TreeNode p = root;
        //小了向左,大了向右
        while (p != null && key != p.item) {
            if (key < p.item) {
                p = p.leftChild;
            } else {
                p = p.rightChild;
            }
        }
        if (p == null) {
            return false;
        } else {
            return true;
        }
        /********** End *********/
    }


    /**
     * 在树root中删除结点key
     *
     * @param key
     * @return
     */
    public void delete(int key) {
        root = delete(root, key);
    }

    private TreeNode delete(TreeNode x, int key) {
        if (x == null) {
            return null;
        }

        if (key < x.item) {
            x.leftChild = delete(x.leftChild, key);
        } else if (key > x.item) {
            x.rightChild = delete(x.rightChild, key);
        } else {
            if (x.leftChild == null) return x.rightChild;
            if (x.rightChild == null) return x.leftChild;
            TreeNode t = x;
            x = min(t.rightChild);
            x.rightChild = deleteMin(t.rightChild);
            x.leftChild = t.leftChild;
        }

        return x;
    }

    /**
     * 删除树x中的最小结点
     * @param x
     * @return
     */
    private TreeNode deleteMin(TreeNode x) {
        if (x.leftChild == null) return x.rightChild;
        x.leftChild = deleteMin(x.leftChild);
        return x;
    }

    /**
     * 查找树x中的最小结点
     *
     * @param x
     * @return
     */
    private TreeNode min(TreeNode x) {
        TreeNode p = x;
        while (p.leftChild != null) {
            p = p.leftChild;
        }
        return p;
    }

    public void preOrder() {
        preOrder(root);
    }

    public void inOrder() {
        inOrder(root);
    }

    public void postOrder() {
        postOrder(root);
    }

    private void preOrder(TreeNode node) {
        if (node != null) {
            System.out.print(node.item + " ");
            preOrder(node.leftChild);
            preOrder(node.rightChild);
        }
    }

    private void inOrder(TreeNode node) {
        if (node != null) {
            inOrder(node.leftChild);
            System.out.print(node.item + " ");
            inOrder(node.rightChild);
        }
    }

    private void postOrder(TreeNode node) {
        if (node != null) {
            postOrder(node.leftChild);
            postOrder(node.rightChild);
            System.out.print(node.item + " ");
        }
    }


    public static class TreeNode {
        private TreeNode leftChild;
        private TreeNode rightChild;
        private int item;

        public TreeNode(int item) {
            this(null, null, item);
        }

        public TreeNode(TreeNode leftChild, TreeNode rightChild, int item) {
            this.leftChild = leftChild;
            this.rightChild = rightChild;
            this.item = item;
        }
    }
}

Java算法与数据结构测试——二叉树

第1关:向二叉树中插入叶子节点

1.任务描述

本关任务:向二叉树中插入左叶子节点,请补全insertLeft(T x, Node<T> parent)函数实现插入左叶子节点的功能

2.思路分析

        我们主要看本关已经给出的insertRight方法,本关要求我们补全插入左子节点的方法,这与insertRight思路逻辑一致,我们只需稍微更改一下孩子的指向即可

//在当前二叉树的parent节点中插入一个新的左子结点,若已存在左子树,则将该左子树变成新左子结点的右子树
	public boolean insertLeft(T x, Node<T> parent)
	{ 
		/********* Begin *********/
		if(parent==null) return false;
        Node<T> p=new Node<>(x);
        if(parent.lChild==null){
            parent.lChild=p;
        }else{
            p.rChild=parent.lChild;
            parent.lChild=p;
        }
        return true;
		/********* End *********/
	} 
    //在当前二叉树的parent节点中插入一个新的右子结点,若已存在右子树,则将该右子树变成新右子结点的左子树
    public boolean insertRight(T x, Node<T> parent)
    { 
		if(parent==null)	return false;
		Node<T> p= new Node<T>(x);
		if(parent.rChild==null) parent.rChild = p;
		else
		{
			p.rChild = parent.rChild;
			parent.rChild = p;
		}
		return true; 
	}