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

排序算法

  • 需要关注的内容:
  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

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

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

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值