排序算法
一、排序算法一缆
二、算法的时间复杂度O(n^M)(算法(程序)的执行时间)
①、度量算法(程序)的执行时间的两种方法,已经时间频度
1)、事后统计的方法
这种方法可行,但是有两个问题,一是要想对设计的算法的运行性能进行评测,需要实际运行该程序,二是所得时间的统计量依赖于计算机的硬件、软件等环境因素,这种方式,要在同一计算机的相同状态下运行,才能比较那个算法速度更快。
2)、事前估算的方法
通过分析某个算法的时间复杂度来判断那个算法更优
3)、时间频度
一个算法花费的时间与算法中语句的执行次数成正比,哪个算法中语句执行次数多,它花费的时间就多,一个算法中语句执行次数称之为语句频度或者时间频度,记为T(n)
4)、计算时间复杂度时,可以忽略常数项、低次项,系数项,因为当数据量足够大时,它们已经起不到关键性的作用了。它们不会随n值的增大而增大
5)、如何计算时间复杂度以及时间复杂度需要注意的细节(来源于Car大佬的学习网址,里面的东西是真不错)
三、算法的空间复杂度
1)、.类似于时间复杂度的讨论,一个算法的空间复杂度(Space Complexity)定义为该算法所耗费的存储空间,它也是问题规模n的函数。
2)、空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度。有的算法需要占用的临时工作单元数与解决问题的规模n有关,它随着n的增大而增大,当n较大时,将占用较多的存储单元,例如快速排序和归并排序算法就属于这种情况
3)、在做算法分析时,主要讨论的是时间复杂度。从用户使用体验上看,更看重的程序执行的速度。一些缓存产品(redis, memcache)和算法(基数排序)本质就是用空间换时间.
四、排序算法
① 冒泡排序(稳定,最坏时间复杂度O(n^2))
我都不想重写写遍,太常见,太基础,看这篇博客,有图解,有代码,有思路,很🆗的。
冒泡排序
② 简单选择排序(不稳定,最坏时间复杂度O(n^2),比冒泡排序要快,测试方法使用了事后统计法)
1)、基本思想,取出数组中最小的放到最前面,取出的数据不与下次做对比,最后就得出排序后的数组了。
2)、基于此思想的算法主要有简单选择排序、树型选择排序和堆排序。
3)、简单选择排序的的基本思路:从arr[1]arr[n]中选出最小,与arr[1]交换位置,在从arr[2]arr[n]中选出最小值,与arr[2]交换位置,以此类推。
4)、代码
/**
* 简单选择排序,时间复杂度:O(n2)
*虽然时间复杂度一样,但是比冒泡要快
* @param arr
* @return
*/
public static int[] selectSort(int arr[]) {
for (int i = 0; i < arr.length - 1; i++) {
int k = i;//拿到第一次遍历的值
for (int j = k + 1; j < arr.length; j++) {//temp+1,temp的后一个值
if (arr[j] < arr[k]) {//小于记录下最小值的位置
k = j;
}
}
//找到最小值,交换位置
if (i != k) {
int temp = arr[i];
arr[i] = arr[k];
arr[k] = temp;
}
}
return arr;
}
③ 简单插入排序
1)、基本思想:把待排序的列表看成一个有序列表和一个无序列表,开始时有序列表中只包含一个元素,无序列表中包含有n-1个元素,排序过程中,每次从无序表中取出第一个元素与有序表中的元素进行比较,将他插到合适的位置,使之变为有序列表
2)、代码:
/**
* 插入排序
* 时间复杂度 O(n2)
*/
public static int[] insertSort(int[] arr){
//从1开始与前面的值比较
for (int i = 1; i < arr.length; i++) {
int k=arr[i];//待插入的数
int v=i-1;//待插入的前一个
/**
* v>=0 保证在给 k找插入位置,不越界
* k<arr[v] 待插入的数,还没有找到位置
* 需要后移,满足条件表示k不能插在原来的位置,v需要后移给我们的k预留我i之,v--让k与v前面位置的值比较,v>=0防止越界
*/
while (v>=0 && k<arr[v]){
arr[v+1]=arr[v];
v--;
}
//退出while循环,说明位置找到看,放到当前v的后面一位,一位不满足条件是当前值大于v下标的值,所以在放在v的后一位。
if (v+1!=i){
arr[v+1]=k;
}
}
return arr;
}
④ 简单希尔排序插入排序的一种(交换法,不推荐使用)
/**
* 希尔排序时,对有序序列在插入时采用交换法,并测试排序速度,速度比简单插入排序还慢
* @param arr
* @return
*/
public static int[] ShellSort(int[] arr){
//增量gap,并逐步缩小增量
for (int gap = arr.length/2; gap >0 ; gap /=2) {
int temp = 0;
//将gap.length个数据分成gap.length/2组
for (int i = gap; i < arr.length; i++) {
//遍历各组中,所有的元素(共gap组),步长gap
for (int j = i - gap; j >= gap; j -= gap) {
//如果当前元素大于加上步长后的那个元素,说明交换
if (arr[j] > arr[j + gap]) {
temp = arr[j];
arr[j] = arr[j + gap];
arr[j + gap] = temp;
}
}
}
}
return arr;
}
⑤ 希尔排序(移动法
/**
* 希尔排序时,对有序序列在插入时采用移动法,并测试排序速度,速度比简单插入排序快很多
* @param arr
* @return
*/
public static int[] ShellSort2(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,找到位置
arr[j]=temp;
}
}
}
return arr;
}
⑦ 快速排序
1)、核心思想:快速排序
2)、代码
/**
* 快速排序
* @param array
*/
public static int[] quickSort(int[] array) {
int len;
if(array == null
|| (len = array.length) == 0
|| len == 1) {
return array;
}
sort(array, 0, len - 1);
return array;
}
/**
* 快排核心算法,递归实现
* @param array
* @param left
* @param right
*/
public static void sort(int[] array, int left, int right) {
if(left > right) {
return;
}
// base中存放基准数
int base = array[left];
int i = left, j = right;
while(i != j) {
// 顺序很重要,先从右边开始往左找,直到找到比base值小的数
while(array[j] >= base && i < j) {
j--;
}
// 再从左往右边找,直到找到比base值大的数
while(array[i] <= base && i < j) {
i++;
}
// 上面的循环结束表示找到了位置或者(i>=j)了,交换两个数在数组中的位置
if(i < j) {
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
}
// 将基准数放到中间的位置(基准数归位)
array[left] = array[i];
array[i] = base;
// 递归,继续向基准的左右两边执行和上面同样的操作
// i的索引处为上面已确定好的基准值的位置,无需再处理
sort(array, left, i - 1);
sort(array, i + 1, right);
}
public static int[] mergeSort(int[] a,int low,int high){
int mid = (low+high)/2;
if(low<high){
mergeSort(a,low,mid);
mergeSort(a,mid+1,high);
//左右归并
merge(a,low,mid,high);
}
return a;
}
public static void merge(int[] a, int low, int mid, int high) {
int[] temp = new int[high-low+1];
int i= low;
int j = mid+1;
int k=0;
// 把较小的数先移到新数组中
while(i<=mid && j<=high){
if(a[i]<a[j]){
temp[k++] = a[i++];
}else{
temp[k++] = a[j++];
}
}
// 把左边剩余的数移入数组
while(i<=mid){
temp[k++] = a[i++];
}
// 把右边边剩余的数移入数组
while(j<=high){
temp[k++] = a[j++];
}
// 把新数组中的数覆盖nums数组
for(int x=0;x<temp.length;x++){
a[x+low] = temp[x];
}
}