一贴看完java常用的查找、排序算法,后续将更新二叉树算法、递归、谈心、回溯等算法。
今天被面试官刁难了一番(问的不是排序查找),想借此复习一下算法基础。
主要内容:
二分查找
冒泡排序算法
插入排序算法
快速排序(含优化)算法
希尔排序算法
归并排序算法
堆排序算法
桶排序算法
基数排序算法
二叉树算法
红黑树算法(手写红黑树)
import java.util.*;
/**
* 二分查找
* 冒泡排序算法
* 插入排序算法
* 快速排序算法
* 希尔排序算法
* 归并排序算法
* 堆排序算法
* 桶排序算法
* 基数排序算法
* @author cherry
* @create 2020-04-23-19:19
*/
public class AllSort {
public static void main(String[] args) {
int[] a = {49, 38, 65, 97, 76, 13, 27, 50};
int[] b1 = {49, 38, 65, 97, 76, 13, 27, 50};
int[] b2 = {49, 38, 65, 97, 76, 13, 27, 50};
int[] c = {49, 38, 65, 97, 76, 13, 27, 50};
int[] d = {49, 38, 65, 97, 76, 13, 27, 50};
int[] e = {49, 38, 65, 97, 76, 13, 27, 50};
int[] f = {49, 38, 65, 97, 76, 13, 27, 50};
int[] g = {49, 38, 65, 97, 76, 13, 27, 50};
int[] h = {49, 38, 65, 97, 76, 13, 27, 50};
int[] i = {49, 38, 65, 97, 76, 13, 27, 50};
MergeSort.mergeSort(a, 0, a.length - 1);
QuickSort.quickSort(b1, 0, b1.length - 1);
QuickSort.newQuickSort(b2);
HeapSort.heapSort(c);
BubbleSort.bubbleSort(e);
InsertSort.insertSort(f);
ShellSort.shellSort(g);
BucketSort.bucketSort(h);
RadixSort.radixSort(i);
System.out.println("归排排好序的数组:" + Arrays.toString(a));
System.out.println("快排排好序的数组:" + Arrays.toString(b1));
System.out.println("快排优化后排好序的数组:" + Arrays.toString(b2));
System.out.println("堆排排好序的数组:" + Arrays.toString(c));
System.out.println("元素'38'的下标为:" + BSearch.biSearch(d, 38));
System.out.println("冒泡排好序的数组:" + Arrays.toString(e));
System.out.println("插排好序的数组:" + Arrays.toString(f));
System.out.println("希尔排好序的数组:" + Arrays.toString(f));
System.out.println("桶排排好序的数组:" + Arrays.toString(f));
System.out.println("基排排好序的数组:" + Arrays.toString(f));
}
}
/**
* 归并排序
* 归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列
* 分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
*/
class MergeSort {
//两路归并算法,两个排好序的子序列合并为一个子序列
private static void merge(int[] a, int left, int mid, int right) {
int[] tmp = new int[a.length];//辅助数组
int p1 = left, p2 = mid + 1, k = left;//p1、p2是检测指针,k是存放指针
while (p1 <= mid && p2 <= right) {
if (a[p1] <= a[p2]) tmp[k++] = a[p1++];
else tmp[k++] = a[p2++];
}
while (p1 <= mid) tmp[k++] = a[p1++];//如果第一个序列未检测完,直接将后面所有元素加到合并的序列中
while (p2 <= right) tmp[k++] = a[p2++];//同上
for (int i = left; i <= right; i++) a[i] = tmp[i];//复制回原素组
}
static void mergeSort(int[] a, int start, int end) {
if (start < end) {//当子序列中只有一个元素时结束递归
int mid = (start + end) / 2;//划分子序列
mergeSort(a, start, mid);//对左侧子序列进行递归排序
mergeSort(a, mid + 1, end);//对右侧子序列进行递归排序
merge(a, start, mid, end);//合并
}
}
}
/**
* 快速排序
* 快速排序的原理:选择一个关键值作为基准值。比基准值小的都在左边序列(一般是无序的),
* 比基准值大的都在右边(一般是无序的)。一般选择序列的第一个元素。
* 一次循环:从后往前比较,用基准值和最后一个值比较,如果比基准值小的交换位置,如果没有
* 继续比较下一个,直到找到第一个比基准值小的值才交换。找到这个值之后,又从前往后开始比
* 较,如果有比基准值大的,交换位置,如果没有继续比较下一个,直到找到第一个比基准值大的
* 值才交换。直到从前往后的比较索引>从后往前比较的索引,结束第一次循环,此时,对于基准值
* 来说,左右两边就是有序的了。
*/
class QuickSort {
//优化前的快排:最好平均最差时间复杂度为O(nlogn)、O(nlogn)、O(n2)
static void quickSort(int[] arr, int start, int end) {
int i, j, temp, t;
if (start > end) return;
i = start;
j = end;
temp = arr[start];//temp就是基准位
while (i < j) {
while (temp <= arr[j] && i < j) j--;//先看右边,依次往左递减
while (temp >= arr[i] && i < j) i++;//再看左边,依次往右递增
t = arr[j];//如果满足条件则交换
arr[j] = arr[i];
arr[i] = t;
}
arr[start] = arr[i];//最后将基准为与i和j相等位置的数字交换
arr[i] = temp;
quickSort(arr, start, j - 1);//递归调用左半数组
quickSort(arr, j + 1, end);//递归调用右半数组
}
//优化后的快排:最好平均最差时间复杂度均为O(nlogn)
static void newQuickSort(int[] arr) {
int random = new Random().nextInt(arr.length + 1);
int temp = arr[random];//将基准设置为随机元素
arr[random] = arr[0];
arr[0] = temp;
quickSort(arr, 0, arr.length - 1);
}
}
/**
* 堆排序
* 堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。
* 将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。
* 如此反复执行,便能得到一个有序序列。
*/
class HeapSort {
static void heapSort(int[] arr) {
//创建堆,从第一个非叶子结点从下至上,从右至左调整结构
for (int i = (arr.length - 1) / 2; i >= 0; i--) adjustHeap(arr, i, arr.length);
//调整堆结构+交换堆顶元素与末尾元素
for (int i = arr.length - 1; i > 0; i--) {
int temp = arr[i];//将堆顶元素与末尾元素进行交换
arr[i] = arr[0];
arr[0] = temp;
adjustHeap(arr, 0, i);//重新对堆进行调整
}
}
//调整堆@param arr待排序列 @param parent 父节点 @param length 待排序列尾元素索引
private static void adjustHeap(int[] arr, int parent, int length) {
int temp = arr[parent];//将temp作为父节点
int lChild = 2 * parent + 1;//左孩子
while (lChild < length) {
int rChild = lChild + 1;//右孩子
if (rChild < length && arr[lChild] < arr[rChild]) lChild++;// 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点
if (temp >= arr[lChild]) break;// 如果父结点的值已经大于孩子结点的值,则直接结束
arr[parent] = arr[lChild];// 把孩子结点的值赋给父结点
parent = lChild;//选取孩子结点的左孩子结点,继续向下筛选
lChild = 2 * lChild + 1;
}
arr[parent] = temp;
}
}
/**
* 二分查找
* 又叫折半查找,要求待查找的序列有序。每次取中间位置的值与待查关键字比较,如果中间位置
* 的值比待查关键字大,则在前半部分循环这个查找的过程,如果中间位置的值比待查关键字小,
* 则在后半部分循环这个查找的过程。直到查找到了为止,否则序列中没有待查的关键字。
*/
class BSearch {
static int biSearch(int[] arr, int num) {
int lo = 0;
int hi = arr.length - 1;
int mid;
while (lo < hi) {
mid = (lo + hi) >> 1;
if (arr[mid] == num) return mid + 1;
else if (arr[mid] > num) hi = mid - 1;
else lo = mid + 1;
}
return -1;
}
}
/**
* 冒泡排序(看不懂请学java基础)
* (1)比较前后相邻的二个数据,如果前面数据大于后面的数据,就将这二个数据交换。
* (2)这样对数组的第 0 个数据到 N-1 个数据进行一次遍历后,最大的一个数据就“沉”到数组第
* N-1 个位置。
* (3)N=N-1,如果 N 不为 0 就重复前面二步,否则排序完成。
*/
class BubbleSort {
public static void bubbleSort(int[] a) {
for (int i = 0; i < a.length; i++) {//表示 n 次排序过程。
for (int j = 1; j < a.length - i; j++) {
if (a[j - 1] > a[j]) {//前面的数字大于后面的数字就交换
int temp;
temp = a[j - 1];
a[j - 1] = a[j];
a[j] = temp;
}
}
}
}
}
/**
* 插入排序
* 通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应的位置并插入。
* 插入排序非常类似于整扑克牌。在开始摸牌时,左手是空的,牌面朝下放在桌上。接着,一次从
* 桌上摸起一张牌,并将它插入到左手一把牌中的正确位置上。为了找到这张牌的正确位置,要将
* 它与手中已有的牌从右到左地进行比较。无论什么时候,左手中的牌都是排好序的。
* 如果输入数组已经是排好序的话,插入排序出现最佳情况,其运行时间是输入规模的一个线性函
* 数。如果输入数组是逆序排列的,将出现最坏情况。平均情况与最坏情况一样,其时间代价是(n2)。
*/
class InsertSort {
public static void insertSort(int arr[]) {
for (int i = 1; i < arr.length; i++) {
//插入的数
int insertVal = arr[i];
//被插入的位置(准备和前一个数比较)
int index = i - 1;
//如果插入的数比被插入的数小
while (index >= 0 && insertVal < arr[index]) {
//将把 arr[index] 向后移动
arr[index + 1] = arr[index];
//让 index 向前移动
index--;
}
//把插入的数放入合适位置
arr[index + 1] = insertVal;
}
}
}
/**
* 希尔排序
* 基本思想:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列
* 中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
* 1. 操作方法:
* 选择一个增量序列 t1,t2,…,tk,其中 ti>tj,tk=1;
* 2. 按增量序列个数 k,对序列进行 k 趟排序;
* 3. 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进
* 行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长
* 度。
*/
class ShellSort {
static void shellSort(int[] a) {
int dk = a.length / 2;
while (dk >= 1) {
shellInsertSort(a, dk);
dk = dk / 2;
}
}
private static void shellInsertSort(int[] a, int dk) {
//类似插入排序,只是插入排序增量是 1,这里增量是 dk,把 1 换成 dk 就可以了
for (int i = dk; i < a.length; i++) {
if (a[i] < a[i - dk]) {
int j;
int x = a[i];//x 为待插入元素
a[i] = a[i - dk];
//通过循环,逐个后移一位找到要插入的位置。
for (j = i - dk; j >= 0 && x < a[j]; j = j - dk) a[j + dk] = a[j];
a[j + dk] = x;//插入
}
}
}
}
/**
* 桶排序
* 桶排序的基本思想是: 把数组 arr 划分为 n 个大小相同子区间(桶),每个子区间各自排序,最
* 后合并 。计数排序是桶排序的一种特殊情况,可以把计数排序当成每个桶里只有一个元素的情况。
* 1.找出待排序数组中的最大值 max、最小值 min
* 2.我们使用 动态数组 ArrayList 作为桶,桶里放的元素也用 ArrayList 存储。桶的数量为(max-
* min)/arr.length+1
* 3.遍历数组 arr,计算每个元素 arr[i] 放的桶
* 4.每个桶各自排序
*/
class BucketSort {
public static void bucketSort(int[] arr) {
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for (int item : arr) {
max = Math.max(max, item);
min = Math.min(min, item);
}
//创建桶
int bucketNum = (max - min) / arr.length + 1;
List<List<Integer>> bucketArr = new ArrayList<>(bucketNum);
for (int i = 0; i < bucketNum; i++) bucketArr.add(new ArrayList<>());
//将每个元素放入桶
for (int value : arr) {
int num = (value - min) / (arr.length);
bucketArr.get(num).add(value);
}
//对每个桶进行排序
for (List<Integer> integers : bucketArr) Collections.sort(integers);
}
}
/**
* 基数排序
* 将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位
* 开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序
* 列。
*/
class RadixSort {
public static void radixSort(int[] array) {
//首先确定排序的趟数;
int max = array[0];
for (int i = 1; i < array.length; i++) if (array[i] > max) max = array[i];
int time = 0;
//判断位数;
while (max > 0) {
max /= 10;
time++;
}
//建立 10 个队列;
List<List> queue = new ArrayList<>();
for (int i = 0; i < 10; i++) {
List<Integer> queue1 = new ArrayList<>();
queue.add(queue1);
}
//进行 time 次分配和收集;
for (int i = 0; i < time; i++) {
//分配数组元素;
for (int j = 0; j < array.length; j++) {
//得到数字的第 time+1 位数;
int x = array[j] % (int) Math.pow(10, i + 1) / (int) Math.pow(10, i);
List<Integer> queue2 = queue.get(x);
queue2.add(array[j]);
queue.set(x, queue2);
}
int count = 0;//元素计数器;
//收集队列元素;
for (int k = 0; k < 10; k++) {
while (queue.get(k).size() > 0) {
List<Integer> queue3 = queue.get(k);
array[count] = queue3.get(0);
queue3.remove(0);
count++;
}
}
}
}
}
各排序算法时间与空间复杂度、稳定性表格
二叉树算法
public class BinaryTree {
public static void main(String[] args) {
TreeNode treeNode = new TreeNode(new TreeNode(new TreeNode(3),
new TreeNode(4), 2, false),
new TreeNode(new TreeNode(6),
new TreeNode(7), 5, false), 1, false);
BinaryTree binaryTree = new BinaryTree();
System.out.println("前序遍历");
binaryTree.preOrder(treeNode);//1 2 3 4 5 6 7
System.out.println("\n中序遍历");
binaryTree.inOrder(treeNode);//3 2 4 1 6 5 7
System.out.println("\n后序遍历");
binaryTree.postOrder(treeNode);//3 4 2 6 7 5 1
}
// 根节点
private TreeNode root;
public TreeNode getRoot() {
return root;
}
/**
* 插入操作
*
* @param value
*/
public void insert(int value) {
TreeNode newNode = new TreeNode(value);
if (root == null) {
root = newNode;
root.setLefTreeNode(null);
root.setRightNode(null);
} else {
TreeNode currentNode = root;
TreeNode parentNode;
while (true) {
parentNode = currentNode;
// 往右放
if (newNode.getValue() > currentNode.getValue()) {
currentNode = currentNode.getRightNode();
if (currentNode == null) {
parentNode.setRightNode(newNode);
return;
}
} else {
// 往左放
currentNode = currentNode.getLefTreeNode();
if (currentNode == null) {
parentNode.setLefTreeNode(newNode);
return;
}
}
}
}
}
/**
* 查找
*
* @param key
* @return
*/
public TreeNode find(int key) {
TreeNode currentNode = root;
if (currentNode != null) {
while (currentNode.getValue() != key) {
if (currentNode.getValue() > key) currentNode = currentNode.getLefTreeNode();
else currentNode = currentNode.getRightNode();
if (currentNode == null) return null;
}
if (currentNode.isDelete()) return null;
else return currentNode;
} else return null;
}
/**
* 前序遍历
*
* @param treeNode
*/
public void preOrder(TreeNode treeNode) {
if (treeNode != null && !treeNode.isDelete()) {
System.out.print(treeNode.getValue() + " ");
preOrder(treeNode.getLefTreeNode());
preOrder(treeNode.getRightNode());
}
}
/**
* 中序遍历
*
* @param treeNode
*/
public void inOrder(TreeNode treeNode) {
if (treeNode != null && !treeNode.isDelete()) {
inOrder(treeNode.getLefTreeNode());
System.out.print(treeNode.getValue() + " ");
inOrder(treeNode.getRightNode());
}
}
/**
* 后序遍历
*
* @param treeNode
*/
public void postOrder(TreeNode treeNode) {
if (treeNode != null && !treeNode.isDelete()) {
postOrder(treeNode.getLefTreeNode());
postOrder(treeNode.getRightNode());
System.out.print(treeNode.getValue() + " ");
}
}
}
class TreeNode {
public TreeNode left;
// 左节点
private TreeNode lefTreeNode;
// 右节点
private TreeNode rightNode;
// 数据
private int value;
private boolean isDelete;
public TreeNode getLefTreeNode() {
return lefTreeNode;
}
public void setLefTreeNode(TreeNode lefTreeNode) {
this.lefTreeNode = lefTreeNode;
}
public TreeNode getRightNode() {
return rightNode;
}
public void setRightNode(TreeNode rightNode) {
this.rightNode = rightNode;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public boolean isDelete() {
return isDelete;
}
public void setDelete(boolean isDelete) {
this.isDelete = isDelete;
}
public TreeNode() {
super();
}
public TreeNode(int value) {
this(null, null, value, false);
}
public TreeNode(TreeNode lefTreeNode, TreeNode rightNode, int value, boolean isDelete) {
super();
this.lefTreeNode = lefTreeNode;
this.rightNode = rightNode;
this.value = value;
this.isDelete = isDelete;
}
@Override
public String toString() {
return lefTreeNode + ", rightNode=" + rightNode + ", value=" + value + ", isDelete=" + isDelete;
}
}
红黑树算法,学会手写红黑树
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.Stack;
/*
平衡二叉树-红黑树 红黑树性质:
1、根节点是黑色
2、节点颜色只能是红色或黑色的
3、红色节点的两个儿子只能是黑色的
4、NIL节点看作是黑色的
5、任意节点到子孙的空节点的路径上的黑色节点数目相同
*/
public class RedBlackTree {
public static void main(String[] args) {
RedBlackTree redBlackTree = new RedBlackTree();
redBlackTree.insert(6);
redBlackTree.insert(4);
redBlackTree.insert(5);
redBlackTree.insert(7);
redBlackTree.insert(3);
redBlackTree.insert(8);
/*redBlackTree.remove(6);
redBlackTree.remove(3);
redBlackTree.remove(8);
redBlackTree.remove(7);
redBlackTree.remove(4);
redBlackTree.remove(5);*/
System.out.println("MAX:" + redBlackTree.findMax());
System.out.println("MIN:" + redBlackTree.findMin());
redBlackTree.preOrder(redBlackTree.root);//5 4 3 7 6 8
redBlackTree.inOrder(redBlackTree.root);//5 4 3 7 6 8
redBlackTree.postOrder(redBlackTree.root);//3 4 6 8 7 5
redBlackTree.levelOrder(redBlackTree.root);//5--Black 4--Black 7--Black 3--Red 6--Red 8--Red
}
//NIL节点(无元素)
private RedBlackNode NIL = new RedBlackNode(Color.Black);
//红黑树的根
private RedBlackNode root = NIL;
//红黑树节点数目
private int size = 0;
public RedBlackTree(RedBlackNode root) {
this.root = root;
}
public RedBlackTree(RedBlackNode NIL, RedBlackNode root, int size) {
this.NIL = NIL;
this.root = root;
this.size = size;
}
public RedBlackTree() {
}
//红黑树中是否含有node节点
public boolean containsRec(RedBlackNode node, RedBlackNode root) {
//红黑树为空
if (root == NIL) {
return false;
}
//搜索右子树
if (node.value > root.value) {
return contains(node, root.right);
}
//搜索左子树
if (node.value < root.value) {
return contains(node, root.left);
}
//包含node节点
return true;
}
//红黑树中是否含有node节点
public boolean contains(RedBlackNode node, RedBlackNode root) {
//红黑树为空
if (root == NIL) {
return false;
}
while (node.value != root.value) {
//搜索右子树
if (node.value > root.value) {
root = root.right;
}
//搜索左子树
else {
root = root.left;
}
//未在红黑树中为找到node节点
if (root == NIL) {
return false;
}
}
return true;
}
//返回红黑树中的最小节点
public int findMin() {
RedBlackNode value = findMin(root);
if (value == null) {
return -1;
} else {
return value.value;
}
}
//在指定红黑树中搜寻最小节点 递归搜索
private RedBlackNode findMin(RedBlackNode root) {
//指定红黑树为空树
if (root == NIL) {
return null;
}
//如果红黑树的左子树非空,则搜索其左子树
if (root.left != NIL) {
return findMin(root.left);
}
return root;
}
//返回红黑树中的最大节点
public int findMax() {
RedBlackNode value = findMax(root);
if (value == null) {
return -1;
} else {
return value.value;
}
}
//搜索指定红黑树中的最大节点 递归实现
private RedBlackNode findMax(RedBlackNode root) {
//红黑树为空
if (root == NIL) {
return null;
}
//搜索右子树
if (root.right != NIL) {
return findMax(root.right);
}
return root;
}
//得到祖父节点
private RedBlackNode getGrandParent(RedBlackNode node) {
//父亲节点为空
if (node.parent == null) {
return null;
}
return node.parent.parent;
}
//得到叔父节点
private RedBlackNode getUncle(RedBlackNode node) {
RedBlackNode grandParent = getGrandParent(node);
//祖父节点为空
if (grandParent == null) {
return null;
}
//父亲节是祖父节点的左儿子
if (node.parent == grandParent.left) {
return grandParent.right;
}
//父亲节点时祖父节点的右儿子
else {
return grandParent.left;
}
}
//左旋 RR型 注意更新root、rightChild、rightChild.left的父亲指针指向
private RedBlackNode rolateWithRightChild(RedBlackNode root) {
//parent记录root的父亲节点
RedBlackNode parent = root.parent;
//rightChild root节点的右儿子
RedBlackNode rightChild = root.right;
//如果rightChild的左儿子非空,更新rightChild.left的parent
if (rightChild.left != NIL) {
rightChild.left.parent = root;
}
root.right = rightChild.left;
rightChild.left = root;
//更新root的parent
root.parent = rightChild;
//root节点为根节点时,根节点改变
if (parent == null) {
this.root = rightChild;
}
//root节点为父亲的右儿子
else if (root == parent.right) {
parent.right = rightChild;
}
//root节点为父亲的左儿子
else {
parent.left = rightChild;
}
//更新rightChild的parent
rightChild.parent = parent;
return rightChild;
}
//右旋 LL型
private RedBlackNode rolateWithLeftChild(RedBlackNode root) {
//parent记录root的父亲节点
RedBlackNode parent = root.parent;
//leftChild root节点的左儿子
RedBlackNode leftChild = root.left;
root.left = leftChild.right;
//如果leftChild的右儿子非空,则更新leftChild.right的parent
if (leftChild.right != NIL) {
leftChild.right.parent = root;
}
leftChild.right = root;
//更新root的parent
root.parent = leftChild;
//如果root节点之前是根节点,则根节点指向更新
if (parent == null) {
this.root = leftChild;
}
//root节点为父亲的左儿子
else if (root == parent.left) {
parent.left = leftChild;
}
//root节点为父亲的右儿子
else {
parent.right = leftChild;
}
//更新leftChild的parent
leftChild.parent = parent;
return leftChild;
}
//双旋转 RL型
private RedBlackNode doubleWithRightChild(RedBlackNode root) {
//先右旋再左旋
rolateWithLeftChild(root.right);
return rolateWithRightChild(root);
}
//双旋转 LR型
private RedBlackNode doubleWithLeftChild(RedBlackNode root) {
//先左旋再右旋
rolateWithRightChild(root.left);
return rolateWithLeftChild(root);
}
//插入值为value的节点
public void insert(int value) {
//插入节点的左右儿子为NIL,颜色为红色
RedBlackNode node = new RedBlackNode(value, NIL, NIL, Color.Red);
//如果已经含有此节点
if (contains(node, root)) {
return;
}
insert(node, root);
//红黑树节点数目增加一个
size++;
}
//向红黑树中插入节点node
public void insert(RedBlackNode node, RedBlackNode root) {
//如果根节点为空,则将插入的节点染成黑色即可
if (this.root == NIL) {
node.color = Color.Black;
this.root = node;
} else {
//记录插入节点的父亲
RedBlackNode parent = root;
//找到node节点应该插入的位置
while (node.value != root.value) {
//更新parent
parent = root;
//插入到右子树
if (node.value > root.value) {
root = root.right;
//到达NIl节点,作为右儿子插入
if (root == NIL) {
node.parent = parent;
parent.right = node;
break;
}
}
//插入到左子树
else {
root = root.left;
//作为左儿子插入
if (root == NIL) {
node.parent = parent;
parent.left = node;
break;
}
}
}
//执行插入修复操作
insertFixUp(node);
}
}
//node节点插入后进行修复
private void insertFixUp(RedBlackNode node) {
//添加修复操作不会超过2次,node节点经过前一次处理后上升到根节点,颜色为红色,染成黑色,更新根节点指针
if (node.parent == null) {
node.color = Color.Black;
//更新root引用
this.root = node;
return;
}
//node节点的父亲颜色是黑色,无需调整
if (node.parent.color == Color.Black) {
return;
}
//得到node节点的叔父、父亲、祖父节点
RedBlackNode uncle = getUncle(node);
RedBlackNode grandParent = getGrandParent(node);
RedBlackNode parent = node.parent;
/*node节点的父节点、叔父节点颜色为红色,祖父节点为黑色
策略:将node节点的父节点、叔父节点颜色染成黑色,祖父节点染成红色。
此时祖父节点可能与其父节点颜色冲突,递归解决*/
if (uncle.color == Color.Red) {
node.parent.color = Color.Black;
uncle.color = Color.Black;
grandParent.color = Color.Red;
//递归修复grandParent
insertFixUp(grandParent);
}
//LL型 叔父节点是黑色 策略:将父亲节点染成黑色,祖父节点染成红色, 右旋转
else if (node == parent.left && parent == grandParent.left) {
parent.color = Color.Black;
grandParent.color = Color.Red;
rolateWithLeftChild(grandParent);
}
//RL型 叔父节点是黑色 策略:node节点染成黑色,祖父节点染成红色,先右旋转再左旋转
else if (node == parent.left && parent == grandParent.right) {
node.color = Color.Black;
grandParent.color = Color.Red;
doubleWithRightChild(grandParent);
}
//RR型 叔父节点黑色策略:将父亲节点染成黑色、祖父节点染成红色,左旋转
else if (node == parent.right && parent == grandParent.right) {
parent.color = Color.Black;
grandParent.color = Color.Red;
rolateWithRightChild(grandParent);
}
//LR型 叔父节点黑色 策略:node节点染成黑色,祖父节点染成红色,先左旋,再右旋
else {
node.color = Color.Black;
grandParent.color = Color.Red;
doubleWithLeftChild(grandParent);
}
}
//删除值为value的节点
public void remove(int val) {
RedBlackNode node = new RedBlackNode(val, Color.Red);
remove(node, root);
}
//删除root中的节点node
private RedBlackNode remove(RedBlackNode node, RedBlackNode root) {
//红黑树为空
if (root == NIL) {
return null;
}
//节点在右子树
if (node.value > root.value) {
root.right = remove(node, root.right);
}
//节点在左子树
if (node.value < root.value) {
root.left = remove(node, root.left);
}
if (node.value == root.value) {
//待删除节点的左右子树非空
if (root.left != NIL && root.right != NIL) {
//用右子树的最小值节点替代待删除的节点
RedBlackNode replace = findMin(root.right);
root.value = replace.value;
//问题转化为删除右子树中的replace节点
root.right = remove(replace, root.right);
}
//待删除节点只有左子树或只有右子树或无左右子树
else {
//被删除节点的父节点
RedBlackNode parent = root.parent;
//被删除的节点
RedBlackNode deleteNode = root;
//被删除节点的位置尤其儿子取代
root = (root.left != NIL) ? root.left : root.right;
//如果被删除节点是根节点, 将后继节点染黑后作为新的根节点
if (parent == null) {
deleteNode.left.parent = parent;
deleteNode.right.parent = parent;
root.color = Color.Black;
this.root = root;
} else {
//node节点是作为parent的左儿子还是右儿子
boolean isLeftChild = false;
if (deleteNode == parent.left) {
isLeftChild = true;
}
//被删除节点的儿子的父亲指向祖父
root.parent = parent;
// 将root接到其祖父
if (isLeftChild) {
parent.left = root;
} else {
parent.right = root;
}
//修复被删除节点
removeFixUp(root, deleteNode);
//清除deleteNode的所有引用
deleteNode.parent = null;
deleteNode.left = null;
deleteNode.right = null;
}
}
}
//将NIL节点的父亲置为null,NIL节点的颜色在修复过程中可能会被染成红色,将其恢复成黑色
NIL.parent = null;
NIL.color = Color.Black;
//红黑树节点数目减少一个
size--;
return root;
}
//删除node的父节点后进行修复红黑树性质的操作,修复操作旋转次数不会超过3次
private void removeFixUp(RedBlackNode node, RedBlackNode deleteNode) {
//如果被删除节点是根节点,直接将node节点颜色染黑让其作为新的根节点
if (deleteNode.parent == null) {
node.color = Color.Black;
this.root = node;
return;
}
//如果被删除节点的颜色是红色的,对红黑树的五条性质不造成影响,无需处理
if (deleteNode.color == Color.Red) {
return;
}
//如果被删除节点的后继节点颜色为红色,将其染成黑色既可
if (node.color == Color.Red) {
node.color = Color.Black;
return;
}
//如果被删除节点的后继节点颜色为黑色,那么从被删除节点父亲节点到被删除节点NIL节点的路径上将少一个黑色节点
if (node.color == Color.Black) {
//得到node的叔叔节点,叔叔节点的左儿子、右儿子, 祖父
RedBlackNode uncle = getBrother(node);
RedBlackNode uncleLeftChild = uncle.left;
RedBlackNode uncleRightChild = uncle.right;
RedBlackNode grandParent = node.parent;
while (true) {
//node节点现在是其祖父的左儿子
if (node == grandParent.left) {
/*状态 - 1 如果叔叔节点颜色是红色
策略:将祖父节点染成红色,叔叔节点染成染成黑色后左旋,使其装态转化为2或3或4以便进一步调整*/
if (uncle.color == Color.Red) {
uncle.color = Color.Black;
grandParent.color = Color.Red;
rolateWithRightChild(grandParent);
//更新node指向
node = grandParent.right;
grandParent = node.parent;
uncle = getBrother(node);
uncleLeftChild = uncle.left;
uncleRightChild = uncle.right;
}
/*状态 - 2
叔叔节点为黑色,其左右儿子也为黑色(此时叔叔节点可能为NIL节点,如果其为NIL节点,其左右儿子将为NULL
,要注意空指针判断) 策略:将叔叔节点染成红色,现在祖父是多了一重黑色,向状态1,3,4 转化*/
else if (isBlack(uncleLeftChild)
&& isBlack(uncleRightChild)) {
//更新叔叔节点颜色
uncle.color = Color.Red;
//更新node指向
node = grandParent;
grandParent = node.parent;
//node现在指向了根节点
if (grandParent == null) {
node.color = Color.Black;
this.root = node;
break;
}
uncle = getBrother(node);
uncleLeftChild = uncle.left;
uncleRightChild = uncle.right;
//当前节点是红黑型的节点,将当前节点颜色染成黑色,调整完成
if (grandParent.color == Color.Red) {
grandParent.color = Color.Black;
break;
}
}
/*状态 - 3 叔叔节点颜色为黑色,其左儿子颜色为红色,右儿子颜色为黑色
策略:将叔叔节点的左儿子染成黑色,叔叔节点染成红色,右旋向状态4转化*/
else if (uncleLeftChild.color == Color.Red
&& uncleRightChild.color == Color.Black) {
uncle.color = Color.Red;
uncleLeftChild.color = Color.Black;
rolateWithLeftChild(uncle);
}
/*状态 - 4 叔叔节点颜色为黑色,右儿子颜色为红色,左儿子颜色任意
策略:将uncle节点颜色与祖父节点颜色互换,叔叔节点右儿子染成黑色,左旋转调整完成。
此次操作是祖父的左子树路径增加了一个黑色节点,但叔叔节点右子树少了一个黑色节点,把叔叔节点的右儿子染成黑色弥补*/
else {
Color temp = uncle.color;
uncle.color = grandParent.color;
grandParent.color = temp;
uncleRightChild.color = Color.Black;
rolateWithRightChild(grandParent);
//调整完成
break;
}
}
//node节点现在使其祖父的右儿子 镜像对称的四种情形
if (node == grandParent.right) {
//状态 - 1 如果叔叔节点颜色是红色
//策略:将祖父节点染成红色,叔叔节点染成黑色后右旋,使其状态转化为2或3或4以便进一步调整
if (uncle.color == Color.Red) {
uncle.color = Color.Black;
grandParent.color = Color.Red;
rolateWithLeftChild(grandParent);
//更新node指向
node = grandParent.left;
grandParent = node.parent;
uncle = getBrother(node);
uncleLeftChild = uncle.left;
uncleRightChild = uncle.right;
}
/*状态 - 2 叔叔节点为黑色,叔叔节点的左右儿子颜色为黑色
策略:将叔叔节点染成红色,现在祖父节点多了一重黑色。如果祖父节点自身颜色为红色
,将祖父节点染成黑色,调整结束。否则修改node指向使其指向祖父*/
else if (isBlack(uncleRightChild)
&& isBlack(uncleLeftChild)) {
uncle.color = Color.Red;
//修改node指向
node = grandParent;
grandParent = node.parent;
//node节点提升到了根节点位置
if (grandParent == null) {
node.color = Color.Black;
this.root = node;
break;
}
uncle = getBrother(node);
uncleLeftChild = uncle.left;
uncleRightChild = uncle.right;
//如果祖父节点自身颜色为红色,将祖父节点染成黑色 ,调整完成
if (node.color == Color.Red) {
node.color = Color.Black;
break;
}
}
/*状态 - 3 叔叔节点为黑色,其左儿子为黑色,右儿子为红色
策略:将叔叔节点染成红色,右儿子染成黑色,右旋向状态 - 4 转换*/
else if (uncleLeftChild.color == Color.Black
&& uncleRightChild.color == Color.Red) {
uncle.color = Color.Red;
uncleRightChild.color = Color.Black;
rolateWithRightChild(uncle);
}
/*状态 - 4 叔叔节点为黑色,左儿子为红色,右儿子颜色任意
策略:将叔叔节点的颜色与祖父颜色互换,叔叔节点的左儿子染成黑色,右旋。调整完成。*/
else {
Color temp = uncle.color;
uncle.color = grandParent.color;
grandParent.color = temp;
uncleLeftChild.color = Color.Black;
rolateWithLeftChild(grandParent);
//调整完成
break;
}
}
}
}
//最后再次把根节点染成黑色(应为node可能上升到根节点 处
this.root.color = Color.Black;
}
//node颜色为黑色或是null节点是认为其实黑色
private boolean isBlack(RedBlackNode node) {
if (node == null || node.color == Color.Black) {
return true;
}
return false;
}
//得到node节点兄弟节点
private RedBlackNode getBrother(RedBlackNode node) {
//当前节点是根节点
if (node.parent == null) {
return null;
}
//兄弟节点是右儿子
if (node == node.parent.left) {
return node.parent.right;
}
//兄弟节点是左儿子
return node.parent.left;
}
//获取当前红黑树的节点数目
public int getSize() {
return size;
}
//非递归先序遍历红黑树
public void preOrder(RedBlackNode root) {
//红黑树节点数目为零
if (root == NIL) {
return;
}
System.out.println("先序遍历:");
Stack<RedBlackNode> stack = new Stack<RedBlackNode>();
stack.push(root);
while (!stack.isEmpty()) {
root = stack.pop();
System.out.print(root.value + " ");
if (root.right != NIL) {
stack.push(root.right);
}
if (root.left != NIL) {
stack.push(root.left);
}
}
System.out.println();
}
//非递归中序遍历红黑树
public void inOrder(RedBlackNode root) {
if (root == NIL) {
return;
}
System.out.println("中序遍历:");
Stack<RedBlackNode> stack = new Stack<RedBlackNode>();
while (root != NIL || !stack.isEmpty()) {
while (root != NIL) {
stack.push(root);
root = root.left;
}
root = stack.pop();
System.out.print(root.value + " ");
root = root.right;
}
System.out.println();
}
//非递归后序遍历红黑树
public void postOrder(RedBlackNode root) {
if (root == NIL) {
return;
}
System.out.println("后序遍历:");
RedBlackNode pre = NIL;
Stack<RedBlackNode> stack = new Stack<RedBlackNode>();
while (root != NIL || !stack.isEmpty()) {
while (root != NIL) {
stack.push(root);
root = root.left;
}
root = stack.peek();
while (root.right == NIL || root.right == pre) {
System.out.print(root.value + " ");
stack.pop();
pre = root;
if (stack.isEmpty()) {
System.out.println();
return;
}
root = stack.peek();
}
root = root.right;
}
}
//层序遍历红黑树
public void levelOrder(RedBlackNode root) {
if (root == NIL) {
return;
}
System.out.println("层序遍历:");
Queue<RedBlackNode> queue = new ArrayDeque<RedBlackNode>();
queue.offer(root);
while (!queue.isEmpty()) {
root = queue.poll();
System.out.print(root.value + "--" + root.color + " ");
if (root.left != NIL) {
queue.offer(root.left);
}
if (root.right != NIL) {
queue.offer(root.right);
}
}
System.out.println();
}
}
//红黑树节点类
class RedBlackNode {
/*@value 节点值
@color 节点颜色
@left @right 节点左右儿子
@parent 父节点*/
int value;
Color color;
RedBlackNode left;
RedBlackNode right;
RedBlackNode parent;
public RedBlackNode(int value, RedBlackNode left, RedBlackNode right, Color color) {
this.value = value;
this.left = left;
this.right = right;
this.color = color;
}
public RedBlackNode(int value, Color color) {
this.value = value;
this.color = color;
}
//用来构造NIL节点
public RedBlackNode(Color color) {
this.color = color;
}
}
//枚举类-节点颜色
enum Color {
Black, Red;
}