十大排序算法 Java 代码示例
总览
稳定: 冒泡、插入、归并、基数
不稳定 选择、快速、希尔、堆
名词解释
- n: 数据规模
- k:桶个数
- In-place:占用常用内存
- Out-place:占用额外内存
基础排序算法
主程序代码
package com.zzfan.sort;
public class Main {
public static void main(String[] args) {
int[] arr = new int[10];
SetInitial(arr);
System.out.println("----排序前-----");
Sout(arr);
System.out.println("----排序后-----");
arr = BubbleSort.sort(arr);
Sout(arr);
}
private static void SetInitial(int[] arr) {
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random() * 100);
}
}
private static void Sout(int[] arr) {
for (int anArr : arr) {
System.out.print(anArr + " ");
}
System.out.println();
}
}
1 冒泡排序
代码
public class BubbleSort {
public static int[] sort(int[] sourceArray) {
// 对 arr 进行拷贝,不改变参数内容
int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
for (int i = 1; i < arr.length; i++) {
// 设定一个标记,若为true,则表示此次循环没有进行交换,也就是待排序列已经有序,排序已经完成。
boolean flag = true;
for (int j = 0; j < arr.length - i; j++) {
if (arr[j] > arr[j + 1]) {
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
flag = false;
}
}
if (flag) {
break;
}
}
return arr;
}
}
2 选择排序
代码
package com.zzfan.sort;
import java.util.Arrays;
public class SelectionSort {
public static int[] sort(int[] sourceArray) {
int arr[] = Arrays.copyOf(sourceArray, sourceArray.length);
//一层循环基本都是N-1轮
for (int i = 0; i < arr.length - 1; i++) {
int min = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[min]) {
min = j;
}
}
if (i != min) {
int tmp = arr[i];
arr[i] = arr[min];
arr[min] = tmp;
}
}
return arr;
}
}
//关键点在于找最小下标,基本都是在一层循环里面搞事情。
3 插入排序
代码
package com.zzfan.sort;
import java.util.Arrays;
public class InsertSort {
public static int[] sort(int[] sourceArray) {
int arr[] = Arrays.copyOf(sourceArray, sourceArray.length);
for (int i = 1; i < arr.length; i++) {
int tmp = arr[i];
int j = i;
while (j > 0 && tmp < arr[j - 1]) {//前面的大
arr[j] = arr[j - 1];
j--;
}
if (j != i) {
arr[j] = tmp;
}
}
return arr;
}
}
//后面的拿下来,前面的移到后面,后面的再放上去(放前面)
4 希尔排序
升级版的插入排序,先分组,后插入。
代码
package com.zzfan.sort;
import java.util.Arrays;
public class ShellSort {
public static int[] sort(int[] sourceArray) {
int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
int gap = 1;
while (gap < arr.length / 3) {
gap = gap * 3 + 1;
}
while (gap > 0) {
for (int i = gap; i < arr.length; i++) {
int tmp = arr[i];
int j = i - gap;
while (j >= 0 && arr[j] > tmp) {//前面的大
arr[j + gap] = arr[j];
j -= gap;
}
arr[j + gap] = tmp;
}
gap = (int) Math.floor(gap / 3);
}
return arr;
}
}
排序算法(重要)
1 归并排序
代码
package com.zzfan.sort;
import java.util.Arrays;
public class MergeSort {
static int[] sort(int[] arr) {
int len = arr.length;
if (len < 2) {
return arr;
}
int mid = len / 2;
int[] left = Arrays.copyOfRange(arr, 0, mid);
int[] right = Arrays.copyOfRange(arr, mid, len);
return merge(sort(left), sort(right));
}
private static int[] merge(int[] left, int[] right) {
int[] result = new int[left.length + right.length];
int i = 0;
while (left.length > 0 && right.length > 0) {
if (left[0] <= right[0]) {
result[i++] = left[0];
left = Arrays.copyOfRange(left, 1, left.length);
} else {
result[i++] = right[0];
right = Arrays.copyOfRange(right, 1, right.length);
}
}
while (left.length > 0) {
result[i++] = left[0];
left = Arrays.copyOfRange(left, 1, left.length);
}
while (right.length > 0) {
result[i++] = right[0];
right = Arrays.copyOfRange(right, 1, right.length);
}
return result;
}
}
2 快速排序
代码_01
package com.zzfan.sort;
public class QuickSort {
static void quickSort(int[] arr, int low, int high) {
int i, j, pivot;
if (low > high) {
return;
}
i = low;
j = high;
pivot = arr[low];
while (i < j) {
while (arr[j] >= pivot && i < j) {
j--;
}
while (arr[i] <= pivot && i < j) {
i++;
}
if (i < j) {
Swap(arr, i, j);
}
}
arr[low] = arr[i];
arr[i] = pivot;
quickSort(arr, low, j - 1);
quickSort(arr, j + 1, high);
}
private static void Swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
代码_02
package com.zzfan.sort;
public class QuickSort_02 {
static void sort(int[] sourceArray) {
quickSort(sourceArray, 0, sourceArray.length - 1);
}
private static int[] quickSort(int[] arr, int left, int right) {
if (left < right) {
int partitionIndex = partition(arr, left, right);
quickSort(arr, left, partitionIndex - 1);
quickSort(arr, partitionIndex + 1, right);
}
return arr;
}
private static int partition(int[] arr, int left, int right) {
int pivot = left;
int index = pivot + 1;
for (int i = index; i <= right; i++) {
if (arr[i] <= arr[pivot]) {
Swap(arr, i, index);
index++;
}
}
Swap(arr, pivot, index - 1);
return index - 1;
}
private static void Swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
3 堆排序
代码
package com.zzfan.sort;
public class HeapSort {
static void sort(int[] sourceArr) {
bulid_heap(sourceArr, sourceArr.length);
for (int i = sourceArr.length - 1; i >= 0; i--) {
Swap(sourceArr, 0, i);
heapify(sourceArr, i, 0);
}
}
//n:数组元素个数
public static void bulid_heap(int[] tree, int n) {//创建一个树
int last_node = n - 1;
int parent_node = (last_node - 1) / 2;
for (int i = parent_node; i >= 0; i--) {
heapify(tree, n, i);
}
}
//n:数组元素个数,i:对第i个节点进行heapify()
public static void heapify(int[] arr, int n, int i) {
if (i >= n) {
return;
}
int left = 2 * i + 1;
int right = 2 * i + 2;
int max = i; //对某个节点做heapify时,就是把该节点当作了父节点。
if (left < n && arr[left] > arr[max]) {
max = left;
}
if (right < n && arr[right] > arr[max]) {
max = right;
}
if (max != i) {
Swap(arr, max, i);
heapify(arr, n, max);
}
}
private static void Swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
排序算法(其他)
1 计数排序
计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
2 桶排序
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效,我们需要做到这两点:
- 在额外空间充足的情况下,尽量增大桶的数量
- 使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中
同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要。
3 基数排序
基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。
基数排序 vs 计数排序 vs 桶排序
基数排序有两种方法:
这三种排序算法都利用了桶的概念,但对桶的使用方法上有明显差异:
- 基数排序:根据键值的每位数字来分配桶;
- 计数排序:每个桶只存储单一键值;
- 桶排序:每个桶存储一定范围的数值;