【算法】排序算法(基数、归并、快速、希尔、插入、选择、冒泡)
1、基数排序
- 基数排序(radix sort)又称桶排序(bucket sort),相对于常见的比较排序,基数排序是一种分配式排序,即通过将所有数字分配到应在的位置最后再覆盖到原数组完成排序的过程。
1.1 图解
1.2 代码实现
package com.data.algorithm.sort;
/**
* 基数排序
*
* @author wangjie
* @version V1.0
* @date 2020/3/29
*/
public class RadixSort {
public static void main(String[] args) {
int[] arr = new int[800000];
for (int i = 0; i < 800000; ++i) {
arr[i] = (int) (Math.random() * 800000.0D);
}
Long startTime = System.currentTimeMillis();
System.out.println("开始排序");
radixSort(arr);
Long endTime = System.currentTimeMillis() - startTime;
System.out.println("排序耗时:" + endTime + "毫秒");
System.out.println("排序耗时:" + endTime / 1000 + "秒");
}
/**
* 基数排序
* @param arr
*/
public static void radixSort(int[] arr) {
//1,找出数组中最大数的位数
int max = arr[0];
int maxLength;
for(maxLength = 1; maxLength < arr.length; ++maxLength) {
if (arr[maxLength] > max) {
max = arr[maxLength];
}
}
maxLength = String.valueOf(max).length();
//2、定义一个二维数组,表示10个桶
int[][] bucket = new int[10][arr.length];
//3、定义一个一维数组,来记录每个桶里有几个数据
int[] bucketElementCounts = new int[10];
//4、循环
for(int n = 1,i = 0; i < maxLength; i++,n *= 10) {
for(int j = 0; j < arr.length; ++j) {
int of = arr[j] / n % 10;
bucket[of][bucketElementCounts[of]] = arr[j];
++bucketElementCounts[of];
}
for(int k = 0,index = 0; k < bucketElementCounts.length; ++k) {
if (bucketElementCounts[k] != 0) {
for(int l = 0; l < bucketElementCounts[k]; ++l) {
arr[index++] = bucket[k][l];
}
}
bucketElementCounts[k] = 0;
}
}
}
}
2、归并排序
- 归并排序是分治法的一个典型应用,其思想就是先递归分解数组,再合并数组。
2.1 图解
2.2 代码实现
package com.data.algorithm.sort;
/**
* 归并排序
*
* @author wangjie
* @version V1.0
* @date 2020/3/25
*/
public class MergetSort {
public static void main(String[] args) {
int[] arr = new int[8000000];
for (int i = 0; i < 8000000; ++i) {
arr[i] = (int) (Math.random() * 80000000.0D);
}
int[] temp = new int[arr.length];
Long startTime = System.currentTimeMillis();
System.out.println("开始排序");
mergeSort(arr,0,arr.length-1,temp);
Long endTime = System.currentTimeMillis() - startTime;
System.out.println("排序耗时:" + endTime + "毫秒");
System.out.println("排序耗时:" + endTime / 1000 + "秒");
}
/**
* 归并排序--归并
* @param arr
* @param left
* @param right
* @param temp
*/
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
if (left < right) {
int mid = (left + right) / 2;
mergeSort(arr, left, mid, temp);
mergeSort(arr, mid + 1, right, temp);
merge(arr, left, mid, right, temp);
}
}
/**
* 归并排序--排序
* @param arr
* @param left
* @param mid
* @param right
* @param temp
*/
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
int i = left;
int j = mid + 1;
int t = 0;
while(i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[t] = arr[i];
++t;
++i;
} else {
temp[t] = arr[j];
++t;
++j;
}
}
while(i <= mid) {
temp[t] = arr[i];
++t;
++i;
}
while(j <= right) {
temp[t] = arr[j];
++t;
++j;
}
t = 0;
for(int tempLeft = left; tempLeft <= right; ++tempLeft) {
arr[tempLeft] = temp[t];
++t;
}
}
}
3、快速排序
- 快速排序是一种排序执行效率很高的排序算法,它利用分治法来对待排序序列进行分治排序,它的思想主要是通过一趟排序将待排记录分隔成独立的两部分,其中的一部分比关键字小,后面一部分比关键字大,然后再对这前后的两部分分别采用这种方式进行排序,通过递归的运算最终达到整个序列有序。
3.1 图解
3.2 代码实现
package com.data.algorithm.sort;
/**
* 快速排序
*
* @author wangjie
* @version V1.0
* @date 2020/3/24
*/
public class QuickSort {
public static void main(String[] args) {
int[] arr = new int[8000000];
for (int i = 0; i < 8000000; ++i) {
arr[i] = (int) (Math.random() * 80000000.0D);
}
Long startTime = System.currentTimeMillis();
System.out.println("开始排序");
quickSort(arr,0,arr.length-1);
Long endTime = System.currentTimeMillis() - startTime;
System.out.println("排序耗时:" + endTime + "毫秒");
System.out.println("排序耗时:" + endTime / 1000 + "秒");
}
/**
* 快速排序
*
* @param
* @param left
* @param right
*/
public static void quickSort(int[] arr, int left, int right) {
int l = left;
int r = right;
int pivot = arr[(left + right) / 2];
boolean var6 = false;
while(l < r) {
while(arr[l] < pivot) {
++l;
}
while(arr[r] > pivot) {
--r;
}
if (l >= r) {
break;
}
int temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
if (arr[l] == pivot) {
--r;
}
if (arr[r] == pivot) {
++l;
}
}
if (l == r) {
++l;
--r;
}
if (left < r) {
quickSort(arr, left, r);
}
if (right > l) {
quickSort(arr, l, right);
}
}
}
4、希尔排序
- 希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因DL.Shell于1959年提出而得名。 希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
4.1 图解
4.2 代码实现
package com.data.algorithm.sort;
/**
* 希尔排序
*
* @author wangjie
* @version V1.0
* @date 2020/3/7
*/
public class ShellSort {
public static void main(String[] args) {
int[] arr = new int[8000000];
for (int i = 0; i < 8000000; ++i) {
arr[i] = (int) (Math.random() * 80000000.0D);
}
int[] arr2 = arr.clone();
Long startTime = System.currentTimeMillis();
System.out.println("开始排序");
shellSort2(arr);
Long endTime = System.currentTimeMillis() - startTime;
System.out.println("排序耗时:" + endTime + "毫秒");
System.out.println("排序耗时:" + endTime / 1000 + "秒");
Long startTime1 = System.currentTimeMillis();
System.out.println("开始排序");
shellSort3(arr2);
Long endTime1 = System.currentTimeMillis() - startTime1;
System.out.println("排序耗时:" + endTime1 + "毫秒");
System.out.println("排序耗时:" + endTime1 / 1000 + "秒");
}
/**
* 替换法希尔排序,效率不高
* @param arr
*/
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;
}
}
}
}
}
/**
* 移位法希尔排序
* @param arr
*/
public static void shellSort2(int[] arr) {
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; ++i) {
int j = i;
int temp = arr[i];
if (arr[i] < arr[i - gap]) {
while (j - gap >= 0 && temp < arr[j - gap]) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = temp;
}
}
}
}
/**
* 移位法希尔排序2
* @param arr
*/
public static void shellSort3(int[] arr) {
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; ++i) {
int temp = arr[i];
int insertIndex;
for(insertIndex = i - gap; insertIndex >= 0 && temp < arr[insertIndex]; insertIndex -= gap) {
arr[insertIndex + gap] = arr[insertIndex];
}
if (insertIndex + gap != i) {
arr[insertIndex + gap] = temp;
}
}
}
}
}
5、插入排序
- 插入排序(英语:Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
5.1 图解
5.2 代码实现
package com.data.algorithm.sort;
/**
* 插入排序
*
* @author wangjie
* @version V1.0
* @date 2020/3/6
*/
public class InsertSort {
public static void main(String[] args) {
int[] arr = new int[80000];
for(int i = 0; i < 80000; ++i) {
arr[i] = (int)(Math.random() * 8000000.0D);
}
Long startTime = System.currentTimeMillis();
System.out.println("开始排序");
insertSort(arr);
Long endTime = System.currentTimeMillis()- startTime;
System.out.println("排序耗时:" + endTime+"毫秒");
System.out.println("排序耗时:" + endTime/1000+"秒");
}
/**
* 插入排序
* @param arr
*/
public static void insertSort(int[] arr) {
int insertVal ;
int insertIndex;
for(int i = 1; i < arr.length; ++i) {
insertVal = arr[i];
for(insertIndex = i - 1; insertIndex >= 0 && insertVal < arr[insertIndex]; --insertIndex) {
arr[insertIndex + 1] = arr[insertIndex];
}
if (insertIndex + 1 != i) {
arr[insertIndex + 1] = insertVal;
}
}
}
}
6、选择排序
6.1 代码实现
package com.data.algorithm.sort;
/**
* 选择排序
*
* @author wangjie
* @version V1.0
* @date 2020/3/6
*/
public class SelectSort {
public static void main(String[] args) {
int[] arr = new int[80000];
for (int i = 0; i < 80000; ++i) {
arr[i] = (int) (Math.random() * 8000000.0D);
}
Long startTime = System.currentTimeMillis();
System.out.println("开始排序");
selectSort(arr);
Long endTime = System.currentTimeMillis() - startTime;
System.out.println("排序耗时:" + endTime + "毫秒");
System.out.println("排序耗时:" + endTime / 1000 + "秒");
}
/**
* 选择排序
* @param arr
*/
public static void selectSort(int[] arr) {
for (int i = 0; i < arr.length - 1; ++i) {
int minIndex = i;
int min = arr[i];
for (int j = i + 1; j < arr.length; ++j) {
if (min > arr[j]) {
min = arr[j];
minIndex = j;
}
}
if (minIndex != i) {
arr[minIndex] = arr[i];
arr[i] = min;
}
}
}
}
7、冒泡排序
7.1 代码实现
package com.data.algorithm.sort;
/**
* 冒泡排序
*
* @author wangjie
* @version V1.0
* @date 2020/3/6
*/
public class BubbleSort {
public static void main(String[] args) {
int[] arr = new int[80000];
for(int i = 0; i < 80000; ++i) {
arr[i] = (int)(Math.random() * 8000000.0D);
}
Long startTime = System.currentTimeMillis();
System.out.println("开始排序");
bubbleSort(arr);
Long endTime = System.currentTimeMillis()- startTime;
System.out.println("排序耗时:" + endTime+"毫秒");
System.out.println("排序耗时:" + endTime/1000+"秒");
}
/**
* 冒泡排序
* @param arr
*/
public static void bubbleSort(int[] arr) {
int temp;
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;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
if (!flag) {
break;
}
flag = false;
}
}
}
8、堆排序
8.1 代码实现
package com.data.algorithm.sort;
import java.util.Arrays;
/**
* 堆排序
*
* @author wangjie
* @version V1.0
* @date 2020/7/17
*/
public class HeapSort {
public static void main(String[] args) {
int[] arr = new int[80000];
for(int i = 0; i < 80000; ++i) {
arr[i] = (int)(Math.random() * 8000000.0D);
}
Long startTime = System.currentTimeMillis();
System.out.println("开始排序");
heapSort(arr);
Long endTime = System.currentTimeMillis()- startTime;
System.out.println("排序耗时:" + endTime+"毫秒");
System.out.println("排序耗时:" + endTime/1000+"秒");
}
/**
* 堆排序
* @param arr
*/
public static void heapSort(int arr[]){
int temp;
for(int i=arr.length/2-1;i >= 0;i--){
adjusHeap(arr,i,arr.length);
}
for(int j = arr.length - 1;j > 0;j--){
temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
adjusHeap(arr,0,j);
}
}
/**
* 将i对应的非叶子节点的树调整成大顶堆
* @param arr 待调整数组
* @param i 非叶子结点在数组中的索引
* @param len 多少个元素继续调整
*/
public static void adjusHeap(int arr[],int i,int len){
int temp = arr[i];
//k = i*2+1是i结点的左子结点
for(int k = i*2+1;k < len;k = k *2+1 ){
if(k+1 < len && arr[k] < arr[k+1]){
k++;
}
if(arr[k] > temp){
arr[i] = arr[k];
i = k;
}else{
break;
}
}
arr[i] = temp;
}
}
9、 常用排序算法对比
- 相关术语解释: