排序算法的介绍:
分类:
- 内部排序(使用内存):插入排序(直接插入排序、希尔排序)、选择排序(简单选择排序、堆排序)、交换排序(冒泡排序、快速排序)、归并排序、基数排序。
- 外部排序(使用内存和外存结合):
算法的时间复杂度:
时间频度:一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。
- T(n):算法中的基本操作语句的重复执行次数是问题规模n的某个函数,
- 辅助函数f(n):当n趋近于无穷大时,T(n) / f(n) 的极限值为不等于零的常数,称f(n)是T(n)的同数量级函数,
- 记作 T(n)=O( f(n) ),称O( f(n) ) 为算法的渐进时间复杂度,简称时间复杂度。
计算时间复杂度的方法:
- 用常数1代替运行时间中的所有加法常数 T(n)=n²+7n+6 => T(n)=n²+7n+1
- 修改后的运行次数函数中,只保留最高阶项 T(n)=n²+7n+1 => T(n) = n²
- 去除最高阶项的系数 T(n) = n² => T(n) = n² => O(n²)
常见的时间复杂度:
常数阶O(1)、对数阶O(log2n)、线性阶O(n)、线性对数阶O(nlog2n)、平方阶O(n^2)、立方阶O(n^3)、k次方阶O(n^k)、指数阶O(2^n)。规模增大,效率越低。
平均时间复杂度和最坏时间复杂度:
算法的空间复杂度(Space Complexity):该算法所耗费的存储空间,它也是问题规模n的函数。
冒泡排序
代码实现:
package com.jiao.algorithm.sort;
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args) {
//时间复杂度:O(n^2) n=7个数 n-1=6比较次数 n-1-i=6,5,4,3,2,1每轮比次数
int[] arr = {10,8,6,5,3,-1,-2}; //定义数组放要排序的数字
System.out.println("原数字为:"+Arrays.toString(arr));
int temp; //用于比较的临时变量
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]) { //从小到大排序
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
System.out.printf("第 %d 轮比较后的数组为:%s \n",i+1, Arrays.toString(arr));
}
}
}
测试结果:
优化冒泡排序:如果某一次排序位置都不变,直接break退出。(flag标识符)
package com.jiao.algorithm.sort;
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args) {
//时间复杂度:O(n^2) n=7个数 n-1=6比较次数 n-1-i=6,5,4,3,2,1每轮比次数
int[] arr = {10,12,6,7,3,-1,-2}; //定义数组放要排序的数字
System.out.println("原数字为:"+Arrays.toString(arr));
bubbleSort(arr);
System.out.printf("排序后的数组为:%s \n", Arrays.toString(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;
}
}
//System.out.printf("第 %d 轮比较后的数组为:%s \n",i+1, Arrays.toString(arr));
if (!flag) {
break; // 该次没有交换,说明剩下的已经排好序,直接退出
} else {
flag = false; // 复位
}
}
}
}
测试8000个随机数排序所耗时间:
package com.jiao.algorithm.sort;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class BubbleSort {
public static void main(String[] args) {
//生成80000个随机数
int[] array = new int[80000];
for (int i = 0; i < 80000; i++) {
array[i] = (int)(Math.random() * 80000); // [0,1)*80000
}
Date date1 = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr1 = sdf.format(date1);
System.out.println("排序前时间:"+dateStr1);
bubbleSort(array);
Date date2 = new Date();
String dateStr2 = sdf.format(date2);
System.out.println("排序后时间:"+dateStr2);
}
//封装
public static void bubbleSort(int[] arr) {
...
}
}
测试时间:
选择排序
代码实现:
package com.jiao.algorithm.sort;
import java.util.Arrays;
public class SelectSort {
public static void main(String[] args) {
int[] arr = {101,34,119,5,62,36,84,2};
System.out.println("选择排序前为:"+Arrays.toString(arr));
selectSort(arr);
System.out.println("选择排序后为:"+Arrays.toString(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 (i != minIndex) { //最小值下标和当前的arr[i]下标一样,说明arr[i]为最小,不需要交换
arr[minIndex] = arr[i];
arr[i] = min;
}
System.out.printf("第%d轮排序后:%s \n",i+1,Arrays.toString(arr));
}
}
}
测试结果:
测试8000个随机数排序所耗时间:
插入排序
代码:
package com.jiao.algorithm.sort;
import java.util.Arrays;
public class InsertSort {
public static void main(String[] args) {
int[] arr = {89,76,44,32,12,4};
System.out.println("插入排序前的数据:"+Arrays.toString(arr));
insertSort(arr);
System.out.println("插入排序后的数据:"+Arrays.toString(arr));
}
//封装
public static void insertSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) { //轮数
int insertVal = arr[i+1]; //待插入的数
int insertIndex = i; //待插入数的前一个坐标
while (insertIndex >= 0 && insertVal < arr[insertIndex]) { //假如还有要比较的数 或者 待插入的数与前者相比较小
arr[insertIndex + 1] = arr[insertIndex]; //待比较的数后移
insertIndex--;
}
arr[insertIndex+1] = insertVal; //最后比较完了, 最小的数下标就是i, 把待插入的数放进去就可以了
System.out.printf("第%d轮的数据为:%s \n",i+1, Arrays.toString(arr));
}
}
}
测试结果:
测试8000个随机数排序所耗时间:
优化(但是时间差不多):
...
if (insertIndex+1 != i) { //优化,假如插入的数已经在此轮比前一个比较大,不需要后移
arr[insertIndex+1] = insertVal; //最后比较完了, 最小的数下标就是i, 把待插入的数放进去就可以了
}
...
希尔排序 (一种插入排序:缩小增量排序)
交换法-代码:【难以理解】
package com.jiao.algorithm.sort;
import java.util.Arrays;
public class ShellSort {
public static void main(String[] args) {
int[] arr = {8,9,1,7,2,3,5,4,6,0};
System.out.println("原始数据是:"+Arrays.toString(arr));
shellSortByChange(arr);
}
//封装 插入用交换法
public static void shellSortByChange(int[] arr) {
int temp = 0;
int num = 1; //轮数统计
for (int gap = arr.length/2; gap > 0 ; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
//遍历各组的元素(gap组,每组len/gap个元素),步长gap
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;
}
}
}
System.out.printf("第%d轮后的排序为:%s\n",num,Arrays.toString(arr));
num++;
}
}
//交换法 分步骤分析
public static void shellSortByChangeTest(int[] arr) {
int temp = 0;
//第一轮,10个数分5组
for (int i = 5; i < arr.length; i++) {
//遍历各组的元素(5组,每组2个元素),步长5
for (int j = i-5; j>=0 ; j -= 5) {
if (arr[j] > arr[j+5]) {
temp = arr[j];
arr[j] = arr[j+5];
arr[j+5] = temp;
}
}
}
System.out.println("第一轮后的排序为:"+ Arrays.toString(arr));
//第二轮,10个数分2组
for (int i = 2; i < arr.length; i++) {
//遍历各组的元素(2组,每组5个元素),步长2
for (int j = i-2; j>=0 ; j -= 2) {
if (arr[j] > arr[j+2]) {
temp = arr[j];
arr[j] = arr[j+2];
arr[j+2] = temp;
}
}
}
System.out.println("第二轮后的排序为:"+ Arrays.toString(arr));
//第三轮,10个数分1组
for (int i = 1; i < arr.length; i++) {
//遍历各组的元素(5组,每组2个元素),步长5
for (int j = i-1; j>=0 ; j -= 1) {
if (arr[j] > arr[j+1]) {
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
System.out.println("第三轮后的排序为:"+ Arrays.toString(arr));
}
}
测试结果:
80000数据进行测试时间:
移位法-代码:【重要】
package com.jiao.algorithm.sort;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class ShellSort {
public static void main(String[] args) {
/*int[] arr = {8,9,1,7,2,3,5,4,6,0};
System.out.println("原始数据是:"+Arrays.toString(arr));
shellSortByChange(arr);*/
//生成80000个随机数
int[] array = new int[80000];
for (int i = 0; i < 80000; i++) {
array[i] = (int)(Math.random() * 80000); // [0,1)*80000
}
Date date1 = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr1 = sdf.format(date1);
System.out.println("排序前时间:"+dateStr1);
shellSortByMove(array);
Date date2 = new Date();
String dateStr2 = sdf.format(date2);
System.out.println("排序后时间:"+dateStr2);
}
//希尔排序 插入用移位发
public static void shellSortByMove(int[] arr) {
//增量gap,并逐步缩小增量
for (int gap = arr.length/2; gap > 0 ; gap /=2) {
//从gap个元素,逐个对其所在的组进行直接插入排序
for (int i = gap; i < arr.length ; i++) {
int j = i;
int temp = arr[j]; //临时变量
if (arr[j] < arr[j - gap]) {
while (j-gap >=0 && temp < arr[j-gap]) { //移动
arr[j] = arr[j-gap];
j -= gap;
}
//当退出while循环,就把temp给找到的位置
arr[j] = temp;
}
//System.out.println(Arrays.toString(arr));
}
}
}
//封装 插入用交换法
public static void shellSortByChange(int[] arr) {
...
}
//交换法 分步骤分析
public static void shellSortByChangeTest(int[] arr) {
...
}
}
80000数据测试:
快速排序
代码:【难】
package com.jiao.algorithm.sort;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class QuickSort {
public static void main(String[] args) {
/*int[] arr = {-9,70,0,-567,78,23};
quickSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));*/
//生成80000个随机数
int[] array = new int[8000000];
for (int i = 0; i < 8000000; i++) {
array[i] = (int)(Math.random() * 8000000); // [0,1)*80000
}
Date date1 = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr1 = sdf.format(date1);
System.out.println("排序前时间:"+dateStr1);
quickSort(array,0,array.length-1);
Date date2 = new Date();
String dateStr2 = sdf.format(date2);
System.out.println("排序后时间:"+dateStr2);
}
public static void quickSort(int[] arr, int left, int right) {
int l = left; //左下标
int r = right; //右下标
int pivot = arr[(left+right)/2]; //中轴值
int temp = 0; //临时变量,用于交换使用
//让比pivot小的放左边,比pivot大的放右边
while (l < r) {
while (arr[l] < pivot) { //在pivot左边找比它大的退出
l++;
}
while (arr[r] > pivot) { //在pivot右边找比它小的退出
r--;
}
if (l >= r) { //找到最后,左右两边都满足pivot左小右大
break;
}
//交换
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
//防止死循环
if (arr[l] == pivot) { //前移
r--;
}
if (arr[r] == pivot) { //后移
l++;
}
}
//递归
if (l == r) { //l++,l--,防止栈溢出
l++;
r--;
}
//向左递归
if (left < r) {
quickSort(arr,left,r);
}
//向右递归
if (right > l) {
quickSort(arr,l,right);
}
}
}
测试800万数据时间: