选择排序,插入排序,希尔排序的详解与比较

排序算法

  • 需要关注的内容:
  1. 运行时间,性能
  2. 额外的内存使用
    • 原地排序算法:除了函数调用所需的栈和固定数目的实例变量之外无需额外内存
    • 其他排序算法:需要额外内存空间来存储另一份数组副本
  3. 接受任何可以比较的数据类型(泛型)

排序算法类模板

需要导入

package Sort;
import java.util.Scanner;
/**
*排序算法类的模板
*/
public class SortExample {
public static void sort(Comparable[] a){
//排序算法实现
}
public static boolean less(Comparable v, Comparable w){
//对元素进行比较
return v.compareTo(w)<0;
}
public static void exch(Comparable[] a,int i,int j){
//交换元素
Comparable t=a[i];
a[i]=a[j];
a[j]=t;
}
public static void show(Comparable[] a){
//在单行中打印数组
for(int i=0;i<a.length;i++){
System.out.print(a[i]+"");
System.out.println();
}
}
public static boolean isSorted(Comparable[] a){
//测试数组元素是否有序
for(int i=1;i<a.length;i++){
if(less(a[i],a[i-1])){
return false;
}
}
return true;
}
public static void main(String[] args){
Scanner scanner=new Scanner(System.in);
while (scanner.hasNext()){
String line=scanner.nextLine();
String[] a=line.split(" ");
sort(a);
assert isSorted(a);
show(a);
}
}
}

选择排序

不断选择剩余元素之中的最小者(最大者)

找到数组的最小(最大)的元素,与数组第一个元素交换位置,然后再剩下的部分中找到最小(最大)的元素,与数组的第二个元素交换,往复,直到数组末尾

在这里插入图片描述

import static Sort.SortExample.*;
// 选择排序
public class SelectSort {
public static void sort(Comparable[] a){
int N = a.length;
for(int i = 0; i < N; i ++){
int min = i;
for(int j = i+1; j < N; j++)
if(less(a[j], a[min])) // 当 a[j] < a[min]时,为true 所以为从小到大排列
min = j;
exch(a, i, min); // 在数组a中交换索引为 i min 的数据
}
}
}

特点

  • 运行时间与输入无关,与数组的有序性无关(对一个有序数组和无序数组,所用时间相同)

  • 数据移动最少 N次交换

时间复杂度

N 2 N^2 N2

插入排序

始终保持索引左边有序,新加入的元素不断向左移动,插入到左侧有序部分

在这里插入图片描述

import static Sort.SortExample.*;
public class insertSort {
public static void sort(Comparable[] b){
int N = a.length;
for(int i = 0; i < N; i ++){
// 循环时要保证当前索引数据小于左边的数据 否则终止循环
for(int j = i; j > 0 && less(a[j], a[j-1]); j --)
exch(a, j, j-1);
}
}
}

特点:

  • 运行时间与数组的有序性有关,成正比

  • 对于处理部分有序的数组很有效

    • 部分有序数组

      1. 数组中每个元素距离它的最终位置都不远
      2. 一个有序的大数组接一个小数组
      3. 数组中只有几个元素的位置不正确

时间复杂度

N 2 N^2 N2

希尔排序

经过优化的插入排序

对于大规模的乱序数组普通的插入排序很慢,因为它只会交换相邻的元素,元素只能一点一点地移动

优化后,希尔排序可以交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的小数组排序

步骤:

  1. 将大数组分成多个小数组,每隔 h-1 个元素为一个小数组
  2. 对每个小数组进行插入排序
  3. 缩小h 再进行小数组排序
  4. 继续缩小 h 直到h = 1 为普通的插入排序,即完成
package Sort;
import java.util.Arrays;
import java.util.Random;
import static Sort.SortExample.*;
// 优化后的插入排序
public class ShellSort {
public static void sort(Comparable[] b) {
int n = a.length;
int h = 1;
while (h < n / 3)
h = 3 * h + 1;
System.out.println(Arrays.toString(a));
while (h >= 1) {
// 将数组排序
for (int i = h; i < n; i++){
for (int j = i; j >= h && less(a[j], a[j - h]); j -= h) {
exch(a, j, j - h);
}
}
h /= 3; // h 变化 当 h变为 1 时, 变为普通插入排序
System.out.println(Arrays.toString(a));
}
}
}

特点:

  • 比前两者都快,数组越大,优势越明显
  • 运算时间小于 N^2
  • 解决问题时,若系统内置排序函数不可用,首先尝试希尔排序

理解希尔排序:

package Sort;
import java.util.Arrays;
import java.util.Random;
import static Sort.SortExample.*;
// 优化后的插入排序
public class ShellSort {
public static void sort(Comparable[] b) {
Comparable[] a = new Comparable[b.length];
System.arraycopy(b, 0, a, 0, b.length);
int n = a.length;
int h = 1;
while (h < n / 3)
h = 3 * h + 1;
System.out.println(Arrays.toString(a));
while (h >= 1) {
System.out.println("h 等于" + h);
// 将数组排序
for (int i = h; i < n; i++){ // i++ 进入下一个小数组的排序 每隔 h-1个元素为一个数组
System.out.println("i 等于" + i);
// 将 a[i] 插入到 a[i-h], a[i-2*h], a[i-3*h] 之中
// 先进行判断 a[j-h] 是否大于 a[j] 若不是不交换
for (int j = i; j >= h && less(a[j], a[j - h]); j -= h) { // 保证这个小数组的有序性
System.out.println("要做交换的是:" + a[j - h] + ", " + a[j]);
exch(a, j, j - h);
System.out.println(Arrays.toString(a));
}
}
h /= 3; // h 变化 当 h变为 1 时, 变为普通插入排序
System.out.println(Arrays.toString(a));
}
if(!isSorted(a))
System.out.println("希尔排序失败");
}
public static void main(String[] args) {
Comparable[] test2 = new Comparable[10];
Random random = new Random();
for(int i = 0; i < 10; i ++)
test2[i] = random.nextInt(100);
sort(test2);
}
}

输出为:

[11, 39, 27, 81, 57, 94, 66, 89, 53, 7]
h 等于4
要做交换的是:57, 53
[11, 39, 27, 81, 53, 94, 66, 89, 57, 7]
要做交换的是:94, 7
[11, 39, 27, 81, 53, 7, 66, 89, 57, 94]
要做交换的是:39, 7
[11, 7, 27, 81, 53, 39, 66, 89, 57, 94]
[11, 7, 27, 81, 53, 39, 66, 89, 57, 94]
h 等于1
要做交换的是:11, 7
[7, 11, 27, 81, 53, 39, 66, 89, 57, 94]
要做交换的是:81, 53
[7, 11, 27, 53, 81, 39, 66, 89, 57, 94]
要做交换的是:81, 39
[7, 11, 27, 53, 39, 81, 66, 89, 57, 94]
要做交换的是:53, 39
[7, 11, 27, 39, 53, 81, 66, 89, 57, 94]
要做交换的是:81, 66
[7, 11, 27, 39, 53, 66, 81, 89, 57, 94]
要做交换的是:89, 57
[7, 11, 27, 39, 53, 66, 81, 57, 89, 94]
要做交换的是:81, 57
[7, 11, 27, 39, 53, 66, 57, 81, 89, 94]
要做交换的是:66, 57
[7, 11, 27, 39, 53, 57, 66, 81, 89, 94]
[7, 11, 27, 39, 53, 57, 66, 81, 89, 94]

三种排序算法的比较

数据来源:algo4 官方提供的 algo4-data.zip 数据包

先进行1k数据量的比较:

选择排序,1000个数据,执行时间为:0.012673201 s
插入排序,1000个数据,执行时间为:0.0126063 s
希尔排序,1000个数据,执行时间为:0.0015172 s

进行32k数据量的比较:

选择排序,32000个数据,执行时间为:4.1962712 s
插入排序,32000个数据,执行时间为:2.6067247 s
希尔排序,32000个数据,执行时间为:0.027314501 s

其他有关排序算法的文章:
归并排序详解,与其他排序算法的比较

快速排序算法详解与其他排序算法的比较

本文作者:清澈的澈

本文链接:https://www.cnblogs.com/lmc7/articles/17531396.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   清澈的澈  阅读(43)  评论(0编辑  收藏  举报  
评论
收藏
关注
推荐
深色
回顶
收起
点击右上角即可分享
微信分享提示