冒泡
java
public static void bubbleSort(int[] arr) {
boolean flag = false;//是否进行交换过
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
flag = true;
int tem = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tem;
}
}
if (!flag) break;
else
flag = false;
}
}
快速排序
版本1
java
public static void quickSort(int[] arr, int start, int end) {
if (start < end) {
//把开始那个数作为基准
int stard = arr[start];
//记录下标
int low = start;
int high = end;
//循环找比基准数大的数和比基准数小的数
while (low < high) {
//右数 比基准数大
while (low < high && stard <= arr[high]) {
high--;
}
//右数替换 左边的数
arr[low] = arr[high];
//左数比基准数小
while (low < high && arr[low] <= stard) {
low++;
}
arr[high] = arr[low];
}
//标准数 付给低或高 位
arr[low] = stard;
quickSort(arr, start, low);
quickSort(arr, low + 1, end);
}
}
版本2
java
public static void quickSort(int[] arr, int start, int end) {
if (start < end) {
//stard随机取值,为了使复杂度逼近n*logn
int[] equalArea = netherlandsFlag(arr, start, end,
arr[start + (int) (Math.random() * (end - start + 1))]);
//和stard相等的数据无需再比较一次
quickSort(arr ,start, equalArea[0] - 1);
quickSort(arr ,equalArea[1] + 1 ,end);
}
}
//荷兰国旗问题 <stard ... =stard ... >stard
private static int[] netherlandsFlag(int[] arr, int L, int R, int stard) {
if (L > R) {
return new int[]{-1, -1};
}
if (L == R) {
return new int[]{L, R};
}
int left = L - 1, index = L, right = R + 1;
while (index < right) {
if (arr[index] == stard) {
index++;
} else if (arr[index] < stard) {
swap(arr, index++, ++left);
} else {
swap(arr, index, --right);
}
}
return new int[]{left + 1, right - 1};
}
private static void swap(int[] arr, int i, int j) {
int tem = arr[i];
arr[i] = arr[j];
arr[j] = tem;
}
插入排序
java
public static void insertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
if (arr[i] < arr[i - 1]) {
int temp = arr[i];
int j;
for (j = i - 1; j >= 0 && temp < arr[j]; j--) {
arr[j + 1] = arr[j];
}
arr[j + 1] = temp;
}
}
}
希尔排序
java
//交换法
public static void shellSort(int[] arr) {
int temp = 0;
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
for (int j = i - gap; j >= 0; j -= gap) {
if (arr[j] > arr[j + gap]) {
temp = arr[j];
arr[j] = arr[j + gap];
arr[j + gap] = temp;
}
}
}
}
}
//移位法
public static void bubbleSort(int[] arr) {
int temp = 0;
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
if (arr[i] < arr[i - gap]) {
temp = arr[i];
int j;
for (j = i - gap; j >= 0 && temp < arr[j]; j -= gap) {
arr[j + gap] = arr[j];
}
arr[j + gap] = temp;
}
}
}
}
选择排序
java
public static void selectSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[minIndex] > arr[j])
minIndex = j;
}
if (minIndex != i) {
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
归并
java
//非递归版本
public static void mergeSort(int[] arr) {
int N = arr.length;
int mergeSize = 1;//两个组(左右组)进行merge,从每组size=1开始
while (mergeSize < N) {
int L = 0;
while (L < N) {
int M = L + mergeSize - 1;
if (M >= N) {//左组的结束位置超了
break;
}
int R = Math.min(M + mergeSize, N - 1);
merge(arr, L, M, R);
L = R + 1;//下一次左组开始位置
}
if (mergeSize > N / 2) {//防止溢出
break;
}
mergeSize <<= 1;
}
}
public static void mergeSort(int[] arr, int low, int high) {
if (low >= high) {
return;
}
int middle = low + ((high - low) >> 1);
mergeSort(arr, low, middle);
mergeSort(arr, middle + 1, high);
merge(arr, low, middle, high);
}
public static void merge(int[] arr, int low, int middle, int high) {
//存归并后的临时数组
int[] temp = new int[high - low + 1];
int firstIndex = low;
int secondIndex = middle + 1;
int index = 0;//临时数组中的下标 index
//遍历两个数组,取出小的数,放入临时数组
while (firstIndex <= middle && secondIndex <= high) {
if (arr[firstIndex] <= arr[secondIndex]) {
temp[index++] = arr[firstIndex++];
} else {
temp[index++] = arr[secondIndex++];
}
}
//处理多余数据
while (secondIndex <= high) {
temp[index++] = arr[secondIndex++];
}
while (firstIndex <= middle) {
temp[index++] = arr[firstIndex++];
}
for (int k = 0; k < temp.length; k++) {
arr[k + low] = temp[k];
}
}
数组小和问题
一个数左面比它小的数合计=这个数的小和。
所有数的小和之和=数组的小和。
java
public static int mergeSort(int[] arr, int low, int high) {
if (low >= high) {
return 0;
}
int middle = low + ((high - low) >> 1);
return mergeSort(arr, low, middle) +
mergeSort(arr, middle + 1, high) +
merge(arr, low, middle, high);
}
public static int merge(int[] arr, int low, int middle, int high) {
int res = 0;
//存归并后的临时数组
int[] help = new int[high - low + 1];
int firstIndex = low;
int secondIndex = middle + 1;
int index = 0;//临时数组中的下标 index
while (firstIndex <= middle && secondIndex <= high) {
if (arr[firstIndex] < arr[secondIndex]) {
res += (high - secondIndex + 1) * arr[firstIndex];
help[index++] = arr[firstIndex++];
} else {//相等时coby右组,防止res少算
help[index++] = arr[secondIndex++];
}
}
//处理多余数据
while (secondIndex <= high) {
help[index++] = arr[secondIndex++];
}
while (firstIndex <= middle) {
help[index++] = arr[firstIndex++];
}
for (int k = 0; k < help.length; k++) {
arr[k + low] = help[k];
}
return res;
}
基数排序
java
//非负数排序,temp的第二维表示桶,先进先出
public static void redixSort(int[] arr) {
//数组里面最大的数
int max = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
//最大的长度,位数
int maxLength = String.valueOf(max).length();
//临时存储数据的数组
int[][] temp = new int[10][arr.length];
//记录 余数对应的 temp 的一维数组的个数
int[] count = new int[10];
//比较 maxLength次
for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
for (int j = 0; j < arr.length; j++) {
//计算余数
int ys = arr[j] / n % 10;
temp[ys][count[ys]] = arr[j];
count[ys]++;
}
//记录取出数据 的索引
int index = 0;
for (int j = 0; j < count.length; j++) {
if (count[j] != 0) {
for (int k = 0; k < count[j]; k++) {//从0开始,先进先出
arr[index++] = temp[j][k];
}
//置空 以供下次循环
count[j] = 0;
}
}
}
}
优化版,节省内存
java
public static void redixSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
redixSort(arr, 0, arr.length - 1, maxBits(arr));
}
public static void redixSort(int[] arr, int L, int R, int digit) {
final int radix = 10;
int i = 0, j = 0;
//有多少数,准备多少辅助空间
int[] help = new int[R - L + 1];
for (int d = 1; d <= digit; d++) {
int[] count = new int[radix];
for (i = L; i <= R; i++) {
j = getDigit(arr[i], d);
count[j]++;
}
//count[i]表示当前位(d位)是(0~i)的数字有多少
for (i = 1; i < radix; i++) {
count[i] = count[i] + count[i - 1];
}
//放入桶再拿出来符合先进先出去,后进后出
for (i = R; i >= L; i--) {
j = getDigit(arr[i], d);//从后面遍历,count[j]是小于等于j的有几个
help[count[j] - 1] = arr[i];//从桶里面拿从来后一定会放在count[j] - 1索引上
count[j]--;
}
for (i = L, j = 0; i <= R; i++, j++) {
arr[i] = help[j];
}
}
}
public static int getDigit(int num, int d) {
return (num / (int) Math.pow(10, d - 1)) % 10;
}
public static int maxBits(int[] arr) {
int max = Integer.MIN_VALUE;
for (int j : arr) {
if (j > max) {
max = j;
}
}
int res = 0;
while (max != 0) {
res++;
max /= 10;
}
return res;
}
堆排序
java
/**
* 已知一个几乎有序的数组,几乎有序是指,如果把数组拍好顺序的话,每个元素移动的举例
* 一定不超过k,并且k相对于数组长度来说是比较小的
*/
private static void sortedArrMoveDistanceLessK(int[] arr, int k) {
//默认小根堆
PriorityQueue<Integer> heap = new PriorityQueue<>();
int index = 0;
for (; index <= Math.max(arr.length - 1, k); index++) {
heap.add(arr[index]);
}
int i = 0;
for (; index < arr.length; i++, index++) {
heap.add(arr[index]);
arr[i] = heap.poll();
}
while (!heap.isEmpty()) {
arr[i++] = heap.poll();
}
}
//堆排序
public static void heapSort(int[] arr) {
//创建大顶堆 从上向下建堆 n*logN
//for (int i = 0; i < arr.length; i++)
// heapInsert(arr, i);
//创建大顶堆 从下向上建堆 logN
for (int i = arr.length - 1; i >= 0; i--) {
heapify(arr, i, arr.length);
}
for (int i = arr.length - 1; i > 0; i--) {
//不断的把堆顶元素和最后一个元素交换,并且缩小heapSize,然后调整堆
swap(arr, 0, i);
heapify(arr, 0, i);
}
}
//从index位置,不断的向树的下方下沉,直到没孩子或我大于我所有的孩子
//heapSize表示堆的大小 或 下一个元素的索引
private static void heapify(int[] arr, int index, int heapSize) {
int left = index * 2 + 1;
while (left < heapSize) {//有孩子的情况
//left + 1 < heapSize表示有右孩子,取两个孩子中较大的孩子的索引
int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
//父节点index和较大孩子的节点比较,如果是父节点>=max(孩子节点),无需堆调整,终止循环
largest = arr[largest] > arr[index] ? largest : index;
if (largest == index) {
break;
}
swap(arr, largest, index);//较小值下沉
index = largest;//把下沉后节点的索引赋值给index做父节点,继续和子节点比较
left = index * 2 + 1;
}
}
//节点i 左节点2*i+1 右节点2*i+2 父节点(i-1)/2
private static void heapInsert(int[] arr, int index) {
//终止循环两种方式:1、index=0 2、arr[index]不比其arr[index父]大了
while (arr[index] > arr[(index - 1) / 2]) {
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
private static void swap(int[] arr, int i, int j) {
int tem = arr[i];
arr[i] = arr[j];
arr[j] = tem;
}
自己的堆
改变堆中的值,重新调整堆,调用resign方法。
java
private static class MyHeap<T> {
private ArrayList<T> heap;
private HashMap<T, Integer> indexMap;
private int heapSize;
private Comparator<? super T> comparator;
public MyHeap(Comparator<? super T> com) {
heap = new ArrayList<>();
indexMap = new HashMap<>();
heapSize = 0;
comparator = com;
}
private void put(T value) {
heap.add(value);
indexMap.put(value, heapSize);
heapInsert(heapSize++);
}
public T pop() {
T ans = heap.get(0);
int end = heapSize - 1;
swap(0, end);
heap.remove(end);
indexMap.remove(ans);
heapfy(0, --heapSize);
return ans;
}
//改变堆中的值,重新调整堆;同时向上或向下调整堆
public void resign(T value) {
Integer valueIndex = indexMap.get(value);
heapInsert(valueIndex);//向上调整堆
heapfy(valueIndex ,heapSize);//向下调整堆
}
private void heapfy(int index, int heapSize) {
int left = index * 2 + 1;
while (left < heapSize) {//有孩子的情况
//left + 1 < heapSize表示有右孩子,取两个孩子中较大的孩子的索引
int largest = left + 1 < heapSize &&
comparator.compare(heap.get(left + 1) ,heap.get(left)) > 0 ? left + 1 : left;
//父节点index和较大孩子的节点比较,如果是父节点>=max(孩子节点),无需堆调整,终止循环
largest = comparator.compare(heap.get(largest) ,heap.get(index)) > 0 ?
largest : index;
if (largest == index) {
break;
}
swap(largest, index);//较小值下沉
index = largest;//把下沉后节点的索引赋值给index做父节点,继续和子节点比较
left = index * 2 + 1;
}
}
private void heapInsert(int index) {
//终止循环两种方式:1、index=0 2、arr[index]不比其arr[index父]大了
while (comparator.compare(heap.get(index), heap.get((index - 1) / 2)) > 0) {
swap(index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
private boolean contains(T key) {
return indexMap.containsKey(key);
}
private void swap(int i, int j) {
T o1 = heap.get(i);
T o2 = heap.get(j);
heap.set(i, o2);
heap.set(j, o1);
indexMap.put(o1, j);
indexMap.put(o2, i);
}
private boolean isEmpty() {
return heapSize == 0;
}
private int size() {
return heapSize;
}
public void show() {
heap.forEach(System.out::println);
}
}
n: 数据规模 k: "桶"的个数
In-place: 占用常数内存,不占用额外内存 Out-place: 占用额外内存
