• 稳定的定义:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。

  • 稳定的意义:排序的内容是一个复杂对象的数字属性,且其原本的初始顺序存在意义,那么在二次排序的基础上保持原有排序,就是稳定的。比如商品销量相同,价格不同,原本已经按价格排好,现在按销量排,就要尽量避免相同销量的商品相对位置改变,要使它们价格顺序不变。

  • 快速排序、希尔排序、选择排序、堆排序是不稳定的排序算法

  • 而基数排序、冒泡排序、插入排序、折半插入排序、归并排序是稳定的排序算法
    (快些选一堆美女一起玩儿——不稳定)

【面试题】在某种情况下,哪种排序算法最快?

  • 如果不在乎浪费空间,应该是桶排序最快
  • 如果整体基本有序**(升序),插入排序最快**,如果降序,则为最慢
  • 如果考虑综合情况,快速排序更加实用常见(希尔排序、堆排序等各种排序也各有优劣)
  • 数据量大,数据分布比较随机,用快速排序,要求稳定则用归并
  • 冒泡排序:数据量不大,对稳定性有要求,且数据基本有序;
  • 选择排序:数据量不大,且对稳定性没有要求。

一、冒泡排序算法

步骤:
(1)比较前后两个数据,若前面的大于后面的,则交换;
(2)遍历后,最大的数据“沉到了数组N-1位置
(3)最大的数据搞定后,对其它的N-1个数据进行处理:N=N-1,只要N>0,则重复前面。

最坏时间复杂度:O(n ^2),最好时间复杂度:O(n),平均时间复杂度:O(n ^2)
空间复杂度:O(1)

public class 冒泡排序
{public static void bubbleSort(int[] a,int N){int i,j;for(i=0;i<N;i++) //全部遍历一遍{for(j=1;j<N-i;j++) //比较前后两个数据,自1始,到n-i结束{if(a[j-1]>a[j]){int temp;temp = a[j-1];a[j-1] = a[j];a[j] = temp;}}}}public static void main(String[] args){int[] a={3,6,8,9,1,4,5};System.out.println("按从小到大顺序排列为:");bubbleSort(a,a.length);for(int i=0;i<a.length;i++){System.out.print(a[i] + " ");}}
}

二、插入排序算法

原理:将要插入的牌从右到左地比较,若原位置的牌更大,则与更前面的牌对比(原位置的后移一个),直到遇到比要插入的数小的,则要插入的数放在该位置的后面一个。
最坏时间复杂度:O(n^2),最好时间复杂度:O(n)

//插入排序法,从小到大排列
public class insertSort {public static void main(String[] args) {int[] a = {2,7,9,4,1,6,5}; insert(a);for (int i : a) {System.out.print(i+" ");;}}public static int[] insert(int[] a) {for(int i=1;i<a.length;i++) {//选中i位置的值,将其与i之前的数挨个比较for(int j=i;j>0;j--) {if(a[j]<a[j-1]) {int temp;temp = a[j-1];a[j-1] = a[j];a[j] = temp;}}}return a;}
}

最佳情况:T(n) = O(n) 最坏情况:T(n) = O(n2) 平均情况:T(n) = O(n2)

三、选择排序

原理:从数组中选择最小的元素,与第一个元素交换位置;再从剩余数组中选择最小的,与第二个元素交换位置,直到实现从小到大排列。

public class SelectionSort {public static void main(String[] args) {int[] a = {2,6,8,4,1,5,3};Selection(a);for (int i : a) {System.out.print(i+" ");}}public static void Selection(int[] a) {//寻找最小值for(int i=0;i<7-1;i++) {int min = i;for(int j=i+1;j<7;j++) {//寻找当前数组的最小元素下标if(a[j]<a[min]) {min=j;}}swap(a,min, i);}}//——Java对普通类型的变量是不支持引用传递的,这里需借用数组来实现交换!public static void swap(int[] a, int i,int j) {int temp = a[j];a[j] = a[i];a[i] = temp;}
}

四、快速排序算法(*)

原理:
1、先从数列中取出一个数作为基准数;
2、分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边;——二分
3、再对左右区间重复第二步,直到各区间只有一个数,此时数组有序。

1)设置两个变量i、j,排序开始的时候:i=0,j=n-1;
2)第一个数组值作为比较值,首先保存到temp中,即temp=A[0];
3)然后j-- ,向前搜索,找到小于temp的数后,停下来;
4)然后i++,向后搜索,找到大于temp的数后,停下来, 交换:s[j]=s[i]
5)继续重复第3、4步,直到i=j,最后使得s[i]=temp
6) 然后采用“二分”的思想,以i为分界线,拆分成两个数组 s[0,i-1]、s[i+1,n-1]又开始进行一遍上述排序

具体步骤:
图片来源于:https://blog.csdn.net/qq_40941722/article/details/94396010
(1)以i=0,j=n-1分别指向序列两端,以A[0]=temp作为基准值;

(2)j–从后往前搜索,找到比基准值小的后停下来,i++从前向后搜索,找到比基准值大的停下来

(3)交换A[i]=A[j]

(4)j和i继续移动(j先移动),则交换9和4,得到如下:

(5)直到i=j后,使A[i]=A[j]=基准值,此时基准值左边的数都比它小,右边的数都比它大,对于基准值来说,左右两边就有序了。

(6)采用“二分”的思想,重新选取基准值,使用递归的方法,对左右两边的序列重复上述排序:

(7)得到:

最终得到有序序列:

其完全具体步骤如图:

最坏时间复杂度:O(n^2),平均时间复杂度:O(nlogn)
空间复杂度:O(n
logn)

import java.util.*;public class Main {public static void main(String[] args) {int[] a = {6,1,2,7,9,3,4,5,10,8};quickSort(a, 0, a.length - 1);for(int i=0;i<a.length;i++){System.out.print(a[i]+" ");}System.out.println();//System.out.print(Arrays.toString(a));}public static void quickSort(int[] a,int low,int high){if(low > high){return;}int i = low;int j = high;int key = a[low];//基准值while(i < j){//循环至i=j//(1)向前搜索,找到比基准值小的,停下来while(a[j] >= key && i < j){j--;}//(2)向后搜索,找到比基准值大的,停下来while(a[i] <= key && i < j){i++;}//(3)此时交换两者if(i < j){int temp = a[i];a[i] = a[j];a[j] = temp;}}//(4)此时i=j,将此处的数a[i]=a[j]和基准值key交换a[low] = a[i];a[i] = key;//(5)二分,左右分别递归quickSort(a, low, i - 1);quickSort(a, j + 1, high); }
}

4.1 利用快排思想,找出第K大的数

import java.util.*;public class Finder {public int findKth(int[] a, int n, int K) {// write code herereturn findK(a, 0, n-1, K);}public static int partition(int[] arr, int left, int right) {int pivot = arr[left];while (left < right) {while (left < right && arr[right] <= pivot) {right--;}arr[left] = arr[right];while (left < right && arr[left] >= pivot) {left++;}arr[right] = arr[left];}arr[left] = pivot;return left;}public static int findK(int[] arr, int left, int right, int k) {if (left <= right) {int pivot = partition(arr, left, right);if (pivot == k - 1) {return arr[pivot];} else if (pivot < k - 1) {return findK(arr, pivot + 1, right, k);} else {return findK(arr, left, pivot - 1, k);}}return -1;}
}

五、归并排序(*)

原理:归并(递归、层层合并)排序法(Merge)即把待排序序列分为若干个子序列每个子序列是有序的。然后再把有序子序列合并为整体有序序列
——分治法,各层分治递归

【时间复杂度】:归并排序最好、最差和平均时间复杂度都是O(n*logn),因为每次划分时两个子序列的长度基本一样。

//归并排序法,从小到大排列.
//把待排序序列分为若干个子序列,每个子序列是有序的。
//然后再把有序子序列合并为整体有序序列。
public class 练习题1_1
{public static void mergeSort(int[] a){sort(a,0,a.length-1);}public static void sort(int[] a, int left, int right){// left:左数组第一个元素索引;right:右数组最后一个元素索引if(left>=right) return;//递归边界条件int center = (left + right)/2;//中间索引,划分子序列sort(a,left,center);//左边递归sort(a,center+1,right);//右边递归merge(a,left,center,right);//合并}//对两个已经有序的数组进行归并public static void merge(int[] a,int left,int center,int right){int[] tempArr = new int[a.length];//临时数组int mid = center +1;//右边第一个元素索引int third = left;//记录临时数组的索引int temp = left;//缓存左边第一个元素的索引while(left<=center && mid <= right){//从两个数组中取出最小的放入临时数组中if(a[left]<=a[mid]){tempArr[third++] = a[left++];}else{tempArr[third++] = a[mid++];}}//剩余部分依次放入临时数组while(mid<=right){tempArr[third++] = a[mid++];}while(left <= center){tempArr[third++] = a[left++];}//将临时数组中的内容拷贝回原数组中while(temp <= right){a[temp]=tempArr[temp++];}}
}

六、堆排序

大顶堆与小顶堆

大顶堆:每个结点都大于其左右孩子结点
小顶堆:每个结点都小于其左右孩子结点

升序:大顶堆(因为顶部大的先出去)
降序:小顶堆

堆排序基本步骤

步骤一:构造初始堆。将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆);
步骤二:最大值为根节点,将堆顶与末尾元素进行交换,使末尾元素最大,然后继续调整堆(将剩下的n-1个元素重新构建成大顶堆),再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换,直到整个序列有序。

具体步骤

步骤一:构造初始堆:

  1. 初始元素为:[4,6,8,5,9],

  2. 从最后一个非叶子结点开始:6,从左至右,从上至下进行调整(与其叶子结点进行对比,交换);

    3)找到第二个非叶子结点:4,进行调整:

4)再次调整最后一个非叶子结点

此时,无序序列就被构造成一个大顶堆了。

步骤二:最大值为根节点,将堆顶与末尾元素进行交换,使末尾元素最大
非叶子结点的索引:arr.length/2-1

package erchashu;
import java.util.Arrays;public class HeapSort {public static void main(String[] args) {int[] arr = {4,6,8,5,9,1,23,15,7};//要求将该数组升序排列heapSort(arr);} //堆排序方法public static void heapSort(int[] arr) {System.out.println("堆排序:");//构建第一个大顶堆for(int i = arr.length/2-1;i>=0;i--) {//i=arr.length/2-1adjustHeap(arr,i,arr.length);}System.out.println("最初调整得到大顶堆数组="+Arrays.toString(arr));//头尾交换for(int j=arr.length-1; j>0 ;j--) {//交换大顶堆顶部和末尾元素int temp = arr[j];arr[j]=arr[0];arr[0] = temp;//再调成大顶堆adjustHeap(arr, 0, j);}System.out.println("最终结果="+Arrays.toString(arr));}//将二叉树调整成大顶堆:将以 i 对应非叶子结点的树调整为大顶堆//i:非叶子结点在数组中的索引,length:对多少个元素进行调整(逐渐减少)public static void adjustHeap(int[] arr,int i,int length) {int temp = arr[i];//先取出当前非叶子结点元素的值,保存在临时变量中//i为要调的非叶子结点,k为其左子结点for(int k = i * 2 + 1;k < length;k = k * 2 + 1) {if(k+1 < length && arr[k] < arr[k+1]) {//判断左右子节点的大小k++;//如果右子结点更大,则让k指向右子结点}if(arr[k]>temp) {//子结点大于父节点arr[i]=arr[k];i = k;//难点:i指向k,继续循环比较}else {break;}}//for循环结束后,此时已经将以i为父结点的局部树的最大值调整到了顶部arr[i]=temp;//将父结点放到调整后的位置}
}

时间复杂度全是:O(nlogn)

6.1 利用大顶堆找出第K大的数

class Solution {public  int findKthLargest(int[] nums, int k) {return  heapSort(nums, k);}private  int heapSort(int[] nums, int k) {//将无序数组构成一个大顶堆for (int i = nums.length / 2 - 1; i >= 0; i--) {adjustHeap(nums, i, nums.length);}int count = 0;for (int j = nums.length - 1; j >= 0; j--) {count++;int temp = nums[j];nums[j] = nums[0];if (count == k) {return nums[0];}nums[0] = temp;adjustHeap(nums, 0, j);}return 0;}private  void adjustHeap(int[] nums, int i, int length) {int temp = nums[i];for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {if (k + 1 < length && nums[k + 1] > nums[k]) {k++;}if (nums[k] > temp) {nums[i] = nums[k];i = k;} else {break;}}nums[i] = temp;}
}

Java排序算法汇总相关推荐

  1. 终于,把十大经典排序算法汇总了!(Java实现版)

    转载自  终于,把十大经典排序算法汇总了!(Java实现版) 最近几天在研究排序算法,看了很多博客,发现网上有的文章中对排序算法解释的并不是很透彻,而且有很多代码都是错误的,例如有的文章中在" ...

  2. Java排序算法:冒泡排序

    Java排序算法:冒泡排序 //创建数组并赋值int[] data = new int[] {11,10,55,78,100,111,45,56,79,90,345,1000};for(int i=0 ...

  3. Java十大排序算法总结,Java排序算法总结之冒泡排序

    本文实例讲述了Java排序算法总结之冒泡排序.分享给大家供大家参考.具体分析如下: 前言:冒泡排序(BubbleSort)就是依次比较相邻的两个数,将小数放在前面,大数放在后面. 下面让我们一起    ...

  4. 排序算法汇总(C/C++实现)

    前言:     本人自接触算法近2年以来,在不断学习中越多地发觉各种算法中的美妙.之所以在这方面过多的投入,主要还是基于自身对高级程序设计的热爱,对数学的沉迷.回想一下,先后也曾参加过ACM大大小小的 ...

  5. Java排序算法之直接选择排序

    Java排序算法之直接选择排序 基本过程:假设一序列为R[0]~R[n-1],第一次用R[0]和R[1]~R[n-1]相比较,若小于R[0],则交换至R[0]位置上.第二次从R[1]~R[n-1]中选 ...

  6. java排序算法 sort_Java排序算法之SleepSort排序示例

    本文实例讲述了Java排序算法之SleepSort排序.分享给大家供大家参考,具体如下: 分享一个很有创意的排序算法:sleepSort .巧妙利用了线程的sleep(),代码如下: public c ...

  7. java 排序算法总结,Java排序算法总结之归并排序

    本文实例讲述了Java排序算法总结之归并排序.分享给大家供大家参考.具体分析如下: 归并操作(merge),也叫归并算法,指的是将两个已经排序的序列合并成一个序列的操作.和快速排序类似,让我们一起来看 ...

  8. Java排序算法——插入排序(Insertion Sort)

    之前总结了交换排序的冒泡排序与选择排序的简单选择排序,这次我们来看看插入排序的简单插入排序~ 往期传送门: 冒泡排序: Java排序算法--冒泡排序(Bubble Sort)https://blog. ...

  9. Java排序算法——选择排序

    Java排序算法--选择排序(Selection sort) 传送门 冒泡排序 插入排序 简述 选择排序(Selection sort)是一种简单直观的排序算法.它的工作原理如下.首先在未排序序列中找 ...

最新文章

  1. Codeforces Round #643 (Div. 2)题解
  2. 基于三层BP神经网络的人脸识别
  3. 济宁市机器人科技乐园_【喜讯】山东省青少年科技创新大赛、机器人竞赛,济宁市信息学奥赛获奖名单新鲜出炉...
  4. ubuntu 14.04安装quickbuild buildagent (二)
  5. java synchronized 静态_Java之Synchronized修饰实例方法和静态方法
  6. QT5开发及实例学习之一main()函数相关内容介绍
  7. springbank 开发日志 springbank是如何执行一个handler的requestMapping对应的方法的
  8. linux中at重定位命令,readelf命令_Linux readelf 命令用法详解:用于显示elf格式文件的信息...
  9. Window: win10 点击打开此电脑中的磁盘时,提示找不到应用程序
  10. NXP Kinetis的 单片机LPUART 模块调试记录
  11. 关于音频录制raw格式转换为mp3文件
  12. python中的index函数
  13. nbiot模块联网问题排查
  14. H5播放B站源的flv视频
  15. 微软编程规范(文档)
  16. matlab y 0,用MATLAB算y-2y-3y=0的解
  17. 页面置换算法(FIFO、第二次机会、LRU)
  18. Bert 论文中文翻译
  19. webpack 降级
  20. 苹果cms模板_泛目录站群神器,万词无限模板站群黑帽SEO程序

热门文章

  1. alpine初始化配置和踩坑记录
  2. vue项目debugger调试看不到源码
  3. 【python】python制作 连连看 游戏脚本(二)
  4. 802.11e和802.11s的Power Save对比
  5. 中国互联网最赚钱的十位草根人物 DotA解说海涛年入千万
  6. linux memcpy需要头文件,memcpy函数详解
  7. python tableview 列宽_PyQt5中QTableWidget设置列宽大小的几种方式
  8. ycf 梗_你从哪些情况下觉得云次方是真的?
  9. Android组件化方案最佳实践
  10. excel多列合并成一列加符号_Excel多列变(合并)一列的方法详解