X16数据结构部分07
简单二叉树的创建及增删改查
package du;
/**
* @author 404小恐龙
* @version 1.8
* @date 2021/10/18 16:57
*/
public class m09 {
public static void main(String[] args) {
/*
创建树和根节点
并将根节点赋给树
*/
BinaryTree binaryTree = new BinaryTree();
BinaryTreeNode root = new BinaryTreeNode(1);
binaryTree.setRoot(root);
/*
创建根节点的左子树和右子树
加入树节点中
*/
BinaryTreeNode rootLeftChild = new BinaryTreeNode(2);
BinaryTreeNode rootRightChild = new BinaryTreeNode(3);
root.setLeftNode(rootLeftChild);
root.setRightNode(rootRightChild);
/*
第2层左右节点分别创建2个子节点
*/
rootLeftChild.setLeftNode(new BinaryTreeNode(4));
rootLeftChild.setRightNode(new BinaryTreeNode(5));
rootRightChild.setLeftNode(new BinaryTreeNode(7));
rootRightChild.setRightNode(new BinaryTreeNode(8));
/*
二叉树遍历方式
前序遍历 root -> left -> right
中序遍历 left -> root -> right
后序遍历 left -> right -> root
*/
binaryTree.preorderTraversal();
binaryTree.inorderTraversal();
/*
前序查找算法实现
*/
BinaryTreeNode treeNode = binaryTree.preorderLookup(5);
System.out.println(treeNode.toString());
// BinaryTreeNode{value=5, leftNode=null, rightNode=null}
/*
删除树的子节点
*/
}
}
/**
* BinaryTreeNode
*
* @author stack frame
*/
class BinaryTreeNode {
int value;
BinaryTreeNode leftNode;
BinaryTreeNode rightNode;
public BinaryTreeNode(int value){
this.value = value;
}
public void setLeftNode(BinaryTreeNode leftNode) {
this.leftNode = leftNode;
}
public void setRightNode(BinaryTreeNode rightNode) {
this.rightNode = rightNode;
}
/**
* 前序遍历
*/
public void preorderTraversal() {
System.out.print(value + " "); // 1 2 4 5 3 7 8
if (leftNode != null) {
leftNode.preorderTraversal();
}
if (rightNode != null) {
rightNode.preorderTraversal();
}
}
/**
* 中序遍历
*/
public void inorderTraversal() {
if (leftNode != null) {
leftNode.preorderTraversal();
}
System.out.print(value + " "); // 2 4 5 1 3 7 8
if (rightNode != null) {
rightNode.preorderTraversal();
}
}
/**
* 前序查找
* @param i 需要查找节点的值
* @return 值对应树的节点对象
*/
public BinaryTreeNode preorderLookup(int i) {
BinaryTreeNode targetNode = null;
/*
先查找根节点
*/
if (this.value == i) {
return this;
}else {
if (leftNode != null) {
targetNode = leftNode.preorderLookup(i);
}
if (targetNode != null) {
/*
程序运行到此处
说明目标节点已经在左子树中查到了
直接返回即可
*/
return targetNode;
}
if (rightNode != null) {
targetNode = rightNode.preorderLookup(i);
}
if (targetNode != null) {
/*
程序运行到此处
说明目标节点已经在左子树中查到了
直接返回即可
*/
return targetNode;
}
}
return targetNode;
}
@Override
public String toString() {
return "BinaryTreeNode{" +
"value=" + value +
", leftNode=" + leftNode +
", rightNode=" + rightNode +
'}';
}
/**
* 删除子节点
* @param i 需要删除的节点值
*/
public void deleteNode(int i) {
/*
删除方法简单
这里就不写了
后续会有二叉排序树的删除方法
过程很复杂的
做好心理准备
*/
}
}
/**
* BinaryTree
*
* @author stack frame
*/
class BinaryTree {
BinaryTreeNode root;
public BinaryTreeNode getRoot(){
return root;
}
public void setRoot(BinaryTreeNode root) {
this.root = root;
}
public void preorderTraversal() {
root.preorderTraversal();
}
public void inorderTraversal() {
root.inorderTraversal();
}
public BinaryTreeNode preorderLookup(int i) {
return root.preorderLookup(i);
}
public void deleteNode(int i) {
if (root.value == i) {
root = null;
}else {
root.deleteNode(i);
}
}
}
/*
补充说明
顺序存储的二叉树概述
通常情况只考虑完全二叉树
需要了解的知识
存储结构是数组
索引为0的位置是根节点
索引为1和2的位置是左子树和右子树
之后就是2的幂次方1组1层之类的了
如果从上往下层级划分节点
就是根节点是0
对应左子树节点是1
右子树节点是2
依次往下
第n个节点的左子节点 2n + 1
第n个节点的右子节点 2n + 2
第n个节点的父节点 (n - 1) / 2
*/
堆排序
public class m13 {
public static void main(String[] args) {
}
public static void heapSort(int[] array) {
int tmp = 0;
/*
程序开始调用堆排序方法
第一次排序返回{4,9,8,5,6}
此时i已经写死
开发中i会被算法计算出来
所以我们忽略这条调用语句
完成最终代码
*/
// adjectBigTopPile(array,1,array.length);
for (int i = array.length; i >= 0; i--) {
/*
程序运行到此处
由于采用递归调用的原因
for循环会一直按左子树往下查找
一直查到最后一个非叶子节点
然后依次往上进行查找
*/
adjectBigTopPile(array,i,array.length);
}
/*
程序运行到此处
说明第1轮堆排序已经结束
此时堆顶为最大元素
程序输出{9,6,8,5,4}
此时将堆顶与末尾交换位置
*/
for (int j = array.length - 1; j > 0; j--) {
tmp = array[j];
array[j] = array[0];
array[0] = tmp;
/*
程序运行到此处
此时4和9已经未完成交换
元素9已经在最终位置
剩余4个元素重新进行调整
所以说堆排序的扫描范围是不断缩小的
注意
下面这个调用语句执行完后
for循环才能继续执行
*/
adjectBigTopPile(array,0,j);
}
}
/**
* 调整当前父元素大于左子树和右子树
* @param array 堆对应的数组
* @param i 对应的非叶子节点调整成大顶堆的二叉树对应数组索引位置
* 若i = 1,则调整最左下角的堆
* 数组变为{4,9,8,5,6}
* 调整1整趟完毕后就是{9,6,8,5,4}
* @param length 表示多少个元素需要调整 逐渐递减 最大值首先被去掉
*/
public static void adjectBigTopPile(int[] array,int i,int length) {
/*
程序开始运行
i默认指向最后一个非叶子节点
*/
int tmp = array[i];
for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {
if (k + 1 < length && array[k] < array[k + 1]) {
/*
程序运行到此处
说明当前数组左子节点相邻位置对应的
右子节点比当前左子节点大
需要调换位置
k指针指向右字节带你
*/
k++;
}
if (array[k] > tmp) {
/*
程序运行到此处
说明当前左子节点或右子节点
大于当前父节点
需要调换位置
此时k指向的元素是3者中最大的元素
然后让i指向k
继续循环比较
*/
array[i] = array[k];
i = k;
}else {
/*
程序运行到此处
说明最大值就是在父节点
直接进行下一轮比较
比较之前
当前i指向的值就是父节点并且是3者中最大的值
下一次比较的时候
i对应的会是父节点的左子节点
与右子节点和父节点进行比较和交换
*/
break;
}
}
/*
程序运行到此处
此时整个数组的最大值已经被放在索引为0的位置
也就是二叉树的根节点位置
此时程序绝对没有执行else语句
需要将之前指向的较小节点覆盖掉
左子树或者右子树
*/
array[i] = tmp;
}
}
线索二叉树代码实现
/**
* BinaryTree
*
* @author stack frame
*/
class BinaryTree {
BinaryTreeNode root;
BinaryTreeNode tmp;
public BinaryTreeNode getRoot() {
return root;
}
public void setRoot(BinaryTreeNode root) {
this.root = root;
}
public void preorderTraversal() {
root.preorderTraversal();
}
public void inorderTraversal() {
root.inorderTraversal();
}
public BinaryTreeNode preorderLookup(int i) {
return root.preorderLookup(i);
}
public void deleteNode(int i) {
if (root.value == i) {
root = null;
} else {
root.deleteNode(i);
}
}
public void threadNode(){
threadNode(root);
}
/**
* 中序线索化二叉树
* @param node 根节点
*/
public void threadNode(BinaryTreeNode node) {
/*
程序运行到此处
需要设置递归出口
*/
if (node == null) {
return;
}
/*
递归处理左子树和右子树
*/
threadNode(node.leftNode);
if (node.leftNode == null) {
/*
程序运行到此处
说明该节点左指针为空
需要改变指针类型
并指向该节点的前驱节点
前驱节点每次调用该方法都会换掉
*/
node.leftNode = tmp;
node.leftType = 1;
tmp = node;
}
/*
程序运行到此处
需要判断当前节点的前驱节点是否为空
如果不做处理
会报空指针异常java.lang.NullPointerException
*/
if (tmp != null && tmp.rightNode == null) {
/*
程序运行到此处
说明当前节点的前一个节点为空
右指针需要指向当前节点
*/
tmp.rightNode = node;
tmp.rightType = 1;
}
threadNode(node.rightNode);
}
}
/*
线索二叉树概述
类似于双向链表二叉树
如果这个节点的左右指针
有空的情况
可以让这个空的指针域
指向前一个节点
中序遍历二叉树的时候
空的左子树指向前驱节点
空的右子树指向后继节点
需要找变量来标识
比如0 1
左指针和右指针
所以需要在TreeNode类里面加入
int类型的标识指针leftType和rightType
为了体现封装思想
需要写一个方法
中序线索化二叉树
*/
遍历线索二叉树
/**
* 遍历线索二叉树
*/
public void trailBinaryTreeTraversal () {
BinaryTreeNode treeNode = root;
if (treeNode != null) {
while (treeNode.leftType == 0) {
/*
程序第1次运行到此处
说明查找当前节点为根节点
接着循环往后找
*/
treeNode = treeNode.leftNode;
}
/*
程序运行到此处
说明能找到最左下角的节点
打印该节点
*/
System.out.println(treeNode);
/*
程序运行到此处
需要查找后继节点是否为空
第一个被查找的节点仍然是最左下角的节点
右指针指向父节点
*/
while (treeNode.rightType == 1) {
treeNode = treeNode.rightNode;
System.out.println(treeNode);
}
/*
程序运行到此处
说明遇到了不等于1的节点
这时候直接让这个节点指向下一个节点
如果是第1次执行这个代码
当前treeNode指向的就是最左下角的父节点
*/
treeNode = treeNode.rightNode;
}
}