一:红黑树
为什么要引入红黑树:
因为我们前面讲到的AVL树对于平衡有着绝对的追求,它要求树高度差的绝对值要小于等于一,所以需要经常进行旋转,但是旋转会很消耗时间,所以即使AVL树的查找时间更快,我们依然引入了更加均衡的红黑树
红黑树的性质:
1,每个节点只能是红色或者黑色
2,根节点一定是黑色的
3,所有叶子节点(NIL节点)都是黑色的
4,红色节点不能连续出现
5,根节点到叶子节点的路径上黑色节点的个数一样
根据这五条性质,可以总结出来:最长路径是最短路径的两倍(最短就是全部是黑色节点,最长路径就是红黑交替)
思考:如果一棵红黑树中黑色节点的个数是X,并且总共有N个节点,那么N的范围是什么
答:N的范围是【X,2X】,那么根据这个我们也可以推断出来:最短路径的时间复杂度是logX,最长路径的时间复杂度是log2X,log2X约等于logX+1,根据时间复杂度的性质,我们也可以认定它的时间复杂度也是logX,所以不管N是X还是2X,我们的时间复杂度都是logN
红黑树的插入与检验:
前提:我们认定新插入的节点是红的,因为根据性质5,我们需要黑色个数一样,所以可能会插入一些我们不需要的黑色节点来满足性质5,所以我们默认新插入的是红的,然后进行颜色调整
//红黑树
public class rbtree {
static class TreeNode{
public TreeNode left;
public TreeNode right;
public TreeNode parent;
public color color;
public int val;
public TreeNode(int val){
this.val=val;
//默认是红色
this.color=color.RED;
}
}
public TreeNode root;
public boolean insert(int val){
TreeNode treeNode=new TreeNode(val);
//先正常插入
if(root==null){
root=treeNode;
root.color=color.BLACK;
return true;
}
//正常插入的时候定义了两个新变量,一个是p(parent),一个是cur
TreeNode p=null;
TreeNode cur=root;
while(cur!=null){
if (val>cur.val){
p=cur;
cur=cur.right;
} else if (val==cur.val) {
return false;
} else {
p=cur;
cur=cur.left;
}
}
//走完这里之后p走到了我们要放的地方的父亲节点,cur走到了空
if (val>p.val){
p.right=treeNode;
}else {
p.left=treeNode;
}
treeNode.parent=p;
cur=treeNode;
//插入完之后,要判断是否满足红黑树的性质,不满足的话进行旋转和变色
while(p!=null && p.color==color.RED){
//先把grandpa节点定义出来
TreeNode grandpa=p.parent;
if (p==grandpa.left){
//再定义一个uncle
TreeNode uncle=grandpa.right;
//uncle存在并且是红色
if(uncle!=null && uncle.color==color.RED){
p.color=color.BLACK;
uncle.color=color.BLACK;
grandpa.color=color.RED;
//并且继续向上调整
cur=grandpa;
p=cur.parent;
}else {
//uncle不存在或者是黑色
//情况三先写
if(cur==p.right){
//先左旋p
RotateL(p);
//再改变指向
TreeNode tmp=p;
p=cur;
cur=tmp;
}
//情况二,因为没有else,所以怎么都会走到这里
RotateR(grandpa);
//变色
p.color=color.BLACK;
grandpa.color=color.RED;
}
}else {
//cur.parent==grandpa.right)
//再定义一个uncle
TreeNode uncle=grandpa.left;
//uncle存在并且是红色
if(uncle!=null && uncle.color==color.RED){
p.color=color.BLACK;
uncle.color=color.BLACK;
grandpa.color=color.RED;
//并且继续向上调整
cur=grandpa;
p=cur.parent;
}else {
//uncle不存在或者是黑色
//uncle不存在或者是黑色
//情况三先写
if(cur==p.left){
//先左旋p
RotateR(p);
//再改变指向
TreeNode tmp=p;
p=cur;
cur=tmp;
}
//情况二,因为没有else,所以怎么都会走到这里
RotateL(grandpa);
//变色
p.color=color.BLACK;
grandpa.color=color.RED;
}
}
}
root.color=color.BLACK;
return true;
}
private void RotateL(TreeNode p) {
TreeNode subR=p.right;
TreeNode subRL=subR.left;
subR.left=p;
p.right=subRL;
//开始指向父亲节点
if (subRL!=null){
subRL.parent=p;
}
p.parent=subR;
TreeNode Pp=p.parent;
if(p==root) {
root = subR;
subR.parent = null;
}else {
if(Pp.left==p){
Pp.left=subR;
}else {
Pp.right=subR;
}
subR.parent=Pp;
}
}
//右旋
private void RotateR(TreeNode p) {
TreeNode subL=p.left;
TreeNode subLR=subL.right;
//subLR可能是空的
//开始旋转
//先指向子结点
subL.right=p;
p.left=subLR;
//再指向父结点
if(subLR!=null){
subLR.parent=p;
}
TreeNode Pp=p.parent;
p.parent=subL;
//可能原来的p节点并不是根结点
if(p==root){
root=subL;
subL.parent=null;
}else {
if (Pp.left==p){
Pp.left=subL;
}else {
Pp.right=subL;
}
subL.parent=Pp;
}
}
//检查是否是红黑树
public boolean check(TreeNode root){
if (root==null){
return true;
}
//先看根节点是不是黑色的
if (root.color==color.RED){
System.out.println("违反了根节点是黑色的这个性质!!");
return false;
}
//再看是否有两个连续的红色节点以及黑色节点的个数
int pathNum=0;
TreeNode cur = root;
while (cur!=null){
if (cur.color==color.BLACK){
pathNum++;
}
cur=cur.left;
}
return CheckRedColor(root) && CheckBlackNum(root,pathNum,0);
}
public boolean CheckRedColor(TreeNode root){
if (root==null) {
return true;
}
if(root.color == color.RED) {
TreeNode parent = root.parent;
if(parent.color == color.RED) {
System.out.println("违反了性质:连续出现了两个红色的节点");
return false;
}
}
return CheckRedColor(root.left) && CheckRedColor(root.right);
}
//pathNum是事先记录好的黑色的个数
public boolean CheckBlackNum(TreeNode root,int pathNum,int blackNum){
if (root==null) {
return true;
}
if (root.color==color.BLACK){
blackNum++;
}
//在root达到叶子节点的时候进行了判断
if (root.left==null && root.right==null){
if (blackNum!=pathNum){
System.out.println("违反了性质:黑色节点个数不一致");
return false;
}
}
return CheckBlackNum(root.left,pathNum,blackNum) &&
CheckBlackNum(root.right,pathNum,blackNum);
}
//中序遍历也可以证明是否是红黑树
public void inorder(TreeNode root) {
if(root == null) {
return;
}
inorder(root.left);
System.out.print(root.val+" ");
inorder(root.right);
}
}
public enum color {
RED,BLACK;
}
public class Test {
public static void main(String[] args) {
int[] array = {4, 2, 6, 1, 3, 5, 15, 7, 16,14};
rbtree rbTree = new rbtree();
for (int i = 0; i < array.length; i++) {
rbTree.insert(array[i]);
}
System.out.println(rbTree.check(rbTree.root));
rbTree.inorder(rbTree.root);
}
}

二:位图

为什么要引入位图:
当数据量很大的时候,比如我们给40亿个整数,判断某一个数字是否存在,40亿个整数大概是16个G,如果我们用数组来存储遍历会空间消耗很大,甚至运行不起来,所以我们引入了位图,将一个字节分成8个位,通过数字0或者1来表示当前数字是否存在,这样我们所需要的空间大概为500左右MB,并且查询速度很快,为O(1)
位图的相关方法实现:
我们放数据的时候只需要让原本对应elem[ArrayIndex]里的八位或上一个1左移对应的index位就好了,因为按位或会有1则1,就会将原来0的位置改为1,原来1的不变
我们查找数据的时候就要按位与,最后得出来的不是0就可以说明有这个数
我们重置的时候需要1左移index然后取反,其他1的位置还是1,想改的那个位置会改为0
import java.util.Arrays;
public class MyBitset {
public byte[] elem;
public int size;
public MyBitset(int size){
this.size = size;
elem = new byte[size/8 + 1];
}
//放元素
public void set(int val){
if (val<0){
throw new IllegalArgumentException("val<0");
}
//看在哪一个字节下
int ArrayIndex=val/8;
if (ArrayIndex>=elem.length){
Arrays.copyOf(elem,ArrayIndex*2);
}
//看在字节的哪一个位置
int index=val%8;
elem[ArrayIndex]|=(1<<index);
size++;
}
//查元素
public boolean get(int val){
if (val<0){
throw new IllegalArgumentException("val<0");
}
//看在哪一个字节下
int ArrayIndex=val/8;
//看在字节的哪一个位置
int index=val%8;
if ((elem[ArrayIndex]&(1<<index))!=0){
return true;
}
return false;
}
//重置某位
public void reset(int val){
if (val<0){
throw new IllegalArgumentException("val<0");
}
//看在哪一个字节下
int ArrayIndex=val/8;
//看在字节的哪一个位置
int index=val%8;
elem[ArrayIndex]&=~(1<<index);
size--;
}
}
public class Test {
public static void main(String[] args) {
int[] array = {1,3,2,13,10,3,14,18,3};
MyBitset myBitSet = new MyBitset(18);
for (int i = 0; i < array.length; i++) {
myBitSet.set(array[i]);
}
for (int i = 0; i < myBitSet.elem.length; i++) {
for (int j = 0; j < 8; j++) {
if((myBitSet.elem[i] & (1 << j) ) != 0 ) {
System.out.print(i*8+j+" ");
}
}
}
}
}
313



被折叠的 条评论
为什么被折叠?



