比较器和二叉树
比较器
- 所谓的比较器就是我们进行大小关系的一个判断!
- 比较器还能对对象数组进行排序,但是排序需要用到比较器的。
- 而其中数组的Arrays类里面的sort方法就可以进行排序,但是想要进行sort方法进行排序,必须使用的类里面要完成比较器的比较方法,才可以使用sort排序,否则就会报错。
- 就是要实现Comparable接口里面的compareTo方法,代码如下:
import java.lang.reflect.Array;
import java.util.Arrays;
class Person implements Comparable<Person>{
private String name;
private int age;
public Person(String name,int age){
this.age = age;
this.name = name;
}
@Override
public String toString(){
return "【person对象】姓名:" + this.name + "、年龄:" + this.age;
}
@Override
public int compareTo(Person o) {
return this.age - o.age;
}
}
public class 比较器练习 {
public static void main(String[] args) {
Person [] ans = new Person[]{
new Person("小明",100),
new Person("小红",80),
new Person("小白",60),
new Person("小黑",20)
};
Arrays.sort(ans);
System.out.println(Arrays.toString(ans));
}
}
- 我们可以知道的是,如果Person类没有对compareTo方法进行实现的话,主函数里面的对象数组是无法完成排序的。
- 所以,如果以后遇到了对象数组排序,那么一定要使用Comparable接口来实现排序;
Comparator
- 属于一种挽救的排序功能,在后期无法对类进行修改的时候,可以使用;
- 可利用这个类进行排序规则的定义!
- 代码如下:
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Comparator;
class PersonComparator implements Comparator<Person>{
@Override
public int compare(Person person1, Person person2) {
return person1.getAge() - person2.getAge();
}
}
class Person {
private String name;
private int age;
public Person(String name,int age){
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
@Override
public String toString(){
return "【person对象】姓名:" + this.name + "、年龄:" + this.age + "\n";
}
}
public class 比较器练习 {
public static void main(String[] args) {
Person [] ans = new Person[]{
new Person("小明",100),
new Person("小红",80),
new Person("小白",60),
new Person("小黑",20)
};
Arrays.sort(ans,new PersonComparator());
System.out.println(Arrays.toString(ans));
}
}
- 这就是挽救型的排序比较;
- 但是呢,这边建议,如果不是必须的情况下,还是使用第一种Comparable接口会比较好一点。
面试题:请解释Comparable与Comparator两个接口的区别?
- java.lang.Comparable是在类定义的时候实现的父接口,主要用于比较排序定义排序规则,里面也只有一个compareTo方法。
- java.util.Comparator是挽救型的比较器操作,需要设置单独的比较器规则类实现排序里面有compare方法。
二叉树结构
- 在进行链表结构学习的时候呢,对于每一种操作他们的复杂度都是O(n),那么在30以内的时候还能接受,但是当里面的数据变得很大的时候,那么复杂度就会直线上升。所以,我们只能够换数据结构,那么二叉树就是解决问题的方法,他可以将复杂度调至到O(logn)。
- 这个我们还是可以接受的。
- 那么二叉树的实现原理如下:
取第一个数据为保存的根节点,小于或等于根节点的节点要放在根节点的左子树,而大于节点的数据,要放在该节点的右子树。
- 那么实现二叉树的处理中最为关键的就是数据的保存,那么数据的保存一定要有比较器的支持,而且比较器的我们首选的应该是Comparable接口的实现。
二叉树的实现
import java.util.Arrays;
class BinaryTree <T extends Comparable<T>> {
private class Node {
private Comparable<T> data; //存放Comparable,可以进行比较
private Node parent;
private Node right;
private Node left;
public Node(Comparable<T> data){ //构造方法直接进行数据的存储
this.data = data;
}
// Node内部类增加节点addNode方法
public void addNode(Node newNode){
if(newNode.data.compareTo( (T)this.data ) <= 0){
if(this.left == null){
this.left = newNode;
newNode.parent = this;
}else{
this.left.addNode(newNode);
}
}else{
if(this.right == null){
this.right = newNode;
newNode.parent = this;
}else{
this.right.addNode(newNode);
}
}
}
// Node内部类数据转换成数组toArrayNode方法
public void toArrayNode(){
if(this.left != null){
this.left.toArrayNode();
}
BinaryTree.this.returnData[BinaryTree.this.foot ++ ] = this.data;
if(this.right != null){
this.right.toArrayNode();
}
}
}
/* -------------- 以下是二叉树的的功能实现 ------------ */
private Node root; //保存的根节点
private int count; //保存数据的个数
private int foot = 0;
private Object [] returnData;
public void add(Comparable<T> data){
if(data == null){
throw new NullPointerException("保存的数据不允许为空");
}
Node newNode = new Node(data);
if(this.root == null){
this.root = newNode;
}else{
this.root.addNode(newNode);
}
this.count ++ ;
}
public Object [] toArray(){
if(this.count == 0){
return null;
}
this.returnData = new Object[this.count];
this.foot = 0;
this.root.toArrayNode();
return returnData;
}
}
public class 二叉树练习 {
public static void main(String[] args) {
BinaryTree<Person> tree = new BinaryTree<Person>();
tree.add(new Person("小黑",20));
tree.add(new Person("小红",30));
tree.add(new Person("小白",40));
tree.add(new Person("小绿",60));
tree.add(new Person("小蓝",50));
tree.add(new Person("小灰",100));
System.out.println(Arrays.toString(tree.toArray()));
}
}
- 我们采用内部类的i形式,就可以完成了二叉数的基本实现了,二叉数在保存完节点以后,在内部里面就会完成排序。所以输出的就是有序的。
二叉树的数据删除
二叉树的数据删除是非常复杂的,因为考虑的东西挺多的,如下:
- 情况1 :如果删除的节点没有子节点,那么就可以直接删除。
- 情况2 :如果待删除的节点只有一个子节点,那么直接删除,用它的子节点去替换它。
- 但是这里要考虑两种情况,一个是这个唯一的子节点是左子节点,还有就是右子节点。但是删除的方式相同
- 情况3 :如果删除的节点里面有两个子节点,先直接删除节点,然后用其右节点的最后的左节点进行顶包。
- 这就是删除数据的三种情况了,我们下面进行代码完成:
import jdk.nashorn.internal.ir.BinaryNode;
import java.util.Arrays;
class BinaryTree <T extends Comparable<T>> {
private class Node {
private Comparable<T> data; //存放Comparable,可以进行比较
private Node parent;
private Node right;
private Node left;
public Node(Comparable<T> data) { //构造方法直接进行数据的存储
this.data = data;
}
// Node内部类增加节点addNode方法
public void addNode(Node newNode) {
if (newNode.data.compareTo((T) this.data) <= 0) {
if (this.left == null) {
this.left = newNode;
newNode.parent = this;
} else {
this.left.addNode(newNode);
}
} else {
if (this.right == null) {
this.right = newNode;
newNode.parent = this;
} else {
this.right.addNode(newNode);
}
}
}
// Node内部类数据转换成数组toArrayNode方法
public void toArrayNode() {
if (this.left != null) {
this.left.toArrayNode();
}
BinaryTree.this.returnData[BinaryTree.this.foot++] = this.data;
if (this.right != null) {
this.right.toArrayNode();
}
}
//Node内部类判断数据是否存在 (可以省略)
public boolean containsNode(Comparable<T> data) {
if(data.compareTo((T)this.data) == 0){
return true;
}else if(data.compareTo((T)this.data) < 0){
if(this.left != null){
return this.left.containsNode(data);
}else {
return false;
}
}else {
if(this.right != null){
return this.right.containsNode(data);
}else{
return false;
}
}
}
//Node类,返回查找到的节点
public Node getRemoveNode(Comparable<T> data){
if(data.compareTo((T)this.data) == 0){
return this;
}else if(data.compareTo((T)this.data) < 0){
if(this.left != null){
return this.left.getRemoveNode(data);
}else{
return null;
}
}else {
if(this.right != null){
return this.right.getRemoveNode(data);
}else{
return null;
}
}
}
}
/* -------------- 以下是二叉树的的功能实现 ------------ */
private Node root; //保存的根节点
private int count; //保存数据的个数
private int foot = 0;
private Object [] returnData;
//BinaryTree类的增加数据方法
public void add(Comparable<T> data){
if(data == null){
throw new NullPointerException("保存的数据不允许为空");
}
Node newNode = new Node(data);
if(this.root == null){
this.root = newNode;
}else{
this.root.addNode(newNode);
}
this.count ++ ;
}
//BinaryTree类的数据转换成数组方法
public Object [] toArray(){
if(this.count == 0){
return null;
}
this.returnData = new Object[this.count];
this.foot = 0;
this.root.toArrayNode();
return returnData;
}
//BinaryTree类判断数据是否存在
public boolean contains(Comparable<T> data){
if(this.count == 0)return false;
return this.root.getRemoveNode(data) != null;
}
//BinaryTree类返回二叉数的大小
public int getCount(){
return this.count;
}
//BinaryTree类的数据删除方法
public void remove(Comparable<T> data){
if(this.root == null){
return ;
}else{
if(this.root.data.compareTo((T)data) == 0){
Node moveNode = this.root.right;
while(moveNode.left != null){
moveNode = moveNode.left;
}
moveNode.left = this.root.left;
moveNode.right = this.root.right;
moveNode.parent.left = null;
this.root = moveNode;
this.count --;
}else{
Node removeNode = this.root.getRemoveNode(data);
if(removeNode != null){
this.count -- ;
//情况一
if(removeNode.left == null && removeNode.right ==null){
if(removeNode.parent.left != null && removeNode.data.compareTo((T)removeNode.parent.left.data) == 0){
removeNode.parent.left = null;
removeNode.parent = null;
}else{
removeNode.parent.right = null;
removeNode.parent = null;
}
}else if(removeNode.left != null && removeNode.right == null){ //情况二_1
if(removeNode.parent.left != null && removeNode.data.compareTo((T)removeNode.parent.left.data) == 0){
removeNode.parent.left = removeNode.left;
removeNode.left.parent = removeNode.parent;
removeNode.parent = null;
}else{
removeNode.parent.right = removeNode.left;
removeNode.left = removeNode.parent;
removeNode.parent = null;
}
}else if(removeNode.left == null && removeNode.right != null){ //情况二_2
if(removeNode.parent.left != null && removeNode.data.compareTo((T)removeNode.parent.left.data) == 0){
removeNode.parent.left = removeNode.right;
removeNode.right = removeNode.parent;
removeNode.parent = null;
}else{
removeNode.parent.right = removeNode.right;
removeNode.right = removeNode.parent;
removeNode.parent = null;
}
}else{
Node moveNode = removeNode.right;
while(moveNode.left != null){
moveNode = moveNode.left;
}
if(removeNode.data.compareTo((T)moveNode.parent.data) == 0){
moveNode.parent.right = moveNode.right;
}else{
moveNode.parent.left = moveNode.right;
}
//判断要删除的节点是在他父亲节点的左边还是右边
if(removeNode.parent.left != null && removeNode.parent.left.data.compareTo((T)removeNode.data) == 0){
removeNode.parent.left = moveNode;
}else{
removeNode.parent.right = moveNode;
}
moveNode.right = removeNode.right;
moveNode.left = removeNode.left;
}
}
}
}
}
}
public class 二叉树练习 {
public static void main(String[] args) {
BinaryTree<Person> tree = new BinaryTree<Person>();
Person ans1 = new Person("小白",80);
Person ans2 = new Person("小黑",50);
Person ans3 = new Person("小红",90);
Person ans4 = new Person("小绿",10);
Person ans5 = new Person("小蓝",60);
Person ans6 = new Person("小灰",85);
Person ans7 = new Person("aa",95);
tree.add(ans1);
tree.add(ans2);
tree.add(ans3);
tree.add(ans4);
tree.add(ans5);
tree.add(ans6);
tree.add(ans7);
tree.add(new Person("小青",55));
tree.add(new Person("小青",65));
tree.remove(new Person("xaio",50));
System.out.println(Arrays.toString(tree.toArray()));
System.out.println(tree.getCount());
}
}
- 说实在话,这个删除数据是真的把我搞哭了,搞了一个下午;
- 第三种情况,思维固化了,最最最主要的是,视频老师没有备课,导致出现了好多bug,最后还是自己把他撸出来了,害,太累了,我要去吐槽;
红黑树原理分析
- 这里重点理解,害,冲冲冲冲;
- 红黑树呢,在本质上面,他是又给二叉查找树,但是他在二叉查找树的原有基础上面添加了一个标记。同时具有一定的规则,这些规则使得二叉树,能够保存平衡。
- 那么他有什么特点呢,让我们来看看把,这可是很重要的,详细理解哦!!!
- 这个还是太负责了,下次单独列一篇写。咳咳。。。