10种排序算法的比较:
名词解释
1.算法的时间复杂度
1.计算时间复杂度的方法:
- ①用常数1代替运行时间中的所有加法常数
- ②修改后的运行次数函数中,只保留最高阶项
- ③去除最高阶项的系数
2.常见的时间复杂度(由小到大)
- ①常数阶O(1)
- 无论代码执行多少行,只要是没有循环等复杂结构,那这个代码的时间复杂度就是O(1)
- ②对数阶O(logn)
- ③线性阶O(n)
- 单层for循环
- ④线性对数阶O(nlogN)
- 时间复杂度为O(logn)的代码循环n遍
- ⑤平方阶 O(n^2)
- 双层n.for循环
- ⑥立方阶O(n^3)
- ⑦K次方阶
3.排序方式
- 1.Out-place:占用额外内存(以空间换时间)
4.稳定性
- 排序后2个相等键值的顺序和排序之前的顺序一致
10种排序算法思想介绍及代码演示分析
1. 冒泡排序
代码演示:
private static int[] bobbleSort(int[] arr){
//冒泡排序 从小到大排序;
int temp = 0;
boolean falg = false;//标识变量,是否进行过交换
for (int i=0;i<arr.length-1;i++){
for (int k=0;k<arr.length-1-i;k++) {
if (arr[k] > arr[k + 1]) {
falg = true;
temp = arr[k];
arr[k] = arr[k + 1];
arr[k + 1] = temp;
}
}
if (!falg){
break; //优化:如果一趟下来没有进行交换,就退出
}else {
falg = false; //重新置为false
}
}
return arr;
}
}
2.选择排序
代码演示:
private static void selectSort(int[] arr){
//选择排序 从小到大排序
for (int i = 0; i < arr.length - 1; i++) {
int min = arr[i];
int j=i;
for (int k=i+1;k<arr.length;k++){
if (arr[k]<min){
min = arr[k];
j = k;
}
}
if (i!=j) {
arr[j] = arr[i];
arr[i] = min;
}
}
}
}
3.插入排序
代码演示:
/**
* 使用for循环嵌套while实现
* @param arr
*/
private static void insertSort2(int[] arr){
for (int i = 1; i < arr.length; i++) {
int insertVal = arr[i]; //要插入的数据
int insertIndex = i-1; //插入的索引位置
while (insertIndex>=0&&arr[insertIndex]>insertVal){
arr[insertIndex+1] = arr[insertIndex];
insertIndex--;
}
arr[insertIndex+1] = insertVal;
}
}
}
4.希尔排序
代码演示:
/**
* 希尔排序 移位法(推荐)
*
* @param arr
*/
private static void shellSort2(int[] arr) {
//对数组进行分组
//增量就是分组后的组数
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
//gap为增量
for (int i = gap; i < arr.length; i++) {
int index = i - gap;
int temp = arr[i];
while (index >= 0 && arr[index] > temp) {
arr[index + gap] = arr[index];
index -= gap;
}
arr[index + gap] = temp;
}
}
}
}
/**
*希尔排序 交换法
*
* @param arr
*/
private static void shellSort(int[] arr) {
int temp;
//将数组分组
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
//gap为步长
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;
}
}
}
}
}
5.归并排序(Out-place)
代码演示:
/**
* 分和并
* @param arr
* @param left
* @param right
* @param temp
*/
public static void fenAndMerge(int[] arr,int left,int right,int[] temp){
if (left<right){
int mid = (left+right)/2;
//向左递归 拆分
fenAndMerge(arr,left,mid,temp);
//向右递归 拆分
fenAndMerge(arr,mid+1,right,temp);
//合并
merge(arr,left,mid,right,temp); //见下方merge()方法
}
}
/**
*
* @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 t = 0;
int j = mid+1;
int i = left;
//比较mid左右两边数据 按大小顺序放入temp中
//1.mid两边都有数据 就一直比较
while (i<=mid&&j<=right){
if (arr[i]<=arr[j]){
temp[t] = arr[i];
i+=1;
t+=1;
}else {
temp[t] = arr[j];
j+=1;
t+=1;
}
}
//2.如果只剩下一边有数据,将数据依次加入到temp中
while (i<=mid){
temp[t] = arr[i];
t+=1;
i+=1;
}
while (j<=right){
temp[t] = arr[j];
t+=1;
j+=1;
}
//3.arr数组中索引是固定的,只是逻辑上拆分,需要将排列后的数组中数据返回指定的位置。
t =0;
int tempLeft = left;
while (tempLeft<=right){
arr[tempLeft]=temp[t];
t+=1;
tempLeft+=1;
}
}
}
6.快速排序
代码演示:
/**
*
* 快速排序:找到一个中间值,依次从左和右向中间值遍历,
* 比中间值小的放到左边,比中间值大的放到右边,递归比较
* @param arr
* @param left 左索引
* @param right 右索引
*/
public static void quickSort(int[] arr,int left,int right){
int l = left;
int r = right;
int temp = 0;
int pivot = arr[(left+right)/2];
//-1 -4 -2 0 3 2 7 9
while (l<r){
while (arr[l]<pivot){
l+=1;
}
while (arr[r]>pivot){
r-=1;
}
//说明左右两边都以中轴划分完成
if (l>=r){
break;
}
//交换数据
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
//[0, 6, 3, 4, 4, 6, 0, 6] 防止出现相同的数字导致死循环 如4,4
if (arr[l]==pivot){
l+=1;
}
if (arr[r]==pivot){
r-=1;
}
}
if (l==r){
l+=1;
r-=1;
}
if (r>left){ //l=r=2
quickSort(arr,left,r);
}
if (l<right){
quickSort(arr,l,right);
}
}
}
7.堆排序
代码演示:
package com.data.struct.sort.heapsort;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
/**
* @author Creepin
* @date 2019/12/20 13:50
* @description 堆排序 时间复杂度O(nlogn)
*/
public class HeapSort {
public static void main(String[] args) {
//临时变量 存放需要交换的数据
int temp = 0;
// int[] arr = {4,6,8,5,9};
// for (int i = arr.length/2-1;i>=0;i--){ //非叶子节点的索引
// heapSort(arr,i,arr.length);
// }
// System.out.println("大顶堆结果:");
// System.out.println(Arrays.toString(arr)); //9,6,8,5,4
//将堆顶元素与末尾元素交换
//重新调整堆结构,满足大顶堆,堆顶元素与末尾元素交换
// for (int j=arr.length-1;j>=0;j--){ //4,6,8,5,9
// temp = arr[j];
// arr[j] = arr[0];
// arr[0] = temp;
// heapSort(arr,0,j);
// }
// System.out.println(Arrays.toString(arr));
//堆排序性能测试
int[] arr = new int[80000];
for (int i = arr.length/2-1;i>=0;i--){ //非叶子节点的索引
heapSort(arr,i,arr.length);
}
Random random = new Random();
for (int i = 0; i < 80000; i++) {
arr[i] = random.nextInt(800000);
}
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = simpleDateFormat.format(date);
System.out.println("排序前的时间:"+dateStr);
long time1 = System.currentTimeMillis();
for (int j = arr.length - 1; j >= 0; j--) { //4,6,8,5,9
temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
heapSort(arr, 0, j);
}
Date date2 = new Date();
String date2Str = simpleDateFormat.format(date2);
System.out.println("排序后的时间:"+date2Str);
long long2 = System.currentTimeMillis();
System.out.println("排序消耗时间:"+(long2-time1)+"ms");
}
/**
*
* @param arr 需要进行排序的数组
* @param i 非叶子节点索引
* @param length 数组长度
*/
private static void heapSort(int[] arr,int i,int length){
int temp = arr[i];
//根据顺序存储二叉树,由非叶子节点可以求出左子节点和右子节点
//左子节点:i*2+1;右子节点:i*2+2
//遍历左子节点
for (int k= i*2+1;k<length;k=2*k+1){
//比较左右节点的大小
if ((k+1)<length&&arr[k]<arr[k+1]){
//k指向左右子树中较大值的索引
k++;
}
if (temp<arr[k]){
arr[i] = arr[k];
i=k;//从当前非叶子节点 再次遍历
}else{
break;
}
arr[i] = temp; //相当于arr[k] 已经做i=k赋值处理过
}
}
}
8.基数排序
代码演示:
public static void radixSort(int[] arr) {
//1.求出arr数组中的最大值
int max = arr[0];
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
//2.求出最大值的位数
int length = String.valueOf(max).length();
//基数排序通过二维数组保存原数组中的数据
int[][] bucket = new int[10][arr.length];
//3.每个桶中的数据个数不固定,使用一维数组记录每个桶中的元素个数
int[] bucketElementCount = new int[10];
//5.由单轮规矩总结
for (int m=0,n=1;m<length;m++,n*=10){
//4.第一轮 按个位数数字放入到桶中
for (int i = 0; i < arr.length; i++) {
int elementIndex = arr[i] /n % 10;
bucket[elementIndex][bucketElementCount[elementIndex]] = arr[i];
bucketElementCount[elementIndex]++;
}
//5.取出桶中的数据放入原数组中
//遍历桶
int index = 0;
for (int j = 0; j < bucket.length; j++) {
//如果桶中有数据,取出并放入到原数组中
if (bucketElementCount[j] > 0) {
//遍历每个桶中的元素
for (int k = 0; k < bucketElementCount[j]; k++) {
arr[index] = bucket[j][k];
index++;
}
}
//每一轮处理结束需要将bucketElementCount置为0
bucketElementCount[j] = 0;
}
}
}
}
未完待续…