树结构-基础部分
文章目录
介绍:
数组的特点总结一句话就是: 查快增慢
插播:ArrayList源码分析
通过名字我们就可以发现其实是数组集合,首先有两种构造器,两种容器都是创建一个数组,只不过一个长度为0,一个长度为,这是在jdk8中的情况,jdk7种elementdata默认的是10,当我们再次添加的时候,首先有个判断,也就是grow方法,如果需要扩容就扩容到十倍,如果没有添加过一次数组那就先设置elementdata为10。
链表特点一句话:增快查慢
而树这种存储结构可以帮我们解决这些问题,他是 查快增快
树的介绍
二叉树
满二叉树就是说,节点总数是2的n次方-1,叶子节点都在最后一层;完全二叉树就是说,除了最后一层其他的度都为2,然后连续的最后一层在左边,右边是倒数第二层。
需求:
二叉树数据结构
谁掉用this,this就只想谁,下面开始代码演示:
public class BinaryTree {
public static void main(String[] args) {
TreeNode root = new TreeNode("1");
root.setLeft(new TreeNode("2"));
root.setRight(new TreeNode("3"));
root.qianxu();
}
}
class TreeNode{
private String no;
private TreeNode left;
private TreeNode right;
public TreeNode(String no) {
this.no = no;
}
public String getNo() {
return no;
}
public void setNo(String no) {
this.no = no;
}
public TreeNode getLeft() {
return left;
}
public void setLeft(TreeNode left) {
this.left = left;
}
public TreeNode getRight() {
return right;
}
public void setRight(TreeNode right) {
this.right = right;
}
@Override
public String toString() {
return "TreeNode{" +
"no='" + no + '\'' +
'}';
}
public void qianxu(){
//输出父节点
System.out.println(this);
if(this==null){
System.out.println("此节点为空");
}
if(this.left!=null){
this.left.qianxu();
}
if(this.right!=null){
this.right.qianxu();
}
}
}
这里就写了一个前序遍历,其他两个都是一个道理哈, 记住一句话this的调用着就是操作者
这一点跟线程的道理是一样的,线程的调用者也就是操作者
查找指定节点
public class BinaryTree {
public static void main(String[] args) {
TreeNode root = new TreeNode("1");
root.setLeft(new TreeNode("2"));
root.setRight(new TreeNode("3"));
root.qianxu();
TreeNode result = root.search("3");
System.out.println("数据前序查找的结果为"+result);
TreeNode result2 = root.searchMid("3");
System.out.println("数据中序查找的结果为"+result2);
}
}
class TreeNode{
private String no;
private TreeNode left;
private TreeNode right;
public TreeNode(String no) {
this.no = no;
}
public String getNo() {
return no;
}
public void setNo(String no) {
this.no = no;
}
public TreeNode getLeft() {
return left;
}
public void setLeft(TreeNode left) {
this.left = left;
}
public TreeNode getRight() {
return right;
}
public void setRight(TreeNode right) {
this.right = right;
}
@Override
public String toString() {
return "TreeNode{" +
"no='" + no + '\'' +
'}';
}
//前序查找
public TreeNode search(String no){
if (this.no==no){
return this;
}
TreeNode node = null;
if (this.left!=null){
node = this.left.search(no);
}
//判断是否是左节点
if (node!=null){
return node;
}
if (this.right!=null){
node=this.right.search(no);
}
return node;
}
//中序查找
public TreeNode searchMid(String no){
TreeNode node=null;
if (this.left!=null){
node = this.left.searchMid(no);
}
if (node!=null){
return node;
}
//判断中间节点
if (this.no==no){
return this;
}
if (this.right!=null){
node = this.right.searchMid(no);
}
return node;
}
}
删除节点
public void delete(String 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.delete(no);
}
if (this.right!=null){
this.right.delete(no);
}
}
顺序存储二叉树
例子:
public class ArrayBinaryTree {
public static void main(String[] args) {
int[] arr={1,2,3,4,5,6,7};
new ArrayTree(arr).list(0);
}
}
class ArrayTree{
private int[] arr;
public ArrayTree(int[] arr) {
this.arr = arr;
}
public void list(int index){
if (index<0||index>=arr.length){
System.out.println("不行,index越界");
}
System.out.print(arr[index]+"\t");
//分别再去比较左右节点
if ((index*2+1)<arr.length){
list(index*2+1);
}
if ((index*2+2)<arr.length){
list(index*2+2);
}
}
}
线索化二叉树
二叉排序树
public class BinarySortTree {
public static void main(String[] args) {
int[] arr={7,3,10,12,5,3,9};
Nodes head = new Nodes(0);
for (int data : arr) {
head.add(new Nodes(data));
}
head.zhongxu();
}
}
class Nodes{
int value;
Nodes left;
Nodes right;
public Nodes(int value) {
this.value = value;
}
@Override
public String toString() {
return "Nodes{" +
"value=" + value +
'}';
}
public void add(Nodes node){
if(node==null){
return ;
}
if(node.value<this.value){
if(this.left==null){
this.left=node;
}else {
this.left.add(node);
}
}else {
if(this.right==null){
this.right=node;
}else {
this.right.add(node);
}
}
}
public void zhongxu(){
if (this.left!=null){
this.left.zhongxu();
}
System.out.print(this.value+"\t");
if (this.right!=null){
this.right.zhongxu();
}
}
}
思想就是每次进去的时候判断,如果小就继续往左边地推,如果大就继续往右边地推,然后找到位置了之后程序就回溯执行完毕!无返回值!
//查找待删除节点
public Nodes search(int value) {
if (this.value == value) {
return this;
} else if (value < this.value) {
if (this.left != null) {
//这里注意查找到的值一定要回溯回去
return this.left.search(value);
} else {
return null;
}
} else {
if (this.right != null) {
return this.right.search(value);
} else {
return null;
}
}
}
//查找待删除节点父节点
public Nodes searchParent(int value) {
//这里注意顺序一定是先判断是否为空,再判断是否等于value,不然就会报空指针异常
if ((this.left != null && this.left.value == value) ||
(this.right != null && this.right.value == value)) {
return this;
} else {
if (value < this.value && this.left != null) {
return this.left.searchParent(value);
} else if (value >= this.value && this.right != null) {
return this.right.searchParent(value);
} else {
return null;
}
}
}
public int delMinNodes(Nodes nodes) {
Nodes target = nodes;
while (target.left != null) {
target = target.left;
}
deleteNodes(target.value);
//这里的target是一个辅助指针,所以实际上删除的是真实树的节点
//这里可以直接返回
return target.value;
}
public void deleteNodes(int value) {
Nodes targetNode = search(value);
Nodes parentNode = searchParent(value);
if (targetNode == null) {
return;
}
if (parentNode.left == null && parentNode.right == null) {
return;
}
//如果是叶子节点的情况
if (targetNode.left == null && targetNode.right == null) {
if (parentNode.left != null && parentNode.left.value == value) {
parentNode.left = null;
} else if (parentNode.right != null && parentNode.right.value == value) {
parentNode.right = null;
}
} else if (targetNode.left != null && targetNode.right != null) {//有两颗子树的情况
//这里解释一下,节点的右子树不管还有多少课子树,右子树所有节点的value都要小于该节点的value
int minValue = delMinNodes(targetNode.right);
targetNode.value = minValue;
} else {//只有一棵子树的情况
if (targetNode.left != null) {
if (parentNode!=null){
if (parentNode.left.value == value) {
parentNode.left = targetNode.left;
} else {
parentNode.right = targetNode.left;
}
}else{
targetNode = targetNode.left;
}
} else {
if (parentNode!=null) {
if (parentNode.left.value == value) {
parentNode.left = targetNode.right;
} else {
parentNode.right = targetNode.right;
}
}else{
targetNode=targetNode.right;
}
}
}
}
平衡二叉树(AVL树)
二叉排序树存在的问题:
左旋转
右旋转
package datastructres.tree;
/**
* @author :Yan Guang
* @date :Created in 2021/2/9 10:11
* @description:
*/
public class AVLTree {
public static void main(String[] args) {
int[] arr={3,6,5,7,8};
Nodess nodess = new Nodess(4);
for (int data : arr) {
nodess.add(new Nodess(data));
}
nodess.zhongxu();
System.out.println();
System.out.println(nodess.height());
System.out.println(nodess.leftHeight());
System.out.println(nodess.rightHeight());
}
}
class Nodess {
int value;
Nodess left;
Nodess right;
public Nodess(int value) {
this.value = value;
}
public Nodess() {
}
@Override
public String toString() {
return "Nodess{" +
"value=" + value +
'}';
}
//返回左子树的高度
public int leftHeight(){
if (left==null){
return 0;
}
return left.height();
}
//返回右子树的高度
public int rightHeight(){
if (right==null){
return 0;
}
return right.height();
}
//返回当前节点的高度
public int height() {
return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
}
//左旋转
private void leftRotate(){
Nodess newNode = new Nodess(value);
newNode.left=left;
newNode.right=right.left;
value=right.value;
right=right.right;
left=newNode;
}
//右旋转
private void rightRotate(){
Nodess newNode =new Nodess(value);
newNode.right=right;
newNode.left=left.right;
value=left.value;
left=left.left;
right=newNode;
}
//查找待删除节点
public Nodess search(int value) {
if (this.value == value) {
return this;
} else if (value < this.value) {
if (this.left != null) {
//这里注意查找到的值一定要回溯回去
return this.left.search(value);
} else {
return null;
}
} else {
if (this.right != null) {
return this.right.search(value);
} else {
return null;
}
}
}
//查找待删除节点父节点
public Nodess searchParent(int value) {
//这里注意顺序一定是先判断是否为空,再判断是否等于value,不然就会报空指针异常
if ((this.left != null && this.left.value == value) ||
(this.right != null && this.right.value == value)) {
return this;
} else {
if (value < this.value && this.left != null) {
return this.left.searchParent(value);
} else if (value >= this.value && this.right != null) {
return this.right.searchParent(value);
} else {
return null;
}
}
}
public int delMinNodess(Nodess Nodess) {
Nodess target = Nodess;
while (target.left != null) {
target = target.left;
}
deleteNodess(target.value);
//这里的target是一个辅助指针,所以实际上删除的是真实树的节点
//这里可以直接返回
return target.value;
}
public void deleteNodess(int value) {
Nodess targetNode = search(value);
Nodess parentNode = searchParent(value);
if (targetNode == null) {
return;
}
if (parentNode.left == null && parentNode.right == null) {
return;
}
//如果是叶子节点的情况
if (targetNode.left == null && targetNode.right == null) {
if (parentNode.left != null && parentNode.left.value == value) {
parentNode.left = null;
} else if (parentNode.right != null && parentNode.right.value == value) {
parentNode.right = null;
}
} else if (targetNode.left != null && targetNode.right != null) {//有两颗子树的情况
//这里解释一下,节点的右子树不管还有多少课子树,右子树所有节点的value都要小于该节点的value
int minValue = delMinNodess(targetNode.right);
targetNode.value = minValue;
} else {//只有一棵子树的情况
if (targetNode.left != null) {
if (parentNode != null) {
if (parentNode.left.value == value) {
parentNode.left = targetNode.left;
} else {
parentNode.right = targetNode.left;
}
} else {
targetNode = targetNode.left;
}
} else {
if (parentNode != null) {
if (parentNode.left.value == value) {
parentNode.left = targetNode.right;
} else {
parentNode.right = targetNode.right;
}
} else {
targetNode = targetNode.right;
}
}
}
}
public void add(Nodess node) {
if (node == null) {
return;
}
if (node.value < this.value) {
if (this.left == null) {
this.left = node;
} else {
this.left.add(node);
}
} else {
if (this.right == null) {
this.right = node;
} else {
this.right.add(node);
}
}
//右边高,左旋转,左边高,右旋转
if ((rightHeight()-leftHeight())>1){
leftRotate();
}
if ((leftHeight()-rightHeight())>1){
rightRotate();
}
}
public void zhongxu() {
if (this.left != null) {
this.left.zhongxu();
}
System.out.print(this.value + "\t");
if (this.right != null) {
this.right.zhongxu();
}
}
}
//右边高,左旋转,左边高,右旋转
//左旋转
if ((rightHeight()-leftHeight())>1){
if (right!=null&&right.leftHeight()>right.rightHeight()){
right.rightRotate();
leftRotate();
}else {
leftRotate();
}
}
//右旋转
if ((leftHeight()-rightHeight())>1){
if (left!=null&&left.rightHeight()>left.leftHeight()){
left.leftRotate();
rightRotate();
}else {
rightRotate();
}
}
多路查找树
B树
2-3树
这里比较繁琐,但是其实就是需要满足2-3树的要求即可叶子节点的高度必须相同,而且节点必须要满度
2-3-4树
B+树
B*树
树结构的应用
堆排序
思想:
public class HeapSort {
public static void main(String[] args) {
int arr[] = {4,6,8,5,9};
//1、我们首先把普通数组转换成为一个大顶堆
for (int i=arr.length/2-1;i>=0;i--){
adjustHeap(arr,i,arr.length);
}
//2、每次将大顶堆元素放到数组最后,放完之后重新把剩下的元素调为大顶堆
//这里因为每次调整之后其实只是把最大的放到数组尾部就可以了,所以其实并不用每次都转换成一个大顶堆
//只用把最大的数放到数组的末部就可以了,可以画草图理解
for (int i=arr.length-1;i>0;i--){
int temp = arr[i];
arr[i]=arr[0];
arr[0]=temp;
adjustHeap(arr,0,i);
}
System.out.println(Arrays.toString(arr));
}
public static void adjustHeap(int arr[], int i, int length) {
int temp = arr[i];
//把数据最大的值放到堆顶
for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {
if (arr[k] < arr[k + 1] && k + 1 < length) {
k++;
}
if (arr[k] > temp) {
arr[i] = arr[k];
i = k;
} else {
break;
}
arr[k] = temp;
}
}
}
堆排序分两种,如果是要改为升序就用大顶堆,降序都用小顶堆,思路都差不多,这里比方是用升序,就一开始把最大的数放到堆顶,然后每次再交换到数组末端就可以了!
堆排序 速度极快!!!
哈夫曼树/赫夫曼树
例子:
WLP的值越小的就是哈夫曼树
创建结果如下,就是每次把组合先求和,小的放左边,大的放右边
public class Huffman {
public static void main(String[] args) {
int[] arr = {13,7,8,3,29,6,1};
Node head = createHuffman(arr);
head.preOrder();
}
//创建哈夫曼树
public static Node createHuffman(int[] arr){
ArrayList<Node> list = new ArrayList<>();
for (int data : arr) {
list.add(new Node(data));
}
//因为当最后一个数存入list的时候其实就已经结束了
while(list.size()>1) {
//将list进行排序
Collections.sort(list);
//开始构建新的树
Node liftNode = list.get(0);
Node rightNode = list.get(1);
Node parent = new Node(liftNode.val + rightNode.val);
parent.left = liftNode;
parent.right = rightNode;
//删除list中已经用过的节点
list.remove(liftNode);
list.remove(rightNode);
//将parent加入节点
list.add(parent);
}
//因为此时list中只存在最后一个最大的权的头结点了,于是返回就可以了
return list.get(0);
}
}
class Node implements Comparable<Node>{
int val;
Node left;
Node right;
public Node(int val) {
this.val = val;
}
@Override
public String toString() {
return "Node{" +
"val=" + val +
'}';
}
@Override
public int compareTo(Node o) {
return this.val-o.val;
}
//前序遍历
public void preOrder(){
System.out.print(this.val+"\t");
if (this.left!=null){
this.left.preOrder();
}
if (this.right!=null){
this.right.preOrder();
}
}
}
哈夫曼树的一个创建可以说是十分简单的啊,就是一个写一个节点,然后自己递归随便先写一个遍历的方式,然后可以开始写代码了,总过就是用一个while循环就可以搞定了啊,一开始先把数组封装成一个Node并且按照降序排列的list,然后每次就把list最前面的0和1取出然后再新创建一个parent节点,然后删除两个利用过得节点再list中,最后再把创建的节点直接压入list就可以了!!!记得最后list中会留一个头节点,也是我们退出循环的条件!!!