java 排序的代码_Java 常用的八种排序算法与代码实现

Java 常用的九种排序算法与代码实现

排序问题一直是程序员工作与面试的重点,今天特意整理研究下与大家共勉!这里列出 8 种常见的经典排序,基本涵盖了所有的排序算法。

1. 直接插入排序

我们经常会到这样一类排序问题:把新的数据插入到已经排好的数据列中。将第一个数和第二个数排序,然后构成一个有序序列将第三个数插入进去,构成一个新的有序序列。对第四个数、第五个数…… 直到最后一个数,重复第二步。如题所示:

b4c89128fb9ea9aba616b7e29ba7b77d.png

直接插入排序(Straight Insertion Sorting)的基本思想:在要排序的一组数中,假设前面 (n-1) [n>=2] 个数已经是排好顺序的,现在要把第 n 个数插到前面的有序数中,使得这 n 个数也是排好顺序的。如此反复循环,直到全部排好顺序。

代码实现:

首先设定插入次数,即循环次数,for(int i=1;i

设定插入数和得到已经排好序列的最后一个数的位数。insertNum 和 j=i-1。

从最后一个数开始向前循环,如果插入数小于当前数,就将当前数向后移动一位。

将当前数放置到空着的位置,即 j+1。

代码如下:

package com.jiading.myRewrite;

/**

* @program: 排序算法的Java实现

* @description: 插入排序

* @author: JiaDing

* @create: 2020-03-23 00:27

* 插入排序是稳定的

* 时间复杂度是O(n^2)

* 空间复杂度是O(1)

**/

public class InsertSort {

public static void insertSort(int[]array){

//从第二个开始排

for(int i=1;i

int temp=array[i];

int j=i-1;

while(j>=0){

if(array[j]

break;

}

if(array[j]>temp){

array[j+1]=array[j];

}

j--;

}

array[j+1]=temp;

}

}

public static void main(String[] args) {

int[] a={49,38,65,97,76,13,27,49,78,34,12,64,1};

insertSort(a);

System.out.println("排序结果:");

for (int i:a

) {

System.out.println(i);

}

}

}

2. 希尔排序

针对直接插入排序的下效率问题,有人对次进行了改进与升级,这就是现在的希尔排序。希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率

但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位

如图所示:

6c667b5f344ff1d97739ab19392d9c1a.png

对于直接插入排序问题,数据量巨大时。

将数的个数设为 n,取奇数 k=n/2,将下标差值为 k 的数分为一组,构成有序序列。

再取 k=k/2 ,将下标差值为 k 的书分为一组,构成有序序列。

重复第二步,直到 k=1 执行简单插入排序。

代码实现:

package com.jiading.myRewrite;

/**

* @program: 排序算法的Java实现

* @description: 希尔排序

* @author: JiaDing

* @create: 2020-03-29 17:47

* 希尔排序是非稳定算法

* 希尔排序的时间复杂度是:O(nlogn)~O(n2),平均时间复杂度大致是O(n√n)

* shell排序的时间复杂度是依赖于 argument sequence 的,所以你用不同的序列,时间复杂度不同

* shell的时间复杂度分析没有完结

* 空间复杂度是O(1)

**/

public class ShellSort {

public static void main(String[] args) {

int[] a={49,38,65,97,76,13,27,49,78,34,12,64,1};

shellSort(a);

System.out.println("排序结果:");

for (int i:a

) {

System.out.println(i);

}

}

private static void shellSort(int[] a) {

/*

这个length在这里并不表示数组长度,而是表示每组元素之间的距离,也就是取元素时的步长

每次合并都将步长减半

对于按步长分开的组中的元素进行直接插入排序

*/

int length=a.length;

while(length!=0){

length=length/2;

//i表示是第几组的,因为步长是length,所以步长之内的都是不同组的

for(int i=0;i

//j表示每次插入排序时考虑的组的元素的右边界

for(int j=i+length;j

//k表示前一个

int k=j-length;

//temp是要找位置往里插的元素

int temp=a[j];

/*

将前面的向后移动,为插入留下空子

*/

while(k>=0&&temp

a[k+length]=a[k];

k-=length;

}

//应该插入到k之后的那个位置,因为第k个位置是已经比temp小的

a[k+length]=temp;

}

}

}

}

}

3. 二分排序

package com.jiading.myRewrite;

/**

* @program: 排序算法的Java实现

* @description: 二分排序

* @author: JiaDing

* @create: 2020-03-23 00:35

* 二分排序比较麻烦,细节比较多

* 二分排序是插入排序中的一种,也是稳定的

* 时间复杂度是O(n^2):这是因为如果每次都插入到第一个位置的话,需要移动n-1+n-2+···+1次

* 空间复杂度是O(1)

**/

public class BinarySort {

public static void binarysort(int[]array){

int n=array.length;

for(int i=1;i

int temp=array[i];

int left=0,right=i-1,mid=(left+right)/2;

while(left<=right){

mid=(left+right)/2;

if(temp>array[mid]){

left=mid+1;

}else if(temp

right=mid-1;

}else{

break;

}

}

for(int j=i-1;j>=left;j--){

array[j+1]=array[j];

}

//如果没有更大的,就不需要移动和插入

if(left!=i)

array[mid]=temp;

}

}

public static void main(String[] args) {

int[] a={49,38,65,97,76,13,27,49,78,34,12,64,1};

binarysort(a);

System.out.println("排序结果:");

for (int i:a

) {

System.out.println(i);

}

}

}

4. 简单选择排序

常用于取序列中最大最小的几个数时。

(如果每次比较都交换,那么就是交换排序;如果每次比较完一个循环再交换,就是简单选择排序。)

遍历整个序列,将最小的数放在最前面。

遍历剩下的序列,将最小的数放在最前面。

重复第二步,直到只剩下一个数。

93f8992e9f93bc5cbc411b0758494a5a.png

代码实现:

首先确定循环次数,并且记住当前数字和当前位置。

将当前位置后面所有的数与当前数字进行对比,小数赋值给 key,并记住小数的位置。

比对完成后,将最小的值与第一个数的值交换。

重复 2、3 步。

package com.jiading.myRewrite;

/**

* @program: 排序算法的Java实现

* @description: 选择排序

* @author: JiaDing

* @create: 2020-03-23 10:04

*选择排序因为存在跨元素间的互换,所以是不稳定算法

* 时间复杂度是O(n^2)

* 空间复杂度是O(1)

**/

public class ChooseSort {

public static void chooseSort(int[]array){

int length=array.length;

for(int i=0;i

int min=Integer.MAX_VALUE;

int pos=0;

for(int j=i;j

if(array[j]

min=array[j];

pos=j;

}

}

int temp=array[pos];

array[pos]=array[i];

array[i]=temp;

}

}

public static void main(String[] args){

int[] a={49,38,65,97,76,13,27,49,78,34,12,64,1};

chooseSort(a);

System.out.println("排序结果:");

for (int i:a

) {

System.out.println(i);

}

}

}

5. 堆排序

对简单选择排序的优化。

将序列构建成大顶堆。

将根节点与最后一个节点交换,然后断开最后一个节点。

重复第一、二步,直到所有节点断开。

db4c9757ace45fd706194328c1cf385d.png

6. 冒泡排序

很简单,用到的很少,据了解,面试的时候问的比较多!

将序列中所有元素两两比较,将最大的放在最后面。

将剩余序列中所有元素两两比较,将最大的放在最后面。

重复第二步,直到只剩下一个数。

7b76c6cc6f37c2025d55b3c087e39d35.png

代码实现:

设置循环次数。

设置开始比较的位数,和结束的位数。

两两比较,将最小的放到前面去。

重复 2、3 步,直到循环次数完毕。

package com.jiading.myRewrite;

/**

* @program: 排序算法的Java实现

* @description: 冒泡排序

* @author: JiaDing

* @create: 2020-03-23 09:28

* 冒泡排序是稳定的

* 时间复杂度是O(n^2)

* 空间复杂度是O(1)

**/

public class BubbleSort {

public static void bubbleSort(int[]array){

int length=array.length;

for(int i=0;i

for(int j=0;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[] a={49,38,65,97,76,13,27,49,78,34,12,64,1};

bubbleSort(a);

System.out.println("排序结果:");

for (int i:a

) {

System.out.println(i);

}

}

}

7. 快速排序

要求时间最快时。

选择第一个数为 p,小于 p 的数放在左边,大于 p 的数放在右边。

递归的将 p 左边和右边的数都按照第一步进行,直到不能递归。

efa8b5c21ec8334d3415aff3876d66f2.png

package com.jiading.myRewrite;

import java.util.Random;

/**

* @program: 排序算法的Java实现

* @description: 快速排序

* @author: JiaDing

* @create: 2020-03-29 18:08

* 快速排序的平均时间复杂度是O(nlgn),最糟时间复杂度是O(n^2)

* 快速排序被公认在所有同数量级O(nlgn)的排序方法中平均性能最好,但如果排序序列有序时,快速排序将转为冒泡排序

* 空间复杂度是O(1)

* 快速排序算法是不稳定算法

**/

public class QuickSort {

public static void main(String[] args) {

int[] a = {49, 38, 65, 97, 76, 13, 27, 49, 78, 34, 12, 64, 1};

quickSort(a);

System.out.println("排序结果:");

for (int i : a

) {

System.out.println(i);

}

}

private static void quickSort(int[] a) {

qSort(0, a.length - 1, a);

}

private static void qSort(int left, int right, int[] a) {

if (left < right) {

//返回枢纽的位置,此时数组的元素已经根据比枢纽小还是大自动分开了

int pivotpos = Partition(left, right, a);

//再递归地处理各个部分

qSort(left, pivotpos - 1, a);

qSort(pivotpos + 1, right, a);

}

}

private static int Partition(int left, int right, int[] a) {

/*

int pivot = a[left];是把第一个元素作为枢纽,但是为了更好的平均时间复杂度,避免极端的情况,建议使用随机选择枢纽的方式

*/

Random random=new Random();

int index = random.nextInt(right - left + 1) + left;

int pivot = a[index];

//把index的元素和left的换一下,为的是这样就可以直接复用以第一个元素为枢纽的代码了

int temp=a[left];

a[left]=pivot;

a[index]=temp;

while (left < right) {//从两侧交替地向中间扫描

while (left < right && a[right] > pivot) {

right--;

}

a[left] = a[right];//将小于或者等于枢纽的元素移动到低位置

while (left < right && a[left] <= pivot) {

left++;

}

a[right] = a[left];//将大于枢纽的元素移动到低位置

}

a[left] = pivot;

return left;

}

}

8. 归并排序

速度仅次于快速排序,内存少的时候使用,可以进行并行计算的时候使用。

选择相邻两个数组成一个有序序列。

选择相邻的两个有序序列组成一个有序序列。

重复第二步,直到全部组成一个有序序列。

6073224721fa34368d423dea3c5f7c4b.png

package com.jiading.myRewrite;

/**

* @program: 排序算法的Java实现

* @description: 归并排序

* @author: JiaDing

* @create: 2020-03-26 00:02

* 时间复杂度是O(nlogn):每次都是n,一共logn次(1合为2,2合为4···)

* 空间复杂度是O(n)

* 归并算法是稳定的

**/

public class MergeSort {

public static void main(String[] args) {

int[]array={49,38,65,97,76,13,27,49,78,34,12,64,1};

int[]temp=new int[array.length];

mergeSort(array,temp,0,array.length-1);

for (int i:array

) {

System.out.println(i);

}

}

public static void mergeSort(int[]toSort,int[]temp,int left,int right){

if(left

int mid=(left+right)/2;

mergeSort(toSort,temp,left,mid);

mergeSort(toSort,temp,mid+1,right);

//merge的时候就顺便将数据从temp拷贝回toSort

merge(toSort,temp,left,mid,right);

}

}

public static void merge(int[]toSort,int[]temp, int left,int mid,int right){

//index是temp中的下标,leftStart和rightStart都是toSort中的下标

int leftStart=left,rightStart=mid+1,index=left;

while((leftStart<=mid)&&(rightStart<=right)){

if(toSort[leftStart]<=toSort[rightStart]){

temp[index]=toSort[leftStart];

index++;

leftStart++;

}else{

temp[index]=toSort[rightStart];

index++;

rightStart++;

}

}

if(leftStart>mid){

for(int i=rightStart;i<=right;i++){

temp[index]=toSort[i];

index++;

}

}else if(rightStart>right){

for(int i=leftStart;i<=mid;i++){

temp[index]=toSort[i];

index++;

}

}

//将数据拷贝回toSort

for(int i=left;i<=right;i++){

toSort[i]=temp[i];

}

}

}

9. 基数排序

用于大量数,很长的数进行排序时。

将所有的数的个位数取出,按照个位数进行排序,构成一个序列。

将新构成的所有的数的十位数取出,按照十位数进行排序,构成一个序列。

代码实现:

package com.jiading.myRewrite;

import java.util.LinkedList;

/**

* @program: 排序算法的Java实现

* @description: 基数排序

* @author: JiaDing

* @create: 2020-03-23 09:05

* 基数排序是稳定的

* 时间复杂度是O(k*n),k为数的最高位的数量

* 空间复杂度是O(n):虽然有十个桶,但是桶中放的元素总和还是n,所以占用总空间是O(n)

**/

public class BaseSort {

public static void baseSort(int[]array){

int num=array.length;

int max=Integer.MIN_VALUE;

int length=0;

for(int i=0;i

max=Math.max(max,array[i]);

}

length=String.valueOf(max).length();

LinkedList>list=new LinkedList>();

for(int i=0;i<10;i++){

list.add(new LinkedList());

}

for(int j=0;j

for(int i=0;i

/*

每一次都根据当前位放到各个桶里

*/

int index=(int)((array[i]%Math.pow(10,j+1))/Math.pow(10,j));

LinkedList integers = list.get(index);

integers.add(array[i]);

list.set(index,integers);

}

int count=0;

for(int i=0;i<10;i++){

LinkedList integers = list.get(i);

while(!integers.isEmpty()){

//当前次从桶里放回数组

Integer integer = integers.pollFirst();

array[count]=integer;

count++;

}

}

}

}

public static void main(String[] args) {

int[] a={49,38,65,97,76,13,27,49,78,34,12,64,1};

baseSort(a);

System.out.println("排序结果:");

for (int i:a

) {

System.out.println(i);

}

}

}

10. 总结:

一、稳定性:

稳定:冒泡排序、插入排序、归并排序和基数排序

不稳定:选择排序、快速排序、希尔排序、堆排序

二、平均时间复杂度

O(n^2): 直接插入排序,简单选择排序,冒泡排序。

在数据规模较小时(9W 内),直接插入排序,简单选择排序差不多。当数据较大时,冒泡排序算法的时间代价最高。性能为 O(n^2) 的算法基本上是相邻元素进行比较,基本上都是稳定的。

O(nlogn): 快速排序,归并排序,希尔排序,堆排序。

其中,快排是最好的, 其次是归并和希尔,堆排序在数据量很大时效果明显。

三、排序算法的选择

1. 数据规模较小

(1)待排序列基本序的情况下,可以选择直接插入排序;

(2)对稳定性不作要求宜用简单选择排序,对稳定性有要求宜用插入或冒泡

2. 数据规模不是很大

(1)完全可以用内存空间,序列杂乱无序,对稳定性没有要求,快速排序,此时要付出 log(N)的额外空间。

(2)序列本身可能有序,对稳定性有要求,空间允许下,宜用归并排序

3. 数据规模很大

(1)对稳定性有求,则可考虑归并排序。

(2)对稳定性没要求,宜用堆排序

4. 序列初始基本有序(正序),宜用直接插入,冒泡

各算法复杂度如下:

aea38098949fb4b81f5f94c2113ada42.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值