Java排序算法(五):快速排序

快速排序是一个速度非常快的交换排序算法,它的基本思路很简单,从待排的数据序列中任取一个数据(如第一个数据)作为分界值,所有比它小的数据元素放到左边,所有比它大的数据元素放到它的右边。经过这样一趟下来,该序列形成左右两个子序列,左边序列中的数据元素的值都比分界值小,右边序列中数据元素的值都比分界值大。

接下来对左右两个子序列进行递归排序,对两个子序列重新选择中心元素并依此规则调整,直到每个元素子表的元素只剩下一个元素,排序完成。

思路:

1.定义一个i变量,i变量从左边第一个索引开始,找大于分界值的元素的索引,并用i来记录它。

2.定义一个j变量,j变量从右边第一个索引开始,找小于分界值的元素的索引,并用j来记录它。

3.如果i

重复执行以上1,2,3步,直到i>=j,可以判断j左边的数据元素都小于分界值,j右边的数据元素都大于分界值,最后将分界值和j索引处的元素交换即可。

时间复杂度

最好情况(每次总是选到中间值作枢轴)T(n)=O(nlogn)

最坏情况(每次总是选到最小或最大元素作枢轴)

做n-1趟,每趟比较n-i次,总的比较次数最大:[O(n²)]

平均时间复杂度为::T(n)=O(nlogn)

代码实现:

packagesort;

publicclassQuickSortTest {

publicstaticvoidmain(String[] args) {

int[] data =newint[] {5,3,6,2,1,9,4,8,7};

print(data);

quickSort(data,0, data.length -1);

System.out.println("排序后的数组:");

print(data);

}

publicstaticvoidswap(int[] data,inti,intj) {

if(i == j) {

return;

}

data[i] = data[i] + data[j];

data[j] = data[i] - data[j];

data[i] = data[i] - data[j];

}

publicstaticvoidquickSort(int[] data,intstart,intend) {

if(start >= end)

return;

//以起始索引为分界点

intpivot = data[start];

inti = start +1;

intj = end;

while(true) {

while(i <= end && data[i] < pivot) {

i++;

}

while(j > start && data[j] > pivot) {

j--;

}

if(i < j) {

swap(data, i, j);

}else{

break;

}

}

//交换 j和分界点的值

swap(data, start, j);

print(data);

//递归左子序列

quickSort(data, start, j -1);

//递归右子序列

quickSort(data, j +1, end);

}

publicstaticvoidprint(int[] data) {

for(inti =0; i < data.length; i++) {

System.out.print(data[i] +"\t");

}

System.out.println();

}

}

运行结果:

536219487

134259687

134259687

123459687

123457689

123456789

排序后的数组:

123456789

Java排序算法(六):直接插入排序

直接插入排序的基本操作就是将待排序的数据元素按其关键字值的大小插入到前面的有序序列中。

直接插入的时间效率并不高,如果在最坏的情况下,所有元素的比较次数总和为(0+1+...+n-1)=O(n^2)。其他情况下也要考虑移动元素的次数,故时间复杂度为O(n^2)

直接插入空间效率很好,只需要1个缓存数据单元,也就是说空间复杂度为O(1).

直接插入排序是稳定的。

直接插入排序在数据已有一定顺序的情况下,效率较好。但如果数据无规则,则需要移动大量的数据,其效率就与冒泡排序法和选择排序法一样差了。

算法描述

对一个有n个元素的数据序列,排序需要进行n-1趟插入操作:

第1趟插入,将第2个元素插入前面的有序子序列--此时前面只有一个元素,当然是有序的。

第2趟插入,将第3个元素插入前面的有序子序列,前面2个元素是有序的。

第n-1趟插入,将第n个元素插入前面的有序子序列,前面n-1个元素是有序的。

代码实现

packagesort;

publicclassInsertSortTest {

publicstaticintcount =0;

publicstaticvoidmain(String[] args) {

int[] data =newint[] {5,3,6,2,1,9,4,8,7};

print(data);

insertSort(data);

print(data);

}

publicstaticvoidinsertSort(int[] data) {

for(inti =1; i < data.length; i++) {

// 缓存i处的元素值

inttmp = data[i];

if(data[i] < data[i -1]) {

intj = i -1;

// 整体后移一格

while(j >=0&& data[j] > tmp) {

data[j +1] = data[j];

j--;

}

// 最后将tmp插入合适的位置

data[j +1] = tmp;

print(data);

}

}

}

publicstaticvoidprint(int[] data) {

for(inti =0; i < data.length; i++) {

System.out.print(data[i] +"\t");

}

System.out.println();

}

}

运行结果:

536219487

356219487

235619487

123569487

123456987

123456897

123456789

123456789

2011-07-06 21:1081人阅读 评论(2)

Java排序算法(七):折半插入排序

折半插入排序法,又称二分插入排序法,是直接插入排序法的改良版,也需要执行i-1趟插入,不同之处在于,第i趟插入,先找出第i+1个元素应该插入的的位置,假定前i个数据是已经处于有序状态。

代码实现:

packagesort;

publicclassBinaryInsertSortTest {

publicstaticintcount =0;

publicstaticvoidmain(String[] args) {

int[] data =newint[] {5,3,6,2,1,9,4,8,7};

print(data);

binaryInsertSort(data);

print(data);

}

publicstaticvoidbinaryInsertSort(int[] data) {

for(inti =1; i < data.length; i++) {

if(data[i] < data[i -1]) {

// 缓存i处的元素值

inttmp = data[i];

// 记录搜索范围的左边界

intlow =0;

// 记录搜索范围的右边界

inthigh = i -1;

while(low <= high) {

// 记录中间位置

intmid = (low + high) /2;

// 比较中间位置数据和i处数据大小,以缩小搜索范围

if(data[mid] < tmp) {

low = mid +1;

}else{

high = mid -1;

}

}

//将low~i处数据整体向后移动1位

for(intj = i; j > low; j--) {

data[j] = data[j -1];

}

data[low] = tmp;

print(data);

}

}

}

publicstaticvoidprint(int[] data) {

for(inti =0; i < data.length; i++) {

System.out.print(data[i] +"\t");

}

System.out.println();

}

}

运行结果:

536219487

356219487

235619487

123569487

123456987

123456897

123456789

123456789

Java排序算法(八):希尔排序(Shell排序)

希尔排序(缩小增量法) 属于插入类排序,由Shell提出,希尔排序对直接插入排序进行了简单的改进:它通过加大插入排序中元素之间的间隔,并在这些有间隔的元素中进行插入排序,从而使数据项大跨度地移动,当这些数据项排过一趟序之后,希尔排序算法减小数据项的间隔再进行排序,依次进行下去,进行这些排序时的数据项之间的间隔被称为增量,习惯上用字母h来表示这个增量。

常用的h序列由Knuth提出,该序列从1开始,通过如下公式产生:

h = 3 * h +1

反过来程序需要反向计算h序列,应该使用

h=(h-1)/3

代码实现:

packagesort;

publicclassShellSortTest {

publicstaticintcount =0;

publicstaticvoidmain(String[] args) {

int[] data =newint[] {5,3,6,2,1,9,4,8,7};

print(data);

shellSort(data);

print(data);

}

publicstaticvoidshellSort(int[] data) {

// 计算出最大的h值

inth =1;

while(h <= data.length /3) {

h = h *3+1;

}

while(h >0) {

for(inti = h; i < data.length; i += h) {

if(data[i] < data[i - h]) {

inttmp = data[i];

intj = i - h;

while(j >=0&& data[j] > tmp) {

data[j + h] = data[j];

j -= h;

}

data[j + h] = tmp;

print(data);

}

}

// 计算出下一个h值

h = (h -1) /3;

}

}

publicstaticvoidprint(int[] data) {

for(inti =0; i < data.length; i++) {

System.out.print(data[i] +"\t");

}

System.out.println();

}

}

运行结果:

536219487

136259487

123659487

123569487

123456987

123456897

123456789

123456789

上面程序在和直接插入法比较,会发现其与直接插入排序的差别在于:直接插入排序中的h会以1代替

Shell排序是不稳定的,它的空间开销也是O(1),时间开销估计在O(N3/2)~O(N7/6)之间

Java排序算法(九):归并排序

归并排序(Merge)是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。 将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

归并排序算法稳定,数组需要O(n)的额外空间,链表需要O(log(n))的额外空间,时间复杂度为O(nlog(n)),算法不是自适应的,不需要对数据的随机读取。

工作原理:

1、申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

2、设定两个指针,最初位置分别为两个已经排序序列的起始位置

3、比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置

4、重复步骤3直到某一指针达到序列尾

5、将另一序列剩下的所有元素直接复制到合并序列尾

代码实现:

publicclassMergeSortTest {

publicstaticvoidmain(String[] args) {

int[] data =newint[] {5,3,6,2,1,9,4,8,7};

print(data);

mergeSort(data);

System.out.println("排序后的数组:");

print(data);

}

publicstaticvoidmergeSort(int[] data) {

sort(data,0, data.length -1);

}

publicstaticvoidsort(int[] data,intleft,intright) {

if(left >= right)

return;

// 找出中间索引

intcenter = (left + right) /2;

// 对左边数组进行递归

sort(data, left, center);

// 对右边数组进行递归

sort(data, center +1, right);

// 合并

merge(data, left, center, right);

print(data);

}

/**

* 将两个数组进行归并,归并前面2个数组已有序,归并后依然有序

*

* @param data

* 数组对象

* @param left

* 左数组的第一个元素的索引

* @param center

* 左数组的最后一个元素的索引,center+1是右数组第一个元素的索引

* @param right

* 右数组最后一个元素的索引

*/

publicstaticvoidmerge(int[] data,intleft,intcenter,intright) {

// 临时数组

int[] tmpArr =newint[data.length];

// 右数组第一个元素索引

intmid = center +1;

// third 记录临时数组的索引

intthird = left;

// 缓存左数组第一个元素的索引

inttmp = left;

while(left <= center && mid <= right) {

// 从两个数组中取出最小的放入临时数组

if(data[left] <= data[mid]) {

tmpArr[third++] = data[left++];

}else{

tmpArr[third++] = data[mid++];

}

}

// 剩余部分依次放入临时数组(实际上两个while只会执行其中一个)

while(mid <= right) {

tmpArr[third++] = data[mid++];

}

while(left <= center) {

tmpArr[third++] = data[left++];

}

// 将临时数组中的内容拷贝回原数组中

// (原left-right范围的内容被复制回原数组)

while(tmp <= right) {

data[tmp] = tmpArr[tmp++];

}

}

publicstaticvoidprint(int[] data) {

for(inti =0; i < data.length; i++) {

System.out.print(data[i] +"\t");

}

System.out.println();

}

}

运行结果:

536219487

356219487

356219487

356129487

123569487

123564987

123564978

123564789

123456789

排序后的数组:

123456789

Java排序算法(十):桶式排序

桶式排序不再是一种基于比较的排序方法,它是一种比较巧妙的排序方式,但这种排序方式需要待排序的序列满足以下两个特征:

待排序列所有的值处于一个可枚举的范围之类;

待排序列所在的这个可枚举的范围不应该太大,否则排序开销太大。

排序的具体步骤如下:

(1)对于这个可枚举范围构建一个buckets数组,用于记录“落入”每个桶中元素的个数;

(2)将(1)中得到的buckets数组重新进行计算,按如下公式重新计算:

buckets[i] = buckets[i] +buckets[i-1] (其中1<=i

桶式排序是一种非常优秀的排序算法,时间效率极高,它只要通过2轮遍历:第1轮遍历待排数据,统计每个待排数据“落入”各桶中的个数,第2轮遍历buckets用于重新计算buckets中元素的值,2轮遍历后就可以得到每个待排数据在有序序列中的位置,然后将各个数据项依次放入指定位置即可。

桶式排序的空间开销较大,它需要两个数组,第1个buckets数组用于记录“落入”各桶中元素的个数,进而保存各元素在有序序列中的位置,第2个数组用于缓存待排数据。

桶式排序是稳定的。

如果待排序数据的范围在0~k之间,那么它的时间复杂度是O(k+n)的

桶式排序算法速度很快,因为它的时间复杂度是O(k+n),而基于交换的排序时间上限是nlgn。

但是它的限制多,比如它只能排整形数组。而且当k较大,而数组长度n较小,即k>>n时,辅助数组C[k+1]的空间消耗较大。

当数组为整形,且k和n接近时, 可以用此方法排序。(有的文章也称这种排序算法为“计数排序”)

代码实现:

publicclassBucketSortTest {

publicstaticintcount =0;

publicstaticvoidmain(String[] args) {

int[] data =newint[] {5,3,6,2,1,9,4,8,7};

print(data);

bucketSort(data,0,10);

print(data);

}

publicstaticvoidbucketSort(int[] data,intmin,intmax) {

// 缓存数组

int[] tmp =newint[data.length];

// buckets用于记录待排序元素的信息

// buckets数组定义了max-min个桶

int[] buckets =newint[max - min];

// 计算每个元素在序列出现的次数

for(inti =0; i < data.length; i++) {

buckets[data[i] - min]++;

}

// 计算“落入”各桶内的元素在有序序列中的位置

for(inti =1; i < max - min; i++) {

buckets[i] = buckets[i] + buckets[i -1];

}

// 将data中的元素完全复制到tmp数组中

System.arraycopy(data,0, tmp,0, data.length);

// 根据buckets数组中的信息将待排序列的各元素放入相应位置

for(intk = data.length -1; k >=0; k--) {

data[--buckets[tmp[k] - min]] = tmp[k];

}

}

publicstaticvoidprint(int[] data) {

for(inti =0; i < data.length; i++) {

System.out.print(data[i] +"\t");

}

System.out.println();

}

}

运行结果:

536219487

123456789

Java排序算法(十一):基数排序

基数排序已经不再是一种常规的排序方式,它更多地像一种排序方法的应用,基数排序必须依赖于另外的排序方法。基数排序的总体思路就是将待排序数据拆分成多个关键字进行排序,也就是说,基数排序的实质是多关键字排序。

多关键字排序的思路是将待排数据里德排序关键字拆分成多个排序关键字;第1个排序关键字,第2个排序关键字,第3个排序关键字......然后,根据子关键字对待排序数据进行排序。

多关键字排序时有两种解决方案:

最高位优先法(MSD)(Most Significant Digit first)

最低位优先法(LSD)(Least Significant Digit first)

例如,对如下数据序列进行排序。

192,221,12,23

可以观察到它的每个数据至多只有3位,因此可以将每个数据拆分成3个关键字:百位(高位)、十位、个位(低位)。

如果按照习惯思维,会先比较百位,百位大的数据大,百位相同的再比较十位,十位大的数据大;最后再比较个位。人得习惯思维是最高位优先方式。

如果按照人得思维方式,计算机实现起来有一定的困难,当开始比较十位时,程序还需要判断它们的百位是否相同--这就认为地增加了难度,计算机通常会选择最低位优先法。

基数排序方法对任一子关键字排序时必须借助于另一种排序方法,而且这种排序方法必须是稳定的。

对于多关键字拆分出来的子关键字,它们一定位于0-9这个可枚举的范围内,这个范围不大,因此用桶式排序效率非常好。

对于多关键字排序来说,程序将待排数据拆分成多个子关键字后,对子关键字排序既可以使用桶式排序,也可以使用任何一种稳定的排序方法。

代码实现:

importjava.util.Arrays;

publicclassMultiKeyRadixSortTest {

publicstaticvoidmain(String[] args) {

int[] data =newint[] {1100,192,221,12,23};

print(data);

radixSort(data,10,4);

System.out.println("排序后的数组:");

print(data);

}

publicstaticvoidradixSort(int[] data,intradix,intd) {

// 缓存数组

int[] tmp =newint[data.length];

// buckets用于记录待排序元素的信息

// buckets数组定义了max-min个桶

int[] buckets =newint[radix];

for(inti =0, rate =1; i < d; i++) {

// 重置count数组,开始统计下一个关键字

Arrays.fill(buckets,0);

// 将data中的元素完全复制到tmp数组中

System.arraycopy(data,0, tmp,0, data.length);

// 计算每个待排序数据的子关键字

for(intj =0; j < data.length; j++) {

intsubKey = (tmp[j] / rate) % radix;

buckets[subKey]++;

}

for(intj =1; j < radix; j++) {

buckets[j] = buckets[j] + buckets[j -1];

}

// 按子关键字对指定的数据进行排序

for(intm = data.length -1; m >=0; m--) {

intsubKey = (tmp[m] / rate) % radix;

data[--buckets[subKey]] = tmp[m];

}

rate *= radix;

}

}

publicstaticvoidprint(int[] data) {

for(inti =0; i < data.length; i++) {

System.out.print(data[i] +"\t");

}

System.out.println();

}

}

运行结果:

11001922211223

排序后的数组:

12231922211100

Java排序算法(十二):总结

前面讲了10种基本的排序算法,现在来作下总结,基于下面几个方面来比较各个排序算法的优劣:

时间复杂度,空间复杂度,稳定性,适用场景

排序算法

时间复杂度

空间复杂度

稳定性

适用场景

直接选择排序

O(n^2)

O(1)

不稳定

时间效率不高,但是空间效率很高,算法实现比较简单

堆排序

O(nlogn),底数为2

O(1)

不稳定

时间效率很高,但是不稳定

冒泡排序

O(n^2)

O(1)

稳定

算法实现比较简单,稳定,且对于已基本排序的数据排序,时间复杂度为O(n)

快速排序

最好O(nlogn),底数为2

最坏O(n^2)

平均O(nlogn),底数为2

O(logn),底数为2

不稳定

时间效率很高,但是不稳定

直接插入排序

O(n^2)

O(1)

稳定

折半插入排序

O(n^2)

O(1)

稳定

时间效率比直接插入排序要好

希尔排序

O(n(logn)^2),底数为2

O(1)

不稳定

归并排序

O(nlogn),底数为2

O(n)

稳定

空间复杂度较高

桶式排序

O(k+n)

O(k+n)

稳定

待排序数据的范围在0~k之间,只能为整形序列

基数排序

稳定

依赖子关键字排序算法,子关键字排序算法必须是稳定的

java系统排序_java各种排序实现相关推荐

  1. java 数据库排序_Java如何排序数据库表的数据内容?

    在Java编程中,如何排序数据库表的数据内容?假定数据库名称是:testdb,其中有一个表:employee,这个表中有4条记录. 创建数据库表的语句 - use testdb; create tab ...

  2. java 8 排序_Java 八大排序实现

    参考链接 本文只给出算法的Java实现版本,具体原理参考:八大排序算法. 公用代码 下面的swap()函数,是排序算法中经常用到的,单独贴出来. public void swap(int[] a, i ...

  3. java史努比_Java八大排序

    Arrays.sort() 采用了2种排序算法 -- 基本类型数据使用快速排序法,对象数组使用归并排序. java的Collections.sort算法调用的是归并排序,它是稳定排序 方法一:直接插入 ...

  4. java map按照key排序_java Map排序(按key和按value)

    1.按照key排序 对于java中Map的排序,有排序Map,比如TreeMap,对于这个Map,首先只能按照键排序,其次再put和remove的时候由于需要排序,性能上会有所牺牲. 这种方案,使用h ...

  5. java 系统 类_Java常用实体类--System类

    字符串.日期.数字是Java程序中最常使用的数据对象,对这些数据的创建.修改.格式化和转换等操作融入在Java程序的每个角落,必须熟练掌握.本节将通过实例演示以下常用实体类Java系统级类:系统类Sy ...

  6. java 10个随机数排序_java随机数排序的问题

    java随机数排序的问题 关注:68  答案:4  mip版 解决时间 2021-01-27 15:23 提问者侢遇噹姩揂 2021-01-27 08:24 public class RN { pub ...

  7. java对象排序_java对象排序(Comparable)详细实例

    对象实现Comparable接口 package collections.sort.comparable; public class Field implements Comparable{ priv ...

  8. java 性能 排序_Java常用排序算法及性能测试集合

    package algorithm.sort; import java.lang.reflect.Method; import java.util.Arrays; import java.util.D ...

  9. java list 排序_java list排序

    java list 排序主要用到: Collections.sort方法: package com.tom.compare; import java.util.ArrayList; import ja ...

  10. java set排序_Java Set排序的方法

    Java Set排序的方法 Set中TreeSet 本身就是有序的元素,那么下面重点介绍下HashSet的2种排序方法. 1. 把HashSet保存在ArrayList里,再用Collections. ...

最新文章

  1. 【SQL】IS NULL and = NULL 在 sql server 中的区别
  2. 前端每日实战:60# 视频演示如何用纯 CSS 创作一块乐高积木
  3. ESP8266 WiFi串口模块的学习与使用(一)
  4. 源码解读:KubeVela 是如何将 appfile 转换为 K8s 特定资源对象的
  5. Docker(十二):Docker集群管理之Compose
  6. 【原创】linux 批量清空文本内容
  7. 诺奖经济学家:中国与世界可找到差异化空间推动合作共赢
  8. java dot画图_小O的图案 (Java代码)(最简单的解法)
  9. MySQL Innodb数据库性能实践——VARCHAR vs CHAR
  10. 俯瞰大雾弥漫下的鄱阳湖二桥
  11. 大数据在农业农村的应用
  12. Mac基础操作:在双显示器设置中将Dock保持在一个屏幕上以防止其移动的方法
  13. Android WideVine
  14. 全网最全软件版本号相关内容科普
  15. 申请公网IP实战 #华北天津联通
  16. 前端学习之html特殊符号
  17. APK 签名:v1 v2 v3 v4
  18. java面试基础题整理(二)
  19. 初识DE2-115(包含光盘demo)
  20. ros学习(2-2):ros节点创建(python)

热门文章

  1. Day003 20210208
  2. c语言打印字符数据在屏幕上,在屏幕上输出各种类型的数据
  3. 阿里云云计算 27 在线实验--SLB初体验
  4. 极客大学架构师训练营 框架开发 第三次作业
  5. 【Python】statsmodels.formula.api模块中ols参数的解释
  6. 387.字符串中的第一个唯一字符
  7. 309.最佳买卖股票时机含冷冻期
  8. 138.复制带随机指针的链表
  9. numpy.squeeze()的用法
  10. Some of the operators in the model are not supported by the standard TensorFlow Lite runtime.