排序也称排序算法,排序是将一组数据,依指定的顺序进行排列的过程
排序的分类
1、内部排序:指将需要处理的所有数据都加载到内部存储器中进行排序
2、外部排序法:数据量过大,无法全部加载到内存中,需要借助外部存储进行排序
内部排序包括:插入排序(直接插入排序、希尔排序)、选择排序(简单选择排序、堆排序)、交换排序(冒泡排序、快速排序)、归并排序、基数排序
时间复杂度
度量一个程序(算法)执行时间的两种方法:
1、事后统计的方法
这种方法可行,但是有两个问题:一是要想对设计的算法的运行性能进行评测,需要实际运行该程序;二是所得时间的统计量依赖于计算机的硬件、软件等环境因素,这种方式,要在同一台计算机的相同状态下运行,才能比较哪个算法速度更快
2、事前估算的方法
通过分析某个算法的时间复杂度来判断哪个算法更优
时间频度:一个算法花费的时间与算法中语句的执行次数成正比,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度,记为T(n)
忽略常数项、忽略低次项、忽略系数
- 一般情况下,算法中的基本操作语句的重复执行次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数,记作T(n)=O(f(n)),称O(f(n))为算法的渐进时间复杂度,简称时间复杂度
- T(n)不同,但时间复杂度可能相同,如: T(n)=n²+3n+5与T(n)=4n²+2n+7,他们的f(n)不同,但时间复杂度相同,都为O(n²)
- 计算时间复杂度的方法:
- (1)用参数1代替运行时间中的所有加法常数
- (2)修改后的运行次数函数中,只保留最高阶项
- (3)去除最高阶项的系数
常见的时间复杂度
常数阶O(1)
对数阶O(log2n)
线性阶O(n)
线性对数阶O(nlog2n)
平方阶O(n^2)
立方阶O(n^3)
k次方阶O(n^k)
指数阶O(2^n)
常见的时间复杂度由小到大依次为:O(1)<O(log2n)<O(n)<O(nlog2n)<O(n^2) <O(n^3) <O(n^k) <O(2^n)
空间复杂度
- 类似于时间复杂度的讨论,一个算法的空间复杂度定义为该算法所耗费的存储空间,它也是问题规模n的函数
- 空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度,有的算法需要占用的临时工作单元数与解决问题的规模n有关,它随着n的增大而增大,当n较大时,将占用较多的存储单元,例如快速排序和归并排序
- 在做算法分析时,主要讨论的是时间复杂度,从用户使用体验上看,更看重的程序执行的速度,一些缓存产品(redis,memcache)和算法(基数排序)本质就是用空间换时间
冒泡排序
冒泡排序的基本思想:通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部
优化:因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,因此要在排序过程中设置一个标志flag判断元素是否进行过交换,从而减少不必要的比较
package sort;
import java.util.Arrays;
//冒泡排序法
public class BubbleSort {
public static void main(String[] args) {
int[] arr = new int[10];
for (int i = 0; i < 10; i++) {
arr[i] = (int)(Math.random() * 20);
}
System.out.println("排序前:" + Arrays.toString(arr));
int temp = 0;
boolean flag = false;
for (int j = 0; j < arr.length - 1; j++) {
for (int i = 0; i < arr.length - 1; i++) {
if (arr[i] > arr[i + 1]){
flag = true;
temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
}
}
//System.out.println("第" + (j + 1) + "次排序结果:" + Arrays.toString(arr));
if (!flag){
break;
}else {
flag = false;
}
}
System.out.println("排序后:" + Arrays.toString(arr));
}
}
选择排序
选择式排序也属于内部排序法,是从欲排序的数据中,按指定的规则选出某一元素,再依规定交换位置后达到排序的目的
选择排序的基本思想:
第一次从arr[0]-arr[n-1]中选取最小值,与arr[0]交换,第二次从arr[1]-arr[n-1]中选取最小值,与arr[1]交换,…,第i次从arr[i-1]-arr[n-1]中选取最小值,与arr[i-1]交换,…,第n-1次从arr[n-2]-arr[n-1]中选取最小值,与arr[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列
说明:
1、选择排序一共有数组大小-1轮排序
2、每一轮排序,又是一个循环,循环的规则(代码)
2.1 先假定当前这个数是最小数
2.2 然后和后面的每个数进行比较,如果发现有比当前数更小的数,就重新确定最小数,并得到下标
2.3 当遍历到数组的最后时,就得到本轮最小数和下标
2.4 交换
package sort;
import java.util.Arrays;
//选择排序法
public class SelectSort {
public static void main(String[] args) {
int[] arr = new int[10];
for (int i = 0; i < 10; i++) {
arr[i] = (int)(Math.random() * 20);
}
System.out.println("排序前结果:" + Arrays.toString(arr));
selectSort2(arr);
System.out.println("排序后结果:" + Arrays.toString(arr));
}
//选择排序分步进行优化前代码
public static void selectSort(int[] arr){
int minIndex = 0;
int min = arr[0];
for (int j = 0 + 1; j < arr.length; j++) {
if (min > arr[j]){
minIndex = j;
min = arr[j];
}
}
arr[minIndex] = arr[0];
arr[0] = min;
System.out.println("第一次排序后:" + Arrays.toString(arr));//[1, 34, 119, 101]
minIndex = 1;
min = arr[1];
for (int j = 1 + 1; j < arr.length; j++) {
if (min > arr[j]){
minIndex = j;
min = arr[j];
}
}
arr[minIndex] = arr[1];
arr[1] = min;
System.out.println("第二次排序后:" + Arrays.toString(arr));//[1, 34, 119, 101]
minIndex = 2;
min = arr[2];
for (int j = 2 + 1; j < arr.length; j++) {
if (min > arr[j]){
minIndex = j;
min = arr[j];
}
}
arr[minIndex] = arr[2];
arr[2] = min;
System.out.println("第三次排序后:" + Arrays.toString(arr));//[1, 34, 101, 119]
}
//选择排序优化后代码
public static void selectSort2(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;
}
//System.out.println("第" + (i + 1) + "次排序后的结果:" + Arrays.toString(arr));
}
}
}
插入排序
插入排序属于内部排序法,是对于欲排序的元素以插入的方式找寻该元素的适当位置,以达到排序的目的
插入排序的基本思想:
把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表
package sort;
import java.util.Arrays;
//插入排序
public class InsertSort {
public static void main(String[] args) {
int[] arr = new int[10];
for (int i = 0; i < 10; i++) {
arr[i] = (int)(Math.random() * 20);
}
System.out.println("排序前结果:" + Arrays.toString(arr));
insertSort2(arr);
System.out.println("排序后结果:" + Arrays.toString(arr));
}
//插入排序分步进行优化前代码
public static void insertSort(int[] arr){
int insertVal = 0;
int insertIndex = 0;
insertVal = arr[1];
insertIndex = 1 - 1;
while (insertIndex >= 0 && insertVal < arr[insertIndex]){
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
arr[insertIndex + 1] = insertVal;
System.out.println("第一次插入结果:" + Arrays.toString(arr));
insertVal = arr[2];
insertIndex = 2 - 1;
while (insertIndex >= 0 && insertVal < arr[insertIndex]){
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
arr[insertIndex + 1] = insertVal;
System.out.println("第二次插入结果:" + Arrays.toString(arr));
insertVal = arr[3];
insertIndex = 3 - 1;
while (insertIndex >= 0 && insertVal < arr[insertIndex]){
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
arr[insertIndex + 1] = insertVal;
System.out.println("第三次插入结果:" + Arrays.toString(arr));
}
//插入排序优化后代码
public static void insertSort2(int[] arr){
int insertVal = 0;
int insertIndex = 0;
for (int i = 1; i < arr.length; i++) {
insertVal = arr[i];
insertIndex = i - 1;
while (insertIndex >= 0 && insertVal < arr[insertIndex]){
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
arr[insertIndex + 1] = insertVal;
//System.out.println("第" + i + "次插入后结果:" + Arrays.toString(arr));
}
}
}