目录
1 常见排序算法
冒泡排序、选择排序、直接插入排序、希尔排序、快速排序、归并排序、堆排序、桶排序等
2 排序算法分类
2.1 稳定排序算法、不稳定排序算法
假设有两个元素a,b,它们的值相等。排序前元素a在元素b的前面,排序后a还在b的前面,此时该排序算法就称之为稳定排序算法 ,否则是非稳定排序算法。通俗一点就是,对于相同的元素排序前后其相对位置没有发生改变的就是稳定排序算法,发生改变了的就是不稳定排序算法。
2.2 外排序、内排序
- 内排序:在整个排序过程中,待排序的所有记录全部被放置在内存中
- 外排序:在整个排序过程中,由于排序的记录个数太多,不能同时放置在内存中,整个排序过程需要在内外存之间多级交换数据才能进行。
- 本博客所讲的属于内排序
2.3 常见排序算法分类如图(借用师兄的一张思维导图)
3 常见排序算法原理及其java代码实现
3.1 冒泡排序
基本思想:两两比较相邻元素,如果左边的元素大于右边的元素,则进行交换。如果有n个元素,则需要进行n-1趟排序,每一趟排序都能确定一个最大值的位置。
package basicKnowledge.sort;
/**
* @program:summary
* @author:peicc
* @create:2019-07-18 17:20:28
**/
//冒泡排序
//两两比较并交换,每趟排序后最大值会排在右边。共需n-1趟排序
public class BubbleSort {
public static void bubbleSort(int []array){
int length=array.length;
for(int i=0;i<length-1;i++){//排序趟数
for(int j=0;j<length-i-1;j++){//每趟排序两两比较
if(array[j]>array[j+1]){
int temp=array[j];
array[j]=array[j+1];
array[j+1]=temp;
}
}
}
}
public static void main(String[] args) {
int[] arr=new int[]{10,6,7,8,9,1,2,5,3,4};
bubbleSort(arr);
for (int i = 0; i <arr.length ; i++) {
System.out.print(arr[i]+" ");
}
}
}
- 时间复杂度:
- 空间复杂度:
3.2 简单选择排序
基本思想:每一趟在个元素中选择最小的元素作为排序后序列的第个元素
package basicKnowledge.sort;
/**
* @program:summary
* @author:peicc
* @create:2019-07-18 21:55:29
**/
//选择排序
//1.遍历整个序列,将最小的数放在最前面
//2.遍历剩下的序列,将最小的数放在最前面
//3.重复第二步
//常用于取序列中最大最小的几个数
public class SelectSort {
public void selectSort(int[] array){
int length=array.length;
for(int i=0;i<length;i++){
int min=array[i];
int position=i;
for(int j=i+1;j<length;j++){
if(min<array[j]){
min=array[j];
position=j;
}
}
//比较完之后交换位置
array[position]=array[i];
array[i]=min;
}
}
}
- 时间复杂度:
- 空间复杂度:
3.3 直接插入排序
基本思想:将序列中待排序元素插入到有序序列之中,从而得到一个新的、记录数增1的有序表
package basicKnowledge.sort;
/**
* @program:summary
* @author:peicc
* @create:2019-07-18 16:46:07
**/
//插入排序
//将未排序的数据插入到已排序的数据系列之中
//时间复杂度O(n2):1+2+3+...+N-1
//最优:O(N):数组有序,每次只需比较不成立即可
//通过交换相邻元素进行排序的任何算法平均都需要Ω(n2)
public class InsertSort {
public void inertSort(int []array){
int length=array.length;
int insertNum;
for(int i=1;i<length;i++){//N-1pl趟排序
insertNum=array[i];//待插入的元素
int j=i-1;//已经排好的元素个数
while(j>=0&&array[j]>insertNum){//从后向前比较,将大于insertNum的数据后移一位
array[j+1]=array[j];
j--;
}
array[j+1]=insertNum;//将要插入的元素放在要插入的位置
}
}
}
- 时间复杂度:
- 空间复杂度:
3.4 希尔排序
基本思想:插入排序的改进版,按步长交换元素(可以达到减少元素移动次数的目的),然后再缩减步长,直至步长为1。当步长为1时就是插入排序,不过由于前面的排序使得此时的序列基本有序,因此基本不用移动太多元素。
package basicKnowledge.sort;
/**
* @基本功能:希尔排序
* @program:summary
* @author:peicc
* @create:2019-07-18 20:52:31
**/
public class ShellSort {
public static void shellSort(int []array){
int length=array.length;
int gap=length/2;
while(gap>=1){//步长
for(int num=0;num<gap;num++){//分的组数
//每一组使用插入排序算法
for(int i=num+gap;i<length;i=i+gap){//组中的元素
int j=i-gap;//j为有序序列的最后一位下标
int insertNum=array[i];//待插入的元素
while(j>=0&&array[j]>insertNum){
array[j+gap]=array[j];
j=j-gap;
}
array[j+gap]=insertNum;
}
}
gap=gap/2;//缩减步长
}
}
public static void main(String[] args) {
int[] a=new int[]{3,1,5,7,2,4,9,6,12,200,15,5,20};
shellSort(a);
for (int i = 0; i <a.length ; i++) {
System.out.print(a[i]+" ");
}
}
}
- 时间复杂度:与增量序列的选取有关,可达到
- 空间复杂度:
3.5 快速排序(面试中高频考点)
基本思想:通过一趟排序将待排序元素分成两部分,其中一部分元素均比另一部分元素小,然后对这两部分继续进行上述排序。
package basicKnowledge.sort;
import java.util.Scanner;
/**
* @基本功能:快速排序
* @时间复杂度:
* @最优:O(nlogn)
* @最差:O(n^2)
* @平均:
* @program:summary
* @author:peicc
* @create:2019-07-22 21:41:29
**/
public class QuickSort {
public static void quickSort(int[] array,int left,int right){
if(left>=right){
return;
}
int benchMark=array[left];
int i=left,j=right;
while(i!=j){
//必须先从从往左找
//j从右向往走寻找比基本小的数
while(array[j]>=benchMark&i<j){
j--;
}
//i从左向右走寻找比基准大的数
while(array[i]<=benchMark&i<j){
i++;
}
if(i<j){//还未相遇
//交换
int temp=array[i];
array[i]=array[j];
array[j]=temp;
}
}
//将基准放在正确位置
array[left]=array[i];
array[i]=benchMark;
//左半部分快速排序
quickSort(array,left,i-1);
//右边部分快速排序
quickSort(array,i+1,right);
}
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
int N=scanner.nextInt();
int[] array=new int[N];
for(int i=0;i<N;i++){
array[i]=scanner.nextInt();
}
printAll(array);
quickSort(array,0,array.length-1);
printAll(array);
}
public static void printAll(int[]array){
System.out.println("***********输出数组*********");
for(int i=0;i<array.length;i++){
System.out.print(array[i]+" ");
}
System.out.println("***********输出完毕*********");
}
}
- 时间复杂度:最优,最坏
- 空间复杂度:最优,最坏
3.6 归并排序
基本思想:对于含有个元素的序列,将其看成个有序的子序列,每个子序列的长度为1,然后将其两两合并,得到g个长度为2(为偶数)或1(为奇数)的有序子序列;然后再两两合并,直到得到一个长度为的有序序列。
package basicKnowledge.sort;
import com.sun.scenario.effect.Merge;
/**
* @基本功能:归并排序(比较占内存,但却效率高且比较稳定的算法)
* @时间复杂度
* @最好、最坏、平均:O(nlogn)
* @空间复杂度:
* O(n+logn): n记录排序结果所需空间,logn递归调用所需的栈空间
* @program:summary
* @author:peicc
* @create:2019-08-20 10:06:35
**/
public class MergeSort {
public static void main(String[] args) {
int[] SR={50,10,90,30,70,40,80,60,20};
int[] TR1=new int[SR.length];
mergeSort(SR,TR1,0,8);
for (int i = 0; i<9; i++) {
System.out.println(TR1[i]);
}
}
//归并排序
/**
* @功能:
* @Param: [SR, TR1, s, t] SR:待排序数组
* @Param SR:待排序数组
* @Param TR1;排序后的数组
* @Param s:元素的起始位置
* @Param t:元素的终止位置
* @return: void
* @Date: 2019/9/6
*/
public static void mergeSort(int SR[],int[] TR1,int s,int t){
int[] TR2=new int[SR.length];
int m;
if(s==t){//只有一个元素
TR1[s]=SR[s];
}else{
//先分再合
m=(s+t)/2;
mergeSort(SR,TR2,s,m);//递归,将SR[s,m]归并排序成有序的TR2[s,m]
mergeSort(SR,TR2,m+1,t);//递归,将SR[m+1,t]归并排序成有序的TR2[m+1,t]
merge(TR2,TR1,s,m,t);//将TR2[s,m]、TR2[m+1,t]归并到TR1[s,m]中
}
}
//合并两个有序数组
public static void merge(int[] SR,int[] TR,int i,int m,int n){
int j=m+1;//第二个有序数组的下标
int k=i;//合并后有序数组的下标
for (; i <=m&&j<=n ; k++) {
if(SR[i]<SR[j]){
TR[k]=SR[i++];
}else{
TR[k]=SR[j++];
}
}
if(i<=m){//说明第一部分没有合并完
for(;i<=m;){
TR[k++]=SR[i++];
}
}
if(j<=n){//说明第二部分没有合并完
for(;j<=n;){
TR[k++]=SR[j++];
}
}
}
}
- 时间复杂度:
- 空间复杂度:
3.7 堆排序
基本思想:利用堆(最大堆、最小堆)进行排序的方法。假设我们利用最大堆进行排序,首先我们需要将待排序的个元素构造成一个最大堆,此时堆顶的元素便为最大值。将最大值移走,然后将剩余的-1个元素重新构造成一个最大堆,此时我们便可以得到次大值。如此反复执行,便能得到一个有序序列。
思想比较简单,但实施起来就很麻烦了,实现它需要解决两个问题:
- 如何从一个无序序列构建一个最大堆?
- 在移除堆顶元素后,如何调整剩余元素成为一个新的堆?
本文主要讲解排序算法的思想以及如何使用,因此此处直接利用JDK自带的优先队列进行堆排序的实现 。JDK优先队列的就是利用最大堆(最小堆)实现的, 关于上述两个问题都可以通过阅读JDK源码去解决。同时,考虑到源码的方法过多,很多人觉得无从看起,本文在另一篇博客手写了一个优先队列对优先队列进行了简单实现,可以帮助读者解决上述两个问题。
package basicKnowledge.sort;
import java.util.PriorityQueue;
/**
* @基本功能:堆排序
* @program:summary
* @author:peicc
* @create:2019-08-19 10:06:47
**/
public class HeapSort {
public static void heapSort(int[] arr){
PriorityQueue priorityQueue=new PriorityQueue();
for (int i = 0; i <arr.length ; i++) {
priorityQueue.add(arr[i]);
}
while (!priorityQueue.isEmpty()){
System.out.print(priorityQueue.poll()+" ");
}
}
public static void main(String[] args) {
int[] arr=new int[]{10,6,7,8,9,1,2,5,3,4};
heapSort(arr);
}
}
- 时间复杂度:
- 空间复杂度:
3.8 桶排序
基本思想:以数组下标存储元素的值,下标所对应的的值为该下标所出现的次数。
package basicKnowledge.sort;
/**
* @基本功能:桶排序
* @program:summary
* @author:peicc
* @create:2019-09-06 18:14:45
**/
public class barrelSort {
/**
* @功能:
* @Param: [arr, n] n待排序元素的最大范围
* @return: void
* @Date: 2019/9/6
*/
public static void barrelSort(int[] arr,int n){
int[] book=new int[n+1];
for (int i = 0; i <arr.length ; i++) {
book[arr[i]]++;
}
for (int i = 0; i <=n ; i++) {
for (int j = 0; j <book[i] ; j++) {
System.out.print(i+" ");
}
}
}
public static void main(String[] args) {
int[] arr=new int[]{10,6,7,8,9,1,2,5,3,4};
barrelSort(arr,10);
}
}
- 时间复杂度: 为元素的个数,为桶的个数(待排序元素的最大值)
- 空间复杂度:为桶的个数