稳定性是指:数组中值相同的元素,排完序之后能不能保证原来的相对次序不变,如果不变则是稳定排序。
选择排序:
选择排序( Selection sort)是一种简单直观的排序算法。它的工作原理是每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。
选择排序算法通过选择和交换来实现排序,其排序流程如下:
(1)首先从原始数组中选择最小的1个数据,将其和位于第1个位置的数据交换。
(2)接着从剩下的n-1个数据中选择次小的1个元素,将其和第2个位置的数据交换
(3)然后,这样不断重复,直到最后两个数据完成交换。最后,便完成了对原始数组的从小到大的排序。
public static void selectSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
int m = i;
for (int j = i; j < n; j++) {
if (arr[m] > arr[j]) {
m = j;
}
}
if (m != i) {
int t;
t = arr[m];
arr[m] = arr[i];
arr[i] = t;
}
}
}
冒泡排序:
原理:
- 比较相邻的两个元素,如果前者比后者大(反之倒序),则交换。
- 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。
- 针对所有的元素重复以上的步骤。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
public static void bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
for (int j = i + 1; j < n; j++) {
if (arr[i] > arr[j]) {
int t;
t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
}
}
插入排序:
有一个已经有序的数据序列,要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序,这个时候就要用到一种新的排序方法一插入排序法,插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序。
插入算法把要排序的数组分成两部分:
- 第一部分包含了这个数组的所有元素,但将最后一个元素除外,
- 而第二部分就只包含这一个元素(即待插入元素)。
- 在第一部分排序完成后,再将这个最后元素插入到已排好序的第一部分中。
public static void insertSort(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; i++) {
int num = arr[i];
int index = i - 1;
while (index >= 0) {
if (num < arr[index]) {
arr[index + 1] = arr[index];
} else {
break;
}
index--;
}
if (num != arr[i]) {
arr[index + 1] = num;
}
}
}
归并排序:
归并排序:( Merge Sort)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法( Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并,使用中牺牲空间换取时间的算法。
归并操作( merge),也叫归并算法,指的是将两个顺序序列合并成一个顺序序列的方法。
package demopacket;
import java.util.*;
public class Solution {
public static void main(String[] args) {
int[] arr = {1, 6, 4, 3, 9, 7, 2, 5,};
process(arr, 0, arr.length - 1);
for (int j : arr) {
System.out.print(j);
System.out.print(" ");
}
}
// 结果:1 2 3 4 5 6 7 9
// Process finished with exit code 0
public static void process(int[] arr, int L, int R) {
if (L == R) {
return;
}
int mid = L + ((R - L) >> 1);
process(arr, L, mid);
process(arr, mid + 1, R);
merge(arr, L, mid, R);
}
private static void merge(int[] arr, int l, int mid, int r) {
int[] help = new int[r - l + 1];
int i = 0;
int p1 = l;
int p2 = mid + 1;
while (p1 <= mid && p2 <= r) {
help[i++] = (arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++]);
}
while (p1 <= mid) {
help[i++] = arr[p1++];
}
while (p2 <= r) {
help[i++] = arr[p2++];
}
for (int k = 0; k < help.length; k++) {
arr[l + k] = help[k];
}
}
}
剑指 Offer 51. 数组中的逆序对
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
限制:
0 <= 数组长度 <= 50000
class Solution {
int num = 0;
public int reversePairs(int[] nums) {
if(nums.length == 0){
return 0;
}
mergeSort(nums,0,nums.length - 1);
return num;
}
public void mergeSort(int[] arr, int left, int right) {
if (left == right) {
return;
}
int mid = left + ((right - left) >> 1);
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
merge(arr, left, mid, right);
}
public void merge(int[] arr, int left, int mid, int right) {
int[] nums = new int[right - left + 1];
int i = 0;
int p1 = left;
int p2 = mid + 1;
while (p1 <= mid && p2 <= right) {
if (arr[p1] <= arr[p2]) {
nums[i++] = arr[p1++];
} else {
nums[i++] = arr[p2++];
num += (mid - p1 + 1);
}
}
while (p1 <= mid) {
nums[i++] = arr[p1++];
}
while (p2 <= right) {
nums[i++] = arr[p2++];
}
for (int j = 0; j < nums.length; j++) {
arr[left + j] = nums[j];
}
}
}
快速排序:
public class Solution {
public static void main(String[] args) {
int[] arr = {1, 6, 4, 3, 9, 7, 2, 5,};
//move(arr, 5);
Quick_Sort(arr, 0, arr.length - 1);
for (int j : arr) {
System.out.print(j);
System.out.print(" ");
}
}
public static void Quick_Sort(int[] arr, int begin, int end) {
if (begin > end)
return;
int tmp = arr[begin];
int i = begin;
int j = end;
while (i != j) {
while (arr[j] >= tmp && j > i)
j--;
while (arr[i] <= tmp && j > i)
i++;
if (j > i) {
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
arr[begin] = arr[i];
arr[i] = tmp;
Quick_Sort(arr, begin, i - 1);
Quick_Sort(arr, i + 1, end);
}
}
public class Solution {
public static void main(String[] args) {
int[] arr = {1, 6, 4, 3, 9, 7, 2, 5,};
quickSort(arr);
for (int j : arr) {
System.out.print(j);
System.out.print(" ");
}
}
public static void quickSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
Sort(arr, 0, arr.length - 1);
}
private static void Sort(int[] arr, int L, int R) {
if (L < R) {
swap(arr, L + (int) (Math.random() * (R - L + 1)), R);
int[] p = partition(arr, L, R);
Sort(arr, L, p[0] - 1);
Sort(arr, p[1] + 1, R);
}
}
public static int[] partition(int[] arr, int L, int R) {
int less = L - 1;
int more = R;
while (L < more) {
if (arr[L] < arr[R]) {
swap(arr, ++less, L++);
} else if (arr[L] > arr[R]) {
swap(arr, --more, L);
} else {
L++;
}
}
swap(arr, more, R);
return new int[]{less + 1, more};
}
public static void swap(int[] arr, int i, int j) {
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
堆排序:
大顶堆:每个节点的值都大于或者等于它的左右子节点的值。
堆排序的基本思想是:1、将带排序的序列构造成一个大顶堆,根据大顶堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素;2、将堆顶元素和最后一个元素交换,然后将剩下的节点重新构造成一个大顶堆;3、重复步骤2,如此反复,从第一次构建大顶堆开始,每一次构建,我们都能获得一个序列的最大值,然后把它放到大顶堆的尾部。最后,就得到一个有序的序列了。
代码如下:
public class Solution {
public static void main(String[] args) {
int[] arr = new int[]{2, 6, 8, 9, 4, 3, 6, 8};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void heapSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i);
}
int heapSize = arr.length;
swap(arr, 0, --heapSize);
while (heapSize > 0) {
heapify(arr, 0, heapSize);
swap(arr, 0, --heapSize);
}
}
public static void heapInsert(int[] arr, int index) {
while (arr[index] > arr[(index - 1) / 2]) {
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
public static void heapify(int[] arr, int index, int heapSize) {
int left = index * 2 + 1;
while (left < heapSize) {
int largest = left + 1 < heapSize && 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;
}
}
public static void swap(int[] arr, int i, int j) {
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
PriorityQueue<Integer> heap = new PriorityQueue<>();//默认小根堆
heap.add(8);
heap.add(4);
heap.add(4);
heap.add(9);
heap.add(10);
heap.add(3);
while (!heap.isEmpty()) {
System.out.println(heap.poll());
}
PriorityQueue<Integer> heap = new PriorityQueue<>(new Comparator<Integer>() {//使用比较器,按照大根堆的顺序排列
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});//默认小根堆
heap.add(8);
heap.add(4);
heap.add(4);
heap.add(9);
heap.add(10);
heap.add(3);
while (!heap.isEmpty()) {
System.out.println(heap.poll());
}
public static void main(String[] args) throws ClassNotFoundException {
PriorityQueue<Integer> heap = new PriorityQueue<>((a, b) -> b - a);//默认小根堆
heap.add(8);
heap.add(4);
heap.add(4);
heap.add(9);
heap.add(10);
heap.add(3);
while (!heap.isEmpty()) {
System.out.println(heap.poll());
}
}
桶排序:
//计算最大值的位数
public static int maxBits(int[] arr) {
int max = Integer.MIN_VALUE;
for (int j : arr) {
max = Math.max(max, j);
}
int res = 0;
while (max != 0) {
res++;
max /= 10;
}
return res;
}
public static void radixSort(int[] arr, int L, int R, int digit) {
final int radix = 10;
int i = 0, j = 0;
//有多少个数就准备多少个辅助空间
int[] bucket = 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]++;
}
for (i = 1; i < radix; i++) {
count[i] = count[i] + count[i - 1];
}
for (i = R; i >= L; i--) {
j = getDigit(arr[i], d);
bucket[count[j] - 1] = arr[i];
count[j]--;
}
for (i = L, j = 0; i <= R; i++, j++) {
arr[i]=bucket[j];
}
}
}
public static int getDigit(int x, int d) {
return ((x / ((int) Math.pow(10, d - 1))) % 10);
}