选泡插
快归堆希
桶计基
private static void swap(int[] arr, int j, int i) {
int temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
选择排序
n^2 不稳
遍历找到最小的数,放到最前,依次
遍历一遍,最小值下标指向最小值,跟第一个数交换
for (int j = 0; j < arr.length; j++) {
int minpos = 0;
for (int i = 1; i < arr.length; i++) {
if (arr[minpos] < arr[i]) {
minpos = i;
}
swap(arr,i,minpos);
}
}
冒泡排序
n^2 稳
第一个数与第二个数比较,谁大谁向后移,第一遍找到最大数
for (int j = 0 ; j<arr.length ;j++){
for(int i=0; i<arr.length-1-j;i++){ // j位后面的数已经有序,不用再排
if(arr[i]>arr[i+1]){
swap(arr,i,i+1);
}
}
}
插入排序
n^2 稳
第二个数与第一个数比较,谁小谁向前移
第三个数与其前面数比较,小向前移
(类似向前的冒泡排序)
for (int i = 1; i < arr.length ; i++) {
for (int j = i ;j >0 ; j--){
if(arr[j-1]>arr[j]){
swap(arr,j-1,j);
}
}
}
希尔排序
取gap间隔的数排序
向后移动一位取间隔排序
间隔减小再排
到间隔为一再排,此时等于插入排序
for (int gap=arr.length/2; gap>0; gap /= 2){ //gap减小到1
for (int j = 0; j < arr.length; j++) { //以gap分组全部排序
for (int i = j; i > gap-1; i-=gap) { //以gap排一次,gap-1将arr[0]加入排序
if (arr[i]<arr[i-gap])
swap(arr,i,i-gap);
}
}
}
归并排序
N*logN:没有浪费比较信息
Java对象排序专用:TimSort 将数组分为多个小块,小块用binarySort排序,后用归并排序排小块
-
第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
-
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置
-
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
-
重复步骤3直到某一指针超出序列尾
-
将另一序列剩下的所有元素直接复制到合并序列尾
import java.util.Arrays;
public class Test6 {
public static void main(String[] args) {
int[] arr = {5,2,8,4,3,6,7,9,1};
sort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
public static void sort(int[] arr, int left, int right){
//循环退出条件:只剩一个数返回,两个数开始排序
if (left == right) return;
//分成两组,递归直到只剩两个数进行merge排序,返回排好序的数组
int mid = left + (right-left)/2;
sort(arr,left,mid);
sort(arr,mid+1,right);
//merge排序,将有序的两数组排序,只有两个数时一个数当作一个数组
merge(arr,left,mid+1,right);
}
public static void merge(int[] arr,int leftPtr,int rightPtr,int rightBound) {
int mid = rightPtr-1;
int[] temp = new int[rightBound-leftPtr+1];
int i = 0;
int saveLeftPtr = leftPtr;
/*while (leftPtr<=mid && rightPtr<=rightBound){
if (arr[leftPtr] <= arr[rightPtr]){
temp[i] = arr[leftPtr];
i++;
leftPtr++;
}else {
temp[i] = arr[rightPtr];
i++;
rightPtr++;
}
}*/
while (left<=mid && right<=rightBound)
res[r++] = arr[left] < arr[right] ? arr[left++] : arr[right++];
while (leftPtr <= mid)temp[i++] = arr[leftPtr++];//必须 <= ,放入最后一个数
while (rightPtr <= rightBound)temp[i++] = arr[rightPtr++];
//将结果存入arr数组,用于递归,同一个数组
for (int j = 0; j < temp.length; j++) {
arr[saveLeftPtr+j] = temp[j];
}
}
}
求解问题
求数组中累计比左边或右边大、小的数
计算小和
小和:数组中,每一个数左边比当前数小的数累加起来
使用归并排序:计算右边有几个数比当前数大
不重复,不漏地:计算范围内有几个数比其大,范围扩大后再计算
import java.util.Arrays;
public class SmallSum {
public static void main(String[] args) {
int[] arr = {1,4,2};
int smallSum = smallSum(arr, 0, arr.length - 1);
System.out.println(smallSum);
}
private static int smallSum(int[] arr,int start,int end) {
if (start==end) return 0;
int mid = start + (end-start)/2;
int a = smallSum(arr, start, mid);
int b = smallSum(arr, mid + 1, end);
int c = merge(arr, start, mid + 1, end);
return a+b+c;
}
private static int merge(int[] arr,int left,int right,int rightBound){
int[] res = new int[rightBound-left+1];
int r = 0;
int mid = right-1;
int saveLeft = left;
int smallSum = 0;
while (left<=mid && right<=rightBound)
while (left<=mid && right<=rightBound){
if (arr[left] < arr[right]){
res[r] = arr[left];
//小于时计算小和
smallSum += arr[left] * (rightBound-right+1); //计算比其大的数的个数需+1
r++;
left++;
}else {
res[r] = arr[right];
r++;
right++;
}
}
while (left<=mid)res[r++] = arr[left++];
while (right<=rightBound)res[r++] = arr[right++];
System.out.println(Arrays.toString(res));
for (int i = 0; i < res.length; i++) {
arr[saveLeft+i] = res[i];
}
return smallSum;
}
}
计数排序
范围:count.length-1
import java.util.Arrays;
public class CountSort {
public static void main(String[] args) {
int[] arr = {5,1,8,3,6,9,2,7,4,2,3};
int[] result = countSort(arr);
System.out.println(Arrays.toString(result));
}
private static int[] countSort(int[] arr) {
int[] result = new int[arr.length];
int[] count = new int[10];
for (int i = 0; i < arr.length; i++) {
count[arr[i]]++;
}
for (int i = 0,n = 0; i < count.length; i++) {
for (int j = 0; j < count[i]; j++) {
result[n] = i;
n++;
}
}
return result;
}
}
稳定
import java.util.Arrays;
public class CountSort {
public static void main(String[] args) {
int[] arr = {5,1,8,3,6,9,2,7,4,2,3};
int[] result = countSort(arr);
System.out.println(Arrays.toString(result));
}
private static int[] countSort(int[] arr) {
int[] result = new int[arr.length];
int[] count = new int[11];
for (int i = 0; i < arr.length; i++) {
count[arr[i]]++;
}
System.out.println(Arrays.toString(count));
/*for (int i = 0,n = 0; i < count.length; i++) {
for (int j = 0; j < count[i]; j++) {
result[n] = i;
n++;
}
}*/
for (int i = 1; i < count.length; i++) { //获取累加数组
count[i] = count[i] + count[i-1]; //桶中相同数最后出现下标
}
for (int i = arr.length-1; i >= 0; i--) { //arr原数组倒叙复制到result,不改变相同数顺序
result[--count[arr[i]]] = arr[i]; //累加数组下标减一就是相同数出现的最后一次,赋予
} //--count[arr[i]]数组最后出现的下标
return result;
}
}
基数排序
先排个位数,相同放在一个桶中
再排十位数,相同放在一个桶中
import java.util.Arrays;
public class Test10 {
public static void main(String[] args) {
int[] arr = {122, 525, 634, 42, 15, 755, 124, 362};
sort(arr);
System.out.println(Arrays.toString(arr));
}
private static void sort(int[] arr) {
int[] count = new int[10];
int[] result = new int[arr.length];
for (int i = 0; i < 3; i++) {
int pow = (int) Math.pow(10, i);
for (int j = 0; j < arr.length; j++) {
int c = (arr[j] / pow) % 10;
count[c]++;
}
for (int j = 1; j < count.length; j++) {
count[j] = count[j] + count[j - 1]; //获取累加数组
}
for (int j = arr.length-1; j >= 0; j--) {
int num = arr[j]/pow%10; //num为0-9的count[]数组的下标
result[--count[num]] = arr[j];
}
Arrays.fill(count, 0);
System.arraycopy(result, 0, arr, 0, result.length);
}
}
}
数组清零
int[] count = new int[10];
Arrays.fill(count,0);
数组复制
System.arrayCopy(srcBytes,0,destBytes,0,5);
// 将srcBytes源数组中 从0位 到 第5位之间的数值 copy 到 destBytes目标数组中,在目标数组的第0位开始放置.
//目标数组必须已经存在,且不会被重构,相当于替换目标数组中的部分元素。
快速排序
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args) {
int[] arr = {2,5,1,5,8,6,9,4,1,1};
sort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
private static void sort(int[] arr,int leftBound,int rightBound) {
if (leftBound < rightBound && rightBound>=0) {
int[] p = partition(arr, leftBound, rightBound);
sort(arr, leftBound, p[0] - 1);
sort(arr,p[1]+1,rightBound);
}
}
private static int[] partition(int[] arr, int leftBound, int rightBound) {
int pivot = rightBound; //支点
int i = leftBound; //遍历数组下标
while (i<rightBound){
if (arr[i] < arr[pivot]){ //当前数小于支点:当前数与小于区域前一个数交换,小于区域扩大
swap(arr,i,leftBound);
leftBound++;
i++;
}else if (arr[i]>arr[pivot]){ //大于:与大于区域前一个数交换,大于区扩大,i不变
swap(arr,leftBound,rightBound-1);
rightBound--;
}else { //等于:下一个数,小于区扩大
leftBound++;
i++;
}
}
swap(arr,leftBound,pivot);
return new int[]{leftBound,rightBound};
}
private static void swap(int[] arr, int j, int i) {
int temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}
Java源码
/*
* Partitioning degenerates to the traditional 3-way
* (or "Dutch National Flag") schema:
*
* left part center part right part
* +-------------------------------------------------+
* | < pivot | == pivot | ? | > pivot |
* +-------------------------------------------------+
* ^ ^ ^
* | | |
* less k great
*
* Invariants:
*
* all in (left, less) < pivot
* all in [less, k) == pivot
* all in (great, right) > pivot
*
* Pointer k is the first index of ?-part.
*/
for (int k = less; k <= great; ++k) {
if (a[k] == pivot) {
continue;
}
int ak = a[k];
if (ak < pivot) { // Move a[k] to left part
a[k] = a[less];
a[less] = ak;
++less;
} else { // a[k] > pivot - Move a[k] to right part
while (a[great] > pivot) {
--great;
}
if (a[great] < pivot) { // a[great] <= pivot
a[k] = a[less];
a[less] = a[great];
++less;
} else { // a[great] == pivot
/*
* Even though a[great] equals to pivot, the
* assignment a[k] = pivot may be incorrect,
* if a[great] and pivot are floating-point
* zeros of different signs. Therefore in float
* and double sorting methods we have to use
* more accurate assignment a[k] = a[great].
*/
a[k] = pivot;
}
a[great] = ak;
--great;
}
}
堆排序
package sort.heap;
import java.util.Arrays;
//大根堆,堆排序
public class Heap {
public static void main(String[] args) {
int[] arr = {2, 6, 4, 8, 3};
sort(arr);
}
private static void sort(int[] arr) {
//逐个加入大根堆,根节点就是最大值
for (int i = 0; i < arr.length; i++) {
//每次加入数,heapInsert后都形成大根堆
heapInsert(arr, i);
}
System.out.println(Arrays.toString(arr));
//依次取出最大数
int heapSize = arr.length;
swap(arr, 0, --heapSize);
while (heapSize > 0) {
//heapify后根节点由孩子中大的占据,形成大根堆
heapify(arr, 0, heapSize);
//将尾部最后一个节点与根节点交换,最大值在数组尾部
swap(arr, 0, --heapSize);
}
System.out.println(Arrays.toString(arr));
}
private static void heapify(int[] arr, int index, int heapSize) {
int left = 2 * index + 1;
//判断是否还有左孩子
while (left < heapSize) {
//判断两子谁大
int largest = left + 1 < heapSize && //判断是否有右孩子,没有则left大
arr[left + 1] > arr[left] ? left + 1 : left;
//判断父与子谁大
largest = arr[largest] > arr[index] ? largest : index;
//父是最大值,结束
if (largest == index) {
break;
}
swap(arr, largest, index);
index = largest;
left = index * 2 + 1;
}
}
private static void heapInsert(int[] arr, int index) {
while (arr[index] > arr[(index - 1) / 2]) { //不比父大,或到0 停止
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
private static void swap(int[] arr, int j, int i) {
int temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}