文章目录
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、堆排序的思想:
- 将待排序的序列先构成一个大顶堆
- 此时,整个序列的最大值就是堆顶的元素
- 将其与末尾元素进行交换,此时末尾就是最大值
- 然后将剩余的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]
分析过程: