技巧
鸽舍原理
如果每个抽屉代表一个集合,每一个苹果就可以代表一个元素,假如有 n+1或多于n+1个元素放到n个集合中去,其中必定至少有一个集合里至少有两个元素。
求中值索引避免越界:int mid = L + ((R - L) >> 1)
java中负数2进制以补码形式展示,10进制转换2进制,正数部分%2取反,小数部分*2.
n的子集->2的n次方
n的排列->n!
奇偶判断:(n&1)==1,为奇数
判断两个数字符号是否相等:((a >> 31) ^ (b >> 31)) == 0
优先级:单目乘除位关系,逻辑三目后赋值。
&&和||:具有短路特点
图的连通:DFS、BFS、UNION-FIND
查找排序算法效率比较?
算法
- 问题理解
- exact、approximation,数据结构
- 算法描述–伪代码
- 算法正确性证明
- 算法分析–时间空间效率
效率分析
- 输入规模度量
- 基本操作次数统计(对数级增长最慢)
- 最优、最差及平均效率
数值交换
寄存法,a与b交换,可能出现溢出
a=a+b
b=a-b
a=a-b
异或法,由a^a=0,a^0=a可以得出,a与b交换,a=a^a^b,b=b^b^a
a=a^b
b=a^b
a=a^b
数组左移、右移k位
颠倒数组,颠倒0~k位,颠倒k+1~n位
找出数组中只出现一次的元素
其余元素出现两次,遍历数组,num[i]^num[i+1]
1.欧几里得算法求最大公约数
(m,n)->(n,m%n)->(x,0)->x为最大公约数
最小公倍数=a*b/gcd(a,b)
public static int gcd(int m, int n){
while(n!=0){
int interim = m%n;
m=n;
n=interim;
}
return m;
}
2.埃拉托色尼筛选法
求不大于n的质数序列,消除从p*p~n
public static int[] sieve(int n){
//记录被消除个数
int count=0;
int[] nums = new int[n];
for(int i=2;i<=Math.sqrt(n);i++){
if(nums[i]==0){
for(int j=i*i;j<n;j+=i){
if(nums[j]==0){
nums[j]=1;
count++;
}
}
}
}
int[] result = new int[n-count-2];
count=0;
for(int i=2;i<n;i++){
if(nums[i]==0){
result[count++]=i;
}
}
return result;
}
蛮力法
3.选择排序—循环一次选择最大或最小交换
4.冒泡排序—比较并交换,一次循环确定最大或最小
5.顺序查找
可以选择将查找元素插入列表,避免每次判断是否到列表尾。
6.蛮力匹配字符串
public static String stringMatch(String str,String pattern){
String result="";
for(int i=0;i<str.length()-pattern.length();i++){
int j=0;
while(j<pattern.length()){
if(str.charAt(i+j)!=pattern.charAt(j)){
break;
}
j++;
}
if(j==pattern.length()){
return result+str.substring(i,i+j)+"-location:"+i+"~"+(i+j);
}
}
return result;
}
7.最近点对
//最近点对问题
public static void closestPair(int[][] points){
double distance = Double.MAX_VALUE;
int pointx=0,pointy=0;
for(int i=0;i<points.length;i++){
for(int j=i+1;j<points.length;j++){
double curDistance=Math.pow((points[i][0]-points[j][0]),2)+Math.pow((points[i][1]-points[j][1]),2);
if(curDistance<distance){
distance=curDistance;
pointx=i;
pointy=j;
}
}
}
System.out.println("最近点对为:(" + points[pointx][0] + "," + points[pointx][1] + ")--(" + points[pointy][0] + "," + points[pointy][1] + ")");
}
8.凸包问题
//凸包问题
public static void convexHull(int[][] points){
Set<Integer> set = new HashSet<Integer>();
for(int i=0;i<points.length;i++){
for(int j=i+1;j<points.length;j++){
//a=y2-y1,b=x1-x2,c=x1y2-x2y1,ax+by=c
int a=points[j][1]-points[i][1],b=points[i][0]-points[j][0],c=points[i][0]*points[j][1]-points[j][0]*points[i][1];
int judge=-1;
int k=0;
for(;k<points.length;k++){
//ax+by>c
if(a*points[k][0]+b*points[k][1]>c){
if(judge==-1){
judge=0;
}else if(judge==1){
break;
}
}else if(a*points[k][0]+b*points[k][1]<c){
if(judge==-1){
judge=1;
}else if(judge==0){
break;
}
}
}
//满足凸点线段
if(k==points.length){
set.add(i);
set.add(j);
}
}
}
System.out.println("凸点集合如下");
for(Integer point:set){
System.out.println("("+points[point][0]+","+points[point][1]+")");
}
}
生成数的排列
//判断交换元素之间是否存在重复元素
public static boolean judgeRepeat(int[] nums,int index,int repeat){
for(int i=index;i<repeat;i++){
if(nums[i]==nums[repeat]) {
return false;
}
}
return true;
}
//求排列--递归
public static void permutation1(int[] nums,int index){
if(index==nums.length){
System.out.println(Arrays.toString(nums));
count++;
return;
}
for(int i=index;i<nums.length;i++){
if(judgeRepeat(nums,index,i)) {
//交换
if(i!=index) {
nums[index] ^= nums[i];
nums[i] ^= nums[index];
nums[index] ^= nums[i];
}
permutation1(nums,index+1);
//复位
if(i!=index) {
nums[index] ^= nums[i];
nums[i] ^= nums[index];
nums[index] ^= nums[i];
}
}
}
}
//求排列--字典序
public static void permutation2(int[] nums){
Arrays.sort(nums);
while(nextPermutate(nums)){
System.out.println(Arrays.toString(nums));
count++;
}
}
public static boolean nextPermutate(int[] nums){
int point1=nums.length-1,point2=nums.length-1;
while(point1>0&&nums[point1]<=nums[point1-1]){
point1--;
}
//未找到递增序列
if(point1==0){
return false;
}else{
//存在递增序列就必定存在point2>point1-1的数
while(nums[point2]<=nums[point1-1]){
point2--;
}
//交换point1和point2
nums[point1-1]^=nums[point2];
nums[point2]^=nums[point1-1];
nums[point1-1]^=nums[point2];
//颠倒从point1到数组末尾的所有数
for(int i=point1,j=nums.length-1;i<j;i++,j--){
nums[i]^=nums[j];
nums[j]^=nums[i];
nums[i]^=nums[j];
}
return true;
}
}
生成数的组合
//求组合 参数:数组,当前递归层数,组合数,遍历数组开始索引,不能解决存在重复元素
public static void combination(int[] nums,int index,int number,int begin){
if(index==number){
for(int i=0;i<number;i++){
System.out.print(nums[i]+" ");
}
System.out.println();
count++;
return ;
}
for(int i=begin;i<nums.length;i++){
if(judgeRepeat(nums,index,i)) {
//交换
if (i != begin) {
nums[i] ^= nums[index];
nums[index] ^= nums[i];
nums[i] ^= nums[index];
}
combination(nums, index + 1, number, i + 1);
//复位
if (i != begin) {
nums[i] ^= nums[index];
nums[index] ^= nums[i];
nums[i] ^= nums[index];
}
}
}
}
生成数的子集
//求子集,不能解决存在重复元素
public static void subset1(int[] nums){
ArrayList<ArrayList<String>> result = new ArrayList<>();
//result初始要有一个空集
result.add(new ArrayList<>());
for(int i=0;i<nums.length;i++) {
//将当前元素与之前所有元素拼接形成新集合
for (int j = result.size() - 1; j >= 0; j--) {
ArrayList<String> list = new ArrayList<>(result.get(j));
list.add(String.valueOf(nums[i]));
result.add(list);
}
}
for(int i=0;i<result.size();i++){
System.out.println(Arrays.toString(result.get(i).toArray()));
}
}
//求子集---2进制方式,不能解决存在重复元素
public static void subset2(int[] nums){
long max=1<<nums.length;
ArrayList<ArrayList<String>> result =new ArrayList<>();
while(max>0){
ArrayList<String> list = new ArrayList<>();
long interim=max-1;
int index=0;
while(interim>0){
if((interim&1)>0){
list.add(String.valueOf(nums[index]));
}
interim>>=1;
index++;
}
result.add(list);
max--;
}
for(int i=0;i<result.size();i++){
System.out.println(Arrays.toString(result.get(i).toArray()));
}
}
旅行商问题
背包问题
分配问题
减治法
减常量
减系数因子
减规模
插入排序—对基本有序数组效率高
拓扑排序
//拓扑排序,减治法
public static void topologicalSort(int[][] matrix){
ArrayList<Integer> topological = new ArrayList<>();
//构建入度表
int[] indegree = new int[matrix.length];
for(int j=0;j<indegree.length;j++){
for(int i=0;i<indegree.length;i++){
if(matrix[i][j]!=0){
indegree[j]++;
}
}
}
LinkedList<Integer> queue= new LinkedList<>();
//将入度为零的节点加入队列
for(int i=0;i<indegree.length;i++){
if(indegree[i]==0){
queue.offer(i);
}
}
//将入度表为零的节点指向的节点入度减一
while(!queue.isEmpty()){
Integer node = queue.poll();
topological.add(node);
for(int i=0;i<indegree.length;i++){
if(matrix[node][i]!=0){
indegree[i]--;
if(indegree[i]==0){
queue.offer(i);
}
}
}
}
if(topological.size()!=indegree.length){
System.out.println("邻接矩阵中存在有向环不能进行拓扑排序!");
}else{
System.out.println("邻接矩阵拓扑排序为:"+Arrays.toString(topological.toArray()));
}
}
//拓扑排序,深度优先,通过标记数组控制
public static void topologicalSort2(int[][] matrix){
//标记数组
int[] flag = new int[matrix.length];
ArrayList<Integer> topological = new ArrayList<>();
//遍历每个节点
for(int i=0;i<flag.length;i++){
if(!topologicalDFS(matrix,flag,i,topological)){
System.out.println("邻接矩阵中存在有向环不能进行拓扑排序!");
return;
}
}
//颠倒
Collections.reverse(topological);
//遍历完成,显示拓扑排序
System.out.println("邻接矩阵拓扑排序为:"+Arrays.toString(topological.toArray()));
}
public static boolean topologicalDFS(int[][] matrix,int[] flag,int node,ArrayList<Integer> result){
//当前节点在当前递归中已经被访问过,表示存在回路
if(flag[node]==1){
return false;
}
//当前节点在其他节点递归中已经访问过,不再添加
if(flag[node]==-1){
return true;
}
flag[node]=1;
for(int i=0;i<flag.length;i++){
if(matrix[node][i]==1&&!topologicalDFS(matrix,flag,i,result)){
return false;
}
}
//当前节点循环结束,将节点加入拓扑排序中
result.add(node);
flag[node]=-1;
return true;
}
假币问题
俄罗斯乘法–用移位和加法处理
//俄罗斯乘法
public static int multiplication(int n,int m){
//n*m=n/2*2m 偶数,n*m=(n-1)/2*2m+m 奇数
int result=0;
while(n>0){
if((n&1)==1){
result+=m;
}
n>>=1;
m<<=1;
}
return result;
}
约瑟夫问题
//约瑟夫问题,排成一圈为m的去除
public static int joseph(int n,int m){
//公式f(n)=(f(n-1)+m-1)%n+1
int result=1;
for(int i=1;i<n;i++){
result=(result+m-1)%(i+1)+1;
}
return result;
}
减规模
求第k个最小元素
//利用快排思想找数组第k小的元素
public static int findMin(int[] nums,int k){
int left=0,right=nums.length-1;
int p=partition(nums,left,right);
while(p+1!=k){
if(p+1>k){
p=partition(nums,left,p-1);
}else{
p=partition(nums,p+1,right);
}
}
return nums[p];
}
public static int partition(int[]nums,int left,int right){
int count=left;
for(int i=left+1;i<=right;i++){
if(nums[i]<nums[count]){
nums[count]^=nums[i];
nums[i]^=nums[count];
nums[count]^=nums[i];
count=i;
}
}
return count;
}
插值查找
相比折半查找在大规模数据查找可能更好
//插值查找,利用两点式直线方程查找键值位置
public static int insertSearch(int[]nums,int value){
int left=0,right=nums.length-1;
//x=x1+[(v-y1)*(x2-x1)/(y2-y1)]
while(left<right){
int x=left+(int)Math.floor((value-nums[left])*(right-left)/(nums[right]-nums[left]));
if(x>right){
return -1;
}else if(nums[x]==value){
return x;
}else if(nums[x]>value){
right=x-1;
}else{
left=x+1;
}
}
return -1;
}
分治法
T(n)=aT(n/b)+f(n):分治时间效率=每个子问题求解花费时间+分解和合并子问题所花时间
合并排序
快速排序
大整数加减乘除
//大数加法
public static String bigDigitAdd(String var1,String var2){
if(var1==null||var2==null||var1.length()<1||var2.length()<1){
return null;
}
int carry=0;
StringBuilder sb = new StringBuilder();
int index1=var1.length()-1,index2=var2.length()-1;
for(;index1>=0||index2>=0;index1--,index2--){
int interim=carry;
interim=index1>=0?interim+var1.charAt(index1)-48:interim;
interim=index2>=0?interim+var2.charAt(index2)-48:interim;
if(interim>9){
carry=1;
sb.append((interim-10));
}else{
sb.append(interim);
carry=0;
}
}
if(carry==1){
sb.append(1);
}
return sb.reverse().toString();
}
//大数减法
public static String bigDigitSubtract(String var1,String var2){
if(var1==null||var2==null||var1.length()<1||var2.length()<1){
return null;
}
String max=var1.length()>=var2.length()?var1:var2;
String min=var1.length()<var2.length()?var1:var2;
int covering=0;
int index1=max.length()-1,index2=min.length()-1;
StringBuilder sb = new StringBuilder();
for(;index1>=0||index2>=0;index1--,index2--){
int interim=max.charAt(index1)-covering-48;
interim=index2>=0?interim-min.charAt(index2)+48:interim;
if(interim<0){
covering=1;
sb.append(interim+10);
}else{
if(index1==0&&interim==0){
break;
}
covering=0;
sb.append(interim);
}
}
return sb.reverse().toString();
}
//大数乘法,ab*cd=a*c+(a+b)*(c+d)-(a*c+b*d)+b*d,ac指数为2m,中间为m
public static String bigDigitMultiply(String var1,String var2){
if(var1==null||var2==null||var1.length()<1||var2.length()<1){
return null;
}
int len1=var1.length();
int len2=var2.length();
int maxLen=Math.max(len1,len2);
if(maxLen<4){
return String.valueOf(Integer.parseInt(var1)*Integer.parseInt(var2));
}
StringBuilder c1= new StringBuilder();
StringBuilder c2= new StringBuilder();
while(len1++<maxLen){
c1.append('0');
}
c1.append(var1);
while(len2++<maxLen){
c2.append('0');
}
c2.append(var2);
int mid = maxLen/2;
String AC=bigDigitMultiply(c1.substring(0,mid),c2.substring(0,mid));
String BD=bigDigitMultiply(c1.substring(mid),c2.substring(mid));
String ABCD=bigDigitMultiply(bigDigitAdd(c1.substring(0,mid),c1.substring(mid)),bigDigitAdd(c2.substring(0,mid),c2.substring(mid)));
String ACBD=bigDigitSubtract(ABCD,bigDigitAdd(AC,BD));
int carry1=(maxLen-mid)*2;
int carry2=maxLen-mid;
while(carry1-->0){
AC+="0";
}
while(carry2-->0){
ACBD+="0";
}
return bigDigitAdd(AC,bigDigitAdd(BD,ACBD));
}
//大数除法,对应位数相减
public static boolean compareStr(String var1,String var2){
if(var1.length()<var2.length()){
return false;
}else if(var1.length()==var2.length()){
return var1.compareTo(var2) >= 0;
}else{
return true;
}
}
public static String bigDigitDivide(String var1,String var2){
//被除数比除数小的情况
if(var1.length()<var2.length()){
return "0";
}else if(var1.length()==var2.length()&&var1.compareTo(var2)<0){
return "0";
}
int diff=var1.length()-var2.length();
String result="0";
while(diff>=0){
int addZero=diff;
String compare=var2;
int count=0;
while(addZero-->0){
compare+="0";
}
while(compareStr(var1,compare)){
var1=bigDigitSubtract(var1,compare);
count++;
}
if(count!=0){
StringBuilder curResult=new StringBuilder();
curResult.append(count);
for(int i=0;i<diff;i++){
curResult.append(0);
}
result=bigDigitAdd(result,curResult.toString());
}
diff--;
}
return result;
}
求平方根
//求平方根--牛顿迭代法
public static double mathSqrt(double x){
if(x<0){
return -1;
}
double preicse = 1e-8;
double root=x;
while(Math.abs(root-x/root)>preicse){
root=(root+x/root)/2;
}
return root;
}
二叉搜索树----插入与树形打印
class Node {
int data;
Node left;
Node right;
Node p;
public Node(int data, Node left, Node right, Node p) {
this.data = data;
this.left = left;
this.right = right;
this.p = p;
}
}
//插入
public void insert(Node x) {
if (root == null) {
root = x;
} else {
Node i = root;
Node j = i;
while (i != null) {
j = i;
if (i.data < x.data) {
i = i.right;
} else {
i = i.left;
}
}
x.p = j;
if (j.data < x.data) {
j.right = x;
} else {
j.left = x;
}
}
}
//树形打印
public void printTree(Node root){
if(root==null){
return;
}
//获得树的高度
int h = this.getHeight(root,0)+1;
//构建矩阵,存储树
String[][] matrix = new String[h][2<<(h-1)];
for(int i=0;i<matrix.length;i++){
for(int j=0;j<matrix[i].length;j++){
matrix[i][j]=" ";
}
}
//确认根位置,上一层位置,当前层起始位置
//使用队列广度优先遍历树
//如果树节点为空且在以h-1为高度的满二叉树范围内则插入一个临时节点用来构建矩阵
int baseP=(2<<(h-1))>>1;
int prevP=baseP;
int curP=prevP-(prevP>>1);
int layer=0;
LinkedList<Node> queue = new LinkedList<>();
queue.offer(root);
matrix[layer++][baseP]=String.valueOf(root.data);
while(!queue.isEmpty()){
if(curP>=(baseP<<1)){
prevP>>=1;
curP=prevP-(prevP>>1);
layer++;
}
Node node=queue.poll();
if(node.left!=null&&node.data!=-1){
queue.offer(node.left);
matrix[layer][curP]=String.valueOf(node.left.data);
}else{
if(layer<h-1) {
queue.offer(new Node(-1, null, null, null));
}
}
curP+=prevP;
if(node.right!=null&&node.data!=-1){
queue.offer(node.right);
matrix[layer][curP]=String.valueOf(node.right.data);
}else{
if(layer<h-1) {
queue.offer(new Node(-1, null, null, null));
}
}
curP+=prevP;
}
//打印树
for(int i=0;i<matrix.length;i++){
for(int j=0;j<matrix[i].length;j++){
System.out.print(matrix[i][j]);
}
System.out.println();
}
}
平衡二叉树插入查找
//节点定义
class Node{
int data;
Node left;
Node right;
Node p;
public Node(int data, Node left, Node right, Node p) {
this.data = data;
this.left = left;
this.right = right;
this.p = p;
}
}
//获得树高度,递归为null返回0
public int getHeight(Node node){
if(node==null){
return 0;
}
int l=1+getHeight(node.left);
int r=1+getHeight(node.right);
return Math.max(l,r);
}
//获得树左右子树高度差
public int getSubtractH(Node node){
return getHeight(node.left)-getHeight(node.right);
}
//获得从当前节点开始遍历的最大值
public Node findMax(Node node){
Node i=node;
Node j=i;
while(i!=null){
j=i;
i=i.right;
}
return j;
}
//获得从当前节点开始遍历的最小值
public Node findMin(Node node){
Node i=node;
Node j=i;
while(i!=null){
j=i;
i=i.left;
}
return j;
}
//四种旋转方式
//左逆旋转,传入节点为第一个不平衡节点
public void rotateL(Node node){
Node child = node.right;
node.right=child.left;
child.left=node;
child.p=node.p;
if(node.p==null){
root=child;
}else{
if(node.p.right==node) {
node.p.right = child;
}else{
//考虑左子树进行左逆旋转
node.p.left=child;
}
}
node.p=child;
}
//左逆右顺,左逆:不平衡节点的子节点与子节点的右节点交换,右顺:不平衡节点与子节点交换
public void rotateLR(Node node){
Node child = node.left;
Node right = child.right;
//左逆
child.right=right.left;
if(right.left!=null){
right.left.p=child;
}
right.left=child;
right.p=child.p;
child.p=right;
node.left=right;
//右顺
rotateR(node);
}
//右顺左逆
public void rotateRL(Node node){
//右顺
Node child = node.right;
Node left = child.left;
child.left=left.right;
if(left.right!=null){
left.right.p=child;
}
left.right=child;
left.p=child.p;
child.p=left;
node.right=left;
//左逆
rotateL(node);
}
//右顺旋转
public void rotateR(Node node){
Node child = node.left;
node.left=child.right;
child.right=node;
child.p=node.p;
if(node.p==null){
root=child;
}else{
if(node.p.left==node) {
node.p.left = child;
}else{
node.p.right = child;
}
}
node.p=child;
}
//重构平衡
public void reBuildTree(Node node){
//从插入节点开始回溯到第一个左右子树不平衡的根节点
Node i=node;
while(i!=null){
int factor=getSubtractH(i);
if(factor<=-2){
//插入节点在右子树的情况,rotateL或则rotateRL
if(getSubtractH(i.right)>0){
rotateRL(i);
}else{
rotateL(i);
}
break;
}else if(factor>=2){
//插入节点在左子树的情况,rotateR或rotateLR,同时考虑考虑删除时左右节点都存在
if(getSubtractH(i.left)>=0){
rotateR(i);
}else{
rotateLR(i);
}
break;
}
i=i.p;
}
}
//根据插入节点来平衡二叉树,从插入节点往上递归至第一个不平衡的父节点
public void insert(Node node){
if(root==null){
root=node;
}else{
Node i = root;
Node j = i;
while(i!=null){
j=i;
if(i.data<node.data){
i=i.right;
}else{
i=i.left;
}
}
node.p=j;
if(node.data<=j.data){
j.left=node;
}else{
j.right=node;
}
}
reBuildTree(node);
}
//删除时平衡二叉树,1.不存在子节点 2.存在一个子节点 3.存在两个子节点,重新平衡删除后的树
public void delete(Node node) {
if (node == null) {
System.out.println("删除节点错误!");
return;
}
Node p = node.p;
if (node.left == null && node.right == null) {
if(p==null){
root=null;
return ;
}
if (p.left == node) {
p.left = null;
} else {
p.right = null;
}
} else if (node.right == null) {
if(p==null){
root=node.left;
node.left.p=null;
return;
}else {
if (p.left == node) {
p.left = node.left;
node.left.p = p;
} else {
p.right = node.left;
node.left.p = p;
}
}
} else if (node.left == null) {
if(p==null){
root=node.right;
node.right.p=null;
return;
}else {
if (p.left == node) {
p.left = node.right;
node.right.p = p;
} else {
p.right = node.right;
node.right.p = p;
}
}
} else {
//同时存在,找到右节点中的最大值来代替删除节点
Node min = null;
if(getSubtractH(node)<0){
min=findMin(node.right);
if(min.p!=node) {
min.p.left = min.right;
}else{
node.right=min.right;
}
}else{
min=findMax(node.left);
if(min.p!=node) {
min.p.right = min.left;
}else{
node.left=min.left;
}
}
min.p=p;
if(p==null){
root=min;
min.left=node.left;
min.right=node.right;
if(node.left!=null) {
node.left.p = min;
}
if(node.right!=null) {
node.right.p = min;
}
return;
}else {
if (p.left == node) {
p.left = min;
min.left = node.left;
min.right = node.right;
if(node.left!=null) {
node.left.p = min;
}
if(node.right!=null) {
node.right.p = min;
}
} else {
p.right = min;
min.left = node.left;
min.right = node.right;
if(node.left!=null) {
node.left.p = min;
}
if(node.right!=null) {
node.right.p = min;
}
}
}
}
//不为根的节点判断删除后平衡
if(getSubtractH(p)>=2||getSubtractH(p)<=-2){
reBuildTree(p);
}
}
23树理解
//自底向上分裂
//2键转换为3键,3键将中键给父节点
红黑树
//节点定义
class Node{
int data;
Node p;
Node left;
Node right;
boolean red;
public Node(int data, Node p, Node left, Node right, boolean red) {
this.data = data;
this.p = p;
this.left = left;
this.right = right;
this.red = red;
}
}
/**
* @description 通过旋转和变色重新平衡红黑树
* @param node
*/
public void reBalance(Node node){
Node p = node.p;
//如果为根节点则将根节点置黑
if(p==null){
node.red=false;
return;
}
//如果父节点为黑不用重新平衡
//如果父节点为红,因为根节点始终为黑所以父节点为红必定存在祖父节点
if(p.red){
Node pp = p.p;
//如果父节点为祖父节点的左节点
if(p==pp.left){
//如果父节点的兄弟节点不为红则进行rotateR和rotateLR
if(pp.right==null||!pp.right.red){
//如果插入节点为左节点,对父节点进行右顺旋转
if(p.left==node){
rotateR(p);
}else{
//父节点左逆右顺旋转
rotateLR(p);
}
}else{
//如果父节点的兄弟节点为红,黑红红->红黑红,重新平衡祖父节点
p.red=false;
pp.right.red=false;
pp.red=true;
reBalance(pp);
}
}
//如果父节点为祖父节点的右节点
else{
//如果父节点的兄弟节点不为红则进行rotateL和rotateRL
if(pp.left==null||!pp.left.red){
//如果插入节点是右节点
if(p.right==node){
rotateL(p);
}else{
rotateRL(p);
}
}else{
//如果父节点的兄弟节点为红,黑红红->红黑红,重新平衡祖父节点
p.red=false;
pp.left.red=false;
pp.red=true;
reBalance(pp);
}
}
}
}
//插入节点及重新平衡
public void insert(Node node){
//根节点为空
if(root==null){
root=node;
root.red=false;
return;
}
Node i=root;
Node j=i;
while(i!=null){
j=i;
if(node.data>i.data){
i=i.right;
}else{
i=i.left;
}
}
node.p=j;
if(node.data>j.data){
j.right=node;
}else{
j.left=node;
}
reBalance(node);
}
//4中旋转方式
public void rotateR(Node node){
Node p = node.p;
//变色
p.red=true;
node.red=false;
//右旋
p.left=node.right;
if(node.right!=null){
node.right.p=p;
}
node.right=p;
node.p=p.p;
//如果父节点为根节点
if(p.p==null){
root=node;
}else{
if(p.p.left==p){
p.p.left=node;
}else{
p.p.right=node;
}
}
p.p=node;
}
public void rotateL(Node node){
Node p = node.p;
//变色
node.red=false;
p.red=true;
//左旋
p.right=node.left;
if(node.left!=null){
node.left.p=p;
}
node.left=p;
node.p=p.p;
if(p.p==null){
root=node;
}else{
if(p.p.right==p){
p.p.right=node;
}else{
p.p.left=node;
}
}
p.p=node;
}
public void rotateLR(Node node){
Node child = node.right;
//左逆旋转
node.right=child.left;
if(child.left!=null){
child.left.p=node;
}
child.left=node;
child.p=node.p;
node.p.left=child;
node.p=child;
rotateR(child);
}
public void rotateRL(Node node){
Node child = node.left;
//右顺旋转
node.left=child.right;
if(child.right!=null){
child.right.p=node;
}
child.right=node;
child.p=node.p;
node.p.right=child;
node.p=child;
//左逆
rotateL(child);
}
//删除
public void delete(Node node){
Node i = root;
while(i!=null){
if(i==node){
deleteReBalance(i);
return;
}else if(i.data>node.data){
i=i.left;
}else{
i=i.right;
}
}
}
//删除后平衡
public void deleteReBalance(Node node){
if(node.left!=null&&node.right!=null){
//左右节点都存在,取前驱或后继转换成只存在一个节点|不存在子节点
Node substitute=maxNode(node.left);
//交换删除节点和代替节点,将删除当前节点改为删除代替节点
node.data=substitute.data;
deleteReBalance(substitute);
}else if(node.left!=null||node.right!=null){
//根据红黑树性质,只有一个节点时只存在黑->红的情况,直接用子节点代替删除节点
node.data=node.left==null?node.right.data:node.left.data;
if(node.left==null){
node.right.p=null;
node.right=null;
}else{
node.left.p=null;
node.left=null;
}
}else{
if(node.p==null){
root=null;
return ;
}
//如果没有子节点,且删除节点为红色,直接删除
if(node.red){
if(node.p.left==node){
node.p.left=null;
node.p=null;
}else{
node.p.right=null;
node.p=null;
}
}else{
Node in = node;
while(in.p!=null) {
//删除节点为黑
Node p = in.p;
//如果删除节点为父节点的左子树
if (in == p.left) {
Node s = p.right;
//如果兄弟节点是黑色或空
if (s == null || !s.red) {
//如果兄弟节点的远子侄存在
if (s.right != null && s.right.red) {
//删除节点
p.left = null;
in.p = null;
//变色,交换父和兄节点颜色,子侄置黑
s.right.red = false;
boolean flag = p.red;
/*p.red = s.red;
s.red = flag;*/
//左旋转
rotateL(s);
s.red=flag;
p.red=false;
break;
}
//如果近子侄存在
else if (s.left != null && s.left.red) {
//变色,交换兄与子侄颜色
s.left.red = s.red;
s.red = true;
//旋转变为远子侄情况
Node i = s.left;
s.left = i.right;
if (i.right != null) {
i.right.p = s;
}
i.right = s;
i.p = s.p;
p.right = i;
s.p = i;
}
//兄弟节点无孩子或都为黑且父为红
else if (p.red) {
//删除节点
p.left = null;
node.p = null;
//父变黑,兄变红
p.red = false;
s.red = true;
break;
}
//兄弟节点无孩子或都为黑且父为黑
else {
//如果为删除节点则删除
if(in==node){
p.left = null;
node.p = null;
}
s.red=true;
in=p;
}
}
//兄弟节点是红色
else {
//删除
p.left = null;
in.p = null;
//兄弟节点变色左旋
rotateL(s);
break;
}
}
//如果删除节点为父节点的右子树
else {
Node s = p.left;
//如果兄弟节点是黑色或空
if (s == null || !s.red){
//如果远子侄为红色
if(s.left!=null&&s.left.red){
//删除节点
p.right = null;
in.p = null;
//变色,交换父和兄节点颜色,子侄置黑
s.left.red = false;
boolean flag = p.red;
/*p.red = s.red;
s.red = flag;*/
//左旋转
rotateR(s);
s.red=flag;
p.red=false;
break;
}
//如果近子侄为红色
else if(s.right!=null&&s.right.red){
//变色,交换兄与子侄颜色
s.right.red = s.red;
s.red = true;
//旋转变为远子侄情况
Node i = s.right;
s.right = i.left;
if (i.left != null) {
i.left.p = s;
}
i.left = s;
i.p = s.p;
p.left = i;
s.p = i;
}
//如果父节点为红
else if(p.red){
//删除节点
p.left = null;
node.p = null;
//父变黑,兄变红
p.red = false;
s.red = true;
break;
}
//如果父为黑
else{
//如果为删除节点则删除
if(in==node){
p.left = null;
node.p = null;
}
s.red=true;
in=p;
}
}
}
}
}
}
}
graham扫描法求凸包