DataStructures - 04 :二叉树_二叉树与数组_线索化_堆

1、二叉树

1.1 二叉树的遍历

1、概念:
在这里插入图片描述
前序遍历:根节点、当前节点左子树、当前节点右子树: 1、2、3、5、4
中序遍历:当前节点左子树、 根节点、当前节点右子树:2、1、5、3、4
后序遍历:当前节点左子树、当前节点右子树、 根节点:2、5、4、3、1

2、代码实现:

(1)HeroNode节点类的实现:

@Data
public class HeroNode{
    private int no;
    private String name;
    private HeroNode left;
    private HeroNode right;
    
	//前序遍历
    public void preOrder(){
        System.out.println(this);
        if(this.left!=null){
            this.left.preOrder();
        }
        if(this.right!=null){
            this.right.preOrder();
        }
    }
    
	//中序遍历
    public void infixOrder(){
        if(this.left!=null){
            this.left.infixOrder();
        }
        System.out.println(this);
        if(this.right!=null){
            this.right.infixOrder();
        }
    }

    //后序遍历
    public void postOrder(){
        if(this.left!=null){
            this.left.postOrder();
        }
        if(this.right!=null){
            this.right.postOrder();
        }
        System.out.println(this);
    }
	
	//构造方法
    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }
    
	//toString类
    @Override
    public String toString() {
        return "HeroNode [no=" + no + ", name=" + name + "]";
    }
}

(2)BinaryTree二叉树类的实现:

public class BinaryTree{
    private HeroNode root;

    public void setRoot(HeroNode root) {
        this.root = root;
    }

    //前序遍历
    public void preOrder(){
        if(this.root!=null){
            this.root.preOrder();
        }else{
            System.out.println("根节点为空无法遍历");
        }
    }

    //中序遍历
    public void infixOrder(){
        if(this.root!=null){
            this.root.infixOrder();
        }else{
            System.out.println("根节点为空无法遍历");
        }
    }

    //后序遍历
    public void postOrder(){
        if(this.root!=null){
            this.root.postOrder();
        }else{
            System.out.println("根节点为空无法遍历");
        }
    }
}

(3)BinaryDemo测试类:创建一颗二叉树并测试

public class BinaryDemo {
    public static void main(String[] args) {
        //先需要创建一颗二叉树
        BinaryTree binaryTree = new BinaryTree();
        //创建需要的结点
        HeroNode root = new HeroNode(1, "宋江");
        HeroNode node2 = new HeroNode(2, "吴用");
        HeroNode node3 = new HeroNode(3, "卢俊义");
        HeroNode node4 = new HeroNode(4, "林冲");
        HeroNode node5 = new HeroNode(5, "关胜");

        //说明,我们先手动创建该二叉树,后面我们学习递归的方式创建二叉树
        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
        binaryTree.setRoot(root);

//        binaryTree.postOrder();
        binaryTree.infixOrder();
    }
}

1.2 二叉树查找某个节点

在这里插入图片描述
1、思路:

查找指定的节点:根据节点编号no进行查找
1、前序查找:
(1)先判断当前节点的no是否等于需要查找的
(2)如果当前节点的左子节点不为空,向左递归前序查找
(3)如果当前节点的右子节点不为空,向右递归前序查找

2、中序查找:
(1)如果当前节点的左子节点不为空,向左递归中序查找
(2)先判断当前节点的no是否等于需要查找的
(3)如果当前节点的右子节点不为空,向右递归中序查找

3、后序查找:
(1)如果当前节点的左子节点不为空,向左递归后序查找
(2)如果当前节点的右子节点不为空,向右递归后序查找
(3)先判断当前节点的no是否等于需要查找的

2、代码实现:

(1)HeroNode节点类:

@Data
public class HeroNode{
    private int no;
    private String name;
    private HeroNode left;
    private HeroNode right;

    //前序查找
    public HeroNode preSearch(int no){
        if(this.no==no){
            return this;
        }

        HeroNode resultNode = null;
        if(this.left!=null){
            resultNode = this.left.preSearch(no);
        }
        if(resultNode!=null){
            return resultNode;
        }

        if(this.right!=null){
            resultNode = this.right.preSearch(no);
        }
        return resultNode;
    }

    //中序查找
    public HeroNode infixSearch(int no){
        HeroNode resultNode = null;
        if(this.left!=null){
            resultNode = this.left.infixSearch(no);
        }
        if(resultNode!=null){
            return resultNode;
        }

        if(this.no==no){
            return this;
        }

        if(this.right!=null){
            resultNode = this.right.infixSearch(no);
        }
        return resultNode;
    }

    //后序查找
    public HeroNode postSearch(int no){
        HeroNode resultNode = null;
        if(this.left!=null){
            resultNode = this.left.postSearch(no);
        }
        if(resultNode!=null){
            return resultNode;
        }

        if(this.right!=null){
            resultNode = this.right.postSearch(no);
        }

        if(resultNode!=null){
            return resultNode;
        }

        if(this.no==no){
            return this;
        }

        return resultNode;
    }
    
    //构造方法
    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }
    
	//toString类
    @Override
    public String toString() {
        return "HeroNode [no=" + no + ", name=" + name + "]";
    }
}

(2)BinaryTree二叉树类:

public class BinaryTree{
    private HeroNode root;

    public void setRoot(HeroNode root) {
        this.root = root;
    }

    //前序查找
    public HeroNode preSearch(int no){
        if(root!=null){
            return root.preSearch(no);
        }else {
            return null;
        }
    }

    //中序查找
    public HeroNode infixSearch(int no){
        if(root!=null){
            return root.infixSearch(no);
        }else{
            return null;
        }
    }

    //后序查找
    public HeroNode postSearch(int no){
        if(root!=null){
            return root.postSearch(no);
        }else{
            return null;
        }
    }
}

(3)BinaryDemo 测试类:

public class BinaryDemo {
    public static void main(String[] args) {
        //先需要创建一颗二叉树
        BinaryTree binaryTree = new BinaryTree();
        //创建需要的结点
        HeroNode root = new HeroNode(1, "宋江");
        HeroNode node2 = new HeroNode(2, "吴用");
        HeroNode node3 = new HeroNode(3, "卢俊义");
        HeroNode node4 = new HeroNode(4, "林冲");
        HeroNode node5 = new HeroNode(5, "关胜");

        //说明,我们先手动创建该二叉树,后面我们学习递归的方式创建二叉树
        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
        binaryTree.setRoot(root);

        HeroNode heroNode = binaryTree.postSearch(3);
        if(heroNode!=null){
            System.out.println(heroNode.toString());
        }else{
            System.out.println("找不到");
        }
    }
}

1.3 二叉树删除某个节点

需求:
(1)如果删除的节点是叶子节点,就删除该节点
(2)如果删除的是非叶子节点,就删除该子树

1、思路:

1、如果只有一个root节点,等价于将二叉树置空
2、如果当前节点左子节点不为空,并且左子节点就是需要删除的节点,this.left=null,并return
3、如果当前节点右子节点不为空,并且右子节点就是需要删除的节点,this.left=null,并return
4、如果左右节点都不是要删除的节点,向左递归删除节点
5、如果向左递归没有找到要删除的节点,向右递归删除节点

(1)节点类HeroNode:

@Data
public class HeroNode{
    private int no;
    private String name;
    private HeroNode left;
    private HeroNode right;

    //删除某个节点
    public void delNode(int no){
        if(this.left!=null && this.left.no==no){
            this.left = null;
            return;
        }

        if(this.right!=null && this.right.no==no){
            this.right = null;
            return;
        }

        if(this.left!=null){
            this.left.delNode(no);
        }

        if(this.right!=null){
            this.right.delNode(no);
        }
    }
    
	//构造方法
    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }
    
	//toString类
    @Override
    public String toString() {
        return "HeroNode [no=" + no + ", name=" + name + "]";
    }
}

(2)二叉树类BinaryTree:

public class BinaryTree{
    private HeroNode root;

    public void setRoot(HeroNode root) {
        this.root = root;
    }

    //删除某个节点
    public void delNode(int no){
        if(root!=null){
            if(root.getNo()==no){
                root = null;
            }else{
                root.delNode(no);
            }
        }else{
            System.out.println("树为空");
        }
    }
}

(3)测试类BinaryDemo:

public class BinaryDemo {
    public static void main(String[] args) {
        //先需要创建一颗二叉树
        BinaryTree binaryTree = new BinaryTree();
        //创建需要的结点
        HeroNode root = new HeroNode(1, "宋江");
        HeroNode node2 = new HeroNode(2, "吴用");
        HeroNode node3 = new HeroNode(3, "卢俊义");
        HeroNode node4 = new HeroNode(4, "林冲");
        HeroNode node5 = new HeroNode(5, "关胜");

        //说明,我们先手动创建该二叉树,后面我们学习递归的方式创建二叉树
        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
        binaryTree.setRoot(root);

        System.out.println("删除前.................");
        binaryTree.preOrder();
        binaryTree.delNode(5);
        System.out.println("删除后.................");
        binaryTree.preOrder();
    }
}

2、顺序存储二叉树

1、概念:

将二叉树的数据以数组的方式来存放,并且遍历的时候仍然可以以前序、中序、后序的方式来遍历。
在这里插入图片描述
顺序存储二叉树的特点:

  • 顺序存储二叉树通常只考虑完全二叉树
  • 第n个元素的左子节点为2*n+1
  • 第n个元素的右子节点为2*n+2
  • 第n个元素的父节点为(n-1)/2

2、代码实现:

(1)ArrayBinaryTree:传入一个数组

public class ArrayBinaryTree {

    private int[] arr;
    public void setArr(int[] arr) {
        this.arr = arr;
    }

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

    /**
     * 前序遍历
     * @param index 数组的索引
     */
    public void preOrder(int index){
        if(arr==null|| arr.length==0){
            return;
        }

        System.out.println(arr[index]);
        //向左递归遍历
        if(2*index+1<arr.length){
            preOrder(2*index+1);
        }
        //向右递归遍历
        if(2*index+2<arr.length){
            preOrder(2*index+2);
        }
    }
}

(2)测试类ArrayBinaryTreeDemo:

public class ArrayBinaryTreeDemo {
    public static void main(String[] args) {
        int[] arr = {12,34,45,67,8,289,90};
        ArrayBinaryTree arrayBinaryTree = new ArrayBinaryTree();
        arrayBinaryTree.setArr(arr);
        arrayBinaryTree.preOrder();
    }
}

3、线索化二叉树

3.1 实现线索化二叉树

1、概念:

一个节点的前一个节点称为前驱节点
一个节点的后一个节点称为后继节点

n个节点的二叉链表中含有n+1个空指针域,利用二叉链表中的空指针域,存放指向节点在某种遍历次序下的前驱和后继节点的指针(这种附加的指针称为线索)

线索二叉树分为前序线索二叉树、中序线索二叉树、后序线索二叉树。
将二叉树进行中序线索二叉树。中序遍历的数列为{67、34、8、12、289、45}
在这里插入图片描述经过线索化之后:
left指向的可能是左子树也可能是前驱节点
right指向的可能是右子树也可能是后继节点

2、代码实现:

(1)HeroNode节点类:

@Data
public class HeroNode {
    private int no;
    private String name;
    private HeroNode left;
    private HeroNode right;

    //leftType=0代表指向左子树,leftType=1代表指向前驱节点
    private int leftType;
    //rightType=0代表指向右子树,leftType=1代表指向后继节点
    private int rightType;
    
    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }
    
    @Override
    public String toString() {
        return "HeroNode [no=" + no + ", name=" + name + "]";
    }
}

(2)ThreadedBinaryTree线索化二叉树类:

public class ThreadedBinaryTree {
    private HeroNode root;

    public void setRoot(HeroNode root) {
        this.root = root;
    }

    //pre总是指向前驱节点
    private HeroNode pre = null;

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

    public void threadedNodes(HeroNode node){

        if(node==null){
            return;
        }

        //线索化左子树
        threadedNodes(node.getLeft());

        //线索化当前节点
        if(node.getLeft()==null){
            node.setLeft(pre);
            node.setLeftType(1);
        }
        if(pre!=null && pre.getRight()==null){
            pre.setRight(node);
            pre.setRightType(1);
        }
        pre = node;

        //线索化右子树
        threadedNodes(node.getRight());
    }
}

(3)测试类ThreadedBinaryTreeDemo:

public class ThreadedBinaryTreeDemo {
    public static void main(String[] args) {
        //测试一把中序线索二叉树的功能
        HeroNode root = new HeroNode(12, "tom");
        HeroNode node2 = new HeroNode(34, "jack");
        HeroNode node3 = new HeroNode(45, "smith");
        HeroNode node4 = new HeroNode(67, "mary");
        HeroNode node5 = new HeroNode(8, "king");
        HeroNode node6 = new HeroNode(289, "dim");

        //二叉树,后面我们要递归创建, 现在简单处理使用手动创建
        root.setLeft(node2);
        root.setRight(node3);
        node2.setLeft(node4);
        node2.setRight(node5);
        node3.setLeft(node6);

        //测试中序线索化
        ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
        threadedBinaryTree.setRoot(root);
        threadedBinaryTree.threadedNodes();

        //测试: 以8号节点测试
        System.out.println(node5.getLeft());
        System.out.println(node5.getRight());
    }
}

3.2 线索化二叉树的遍历

对前面实现的中序线索二叉树进行遍历:
在ThreadedBinaryTree类中国添加一个遍历中序线索化二叉树的方法:

//遍历线索化二叉树
public void threadList(){
    HeroNode node = root;
    while(node!=null){
        while(node.getLeftType()==0){
            node = node.getLeft();
        }

        System.out.println(node);

        while(node.getRightType()==1){
            node = node.getRight();
            System.out.println(node);
        }

        node = node.getRight();
    }
}

4、二叉堆

4.1 堆的概念

1、完全二叉树:
在这里插入图片描述
2、堆是具有以下性质的完全二叉树:
(1)大顶堆:每个节点的值都大于或者等于其左右孩子节点的值
(2)小顶堆:每个节点的值都小于或者等于其左右孩子节点的值

4.2 堆排序

1、堆排序的思想:

  1. 将待排序的序列先构成一个大顶堆
  2. 此时,整个序列的最大值就是堆顶的元素
  3. 将其与末尾元素进行交换,此时末尾就是最大值
  4. 然后将剩余的n-1个元素重新构成堆,这样会得到n个元素的次小值,如此反复,就可以得到一个有序的序列

2、大顶堆映射到数组:

arr[i]>=arr[2*i+1]且arr[i]>=arr[2*i+2]

在这里插入图片描述3、将一个序列调整为大顶堆的过程:

按照从做到右,从上到下的顺序调整各个非叶子节点:

public class HeapSort {    
    /**
     * @param arr
     * @param i 表示非叶子节点在数组中的索引
     * @param length 表示对多少个元素进行调整
     */
    public static void adjustHeap(int arr[],int i,int length){
        int temp = arr[i];
        //k表示i的左子节点
        for(int k=2*i+1;k<length;k=2*k+1){
            //如果左子节点<右子节点,就让k指向右子节点
            if(k+1<length && arr[k]<arr[k+1]){
                k++;
            }
            if(arr[k]>temp){
                arr[i] = arr[k];
                arr[k] = temp;
                i = k;
            }else{
                break;
            }
        }
    }
	
	//测试将序列逐步调整为大顶堆的方法
    public static void main(String[] args) {
        int arr[] = {12,34,45,67,8,78,5,23,99};
        //将以3为对应的非叶子节点的子树调整为大顶堆
        adjustHeap(arr,3,arr.length);
        System.out.println(Arrays.toString(arr));
		
		//将以2为对应的非叶子节点的子树调整为大顶堆
        adjustHeap(arr,2,arr.length);
        System.out.println(Arrays.toString(arr));
        
		//将以1为对应的非叶子节点的子树调整为大顶堆
        adjustHeap(arr,1,arr.length);
        System.out.println(Arrays.toString(arr));
		
		//将以0为对应的非叶子节点的子树调整为大顶堆
        adjustHeap(arr,0,arr.length);
        System.out.println(Arrays.toString(arr));
    }
}

在这里插入图片描述
4、堆排序:
在这里插入图片描述

package 二叉堆排序;

import java.util.Arrays;

public class HeapSort {
    public static void main(String[] args) {
        int arr[] = {12,34,45,67,8,78,5,23,99};
        heapSort(arr);
    }
    
    //编写一个堆排序的方法
    public static void heapSort(int arr[]) {
        /**
         * 将一个序列转换成大顶堆
         */
        for(int i=arr.length/2-1;i>=0;i--){
            adjustHeap(arr,i,arr.length);
        }

        System.out.println("将序列转换为大顶堆:");
        System.out.println(Arrays.toString(arr));

        int temp;
        for(int j=arr.length-1;j>0;j--){
            //交换
            temp = arr[j];
            arr[j] = arr[0];
            arr[0] = temp;

            //此时只需要调整堆顶节点即可,因此堆顶下面的节点已经符合堆的定义了
            adjustHeap(arr,0,j);

            System.out.println("将剩余序列转换成大顶堆:");
            System.out.println(Arrays.toString(arr));
        }
    }

    /**
     * @param arr
     * @param i 表示非叶子节点在数组中的索引
     * @param length 表示对多少个元素进行调整
     */
    public static void adjustHeap(int arr[],int i,int length){
        int temp = arr[i];
        //k表示i的左子节点
        for(int k=2*i+1;k<length;k=2*k+1){
            //如果左子节点<右子节点,就让k指向右子节点
            if(k+1<length && arr[k]<arr[k+1]){
                k++;
            }
            if(arr[k]>temp){
                arr[i] = arr[k];
                arr[k] = temp;
                i = k;
            }else{
                break;
            }
        }
    }
}

输出结果:

将序列转换为大顶堆:
[99, 67, 78, 34, 8, 45, 5, 23, 12]
将剩余序列转换成大顶堆:
[78, 67, 45, 34, 8, 12, 5, 23, 99]
将剩余序列转换成大顶堆:
[67, 34, 45, 23, 8, 12, 5, 78, 99]
将剩余序列转换成大顶堆:
[45, 34, 12, 23, 8, 5, 67, 78, 99]
将剩余序列转换成大顶堆:
[34, 23, 12, 5, 8, 45, 67, 78, 99]
将剩余序列转换成大顶堆:
[23, 8, 12, 5, 34, 45, 67, 78, 99]
将剩余序列转换成大顶堆:
[12, 8, 5, 23, 34, 45, 67, 78, 99]
将剩余序列转换成大顶堆:
[8, 5, 12, 23, 34, 45, 67, 78, 99]
将剩余序列转换成大顶堆:
[5, 8, 12, 23, 34, 45, 67, 78, 99]

分析过程:
在这里插入图片描述

发布了716 篇原创文章 · 获赞 130 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/qq_42764468/article/details/105252062