排序算法之冒泡、插入和希尔排序

本文介绍了三种简单的排序算法:冒泡排序、插入排序和希尔排序。冒泡排序通过不断交换相邻逆序元素实现排序;插入排序类似人工排序扑克牌,将元素逐一插入已排序部分;希尔排序则是改进的插入排序,通过间隔h的元素排序加速过程。文中还提供了每种排序算法的原理、示例和Java实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简单排序算法

冒泡排序

冒泡排序算法是一种很简单的排序算法,它重复的访问要排序的数列,比较两个相邻的元素,如果这个两个元素不是出于正确的位置及左边的元素比右边大,则交换这两个元素的位置(从小到大排序)。一直重复这个过程直到所有的元素都是有序的。

例如:对{5 1 4 2 8}进行从小到大进行排序。
第一遍
( 5 1 4 2 8 ) ( 1 5 4 2 8 ), 首先, 冒泡算法比较第一个和第二个元素,因为 5>1 所以进行交换。
( 1 5 4 2 8 ) ( 1 4 5 2 8 ), 因为 5>4 进行交换
( 1 4 5 2 8 ) ( 1 4 2 5 8 ), 交换,因为 5>2
( 1 4 2 5 8 ) ( 1 4 2 5 8 ), 现在, 因为这两个元素出于有序状态 (8>5) ,所有算法并不交换他们。
第二遍
( 1 4 2 5 8 ) ( 1 4 2 5 8 )
( 1 4 2 5 8 ) ( 1 2 4 5 8 ), 交换因为 4>2
( 1 2 4 5 8 ) ( 1 2 4 5 8 )
( 1 2 4 5 8 ) ( 1 2 4 5 8 )
现在该数组是有序的了,但是该算法并不知道是否已经完成了排序。因此,该算法需要继续访问数组,如果访问数组时没有任何的元素交换,则说明该数组是有序的
第三遍
( 1 2 4 5 8 ) ( 1 2 4 5 8 )
( 1 2 4 5 8 ) ( 1 2 4 5 8 )
( 1 2 4 5 8 ) ( 1 2 4 5 8 )
( 1 2 4 5 8 ) ( 1 2 4 5 8 )
BubbleSort

这里写图片描述

插入排序

插入排序的工作方式像许多人工排序一手扑克牌。开始时,我们的左手边为空并且桌子上的牌面向下。然后,我们每次从桌子上拿走一张牌并将它插入左手中正确的位置。为了找到一张牌正确的位置,我们从右到左将它与已在手中的每张牌进行比较,如图所示。拿在左手上的牌总是排好序的,原来这些牌是桌子上牌堆中顶部的牌。
插入排序

其插入排序算法的过程为:
insertSort

代码为:
insertCode

这里写图片描述
这里写图片描述

希尔排序

由于插入排序一次只能消除一个逆序对,及元素只能一点一点地从数组的一端移动到另一端。希尔排序为了加快速度简单的修改了插入排序,交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。

希尔排序的思想是使数组中任意间隔为h的元素是有序的。这样的数组称为h有序数组。换句话说,一个h有序数组就是h个互相独立的有序数组编织在一起组成一个数组。在进行排序是,如果好很大,我们就能将元素移动到很远的地方,为实现更小的h有序创造方便。用这种方式,对于任意以1结尾的h序列,我们都能够将数组排序。这就是希尔排序。

理论很难懂,来点实际的举个“栗子”吧。列如对数列{81, 94, 11, 96, 12, 35, 17, 95, 28, 58, 41, 75, 15},进行希尔排序。

首先使用5间隔进行排序
1
2
持续这个过程直到所有以5间隔的元素都是有序的
3
接下来说继续对上列数列分别进行3间隔和1间隔排序
4

其中我们把5间隔,3间隔,1间隔称为递增序列或者说增量序列。增量序列满足以下条件
5
对于不同的增量序列,希尔排序不同的性能。其中比较常用的增量序列为
Dk=3Dk1+1 where D1=1
这里写图片描述

希尔排序代码为:
这里写图片描述

三种代码的Java实现为:

package SimpleSort;


import java.util.*;
import static java.lang.System.out;

public class Sort {
    private static  <T> void swap(T [] arr, int i, int j) {
        T tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

    /**
     * 冒泡排序算法
     * @param arr 要排序的数组
     */
    public static  void bubbleSort(Comparable [] arr) {
        int N = arr.length;
        boolean flag = false;

        for (int i = 0; i < arr.length; ++i) {
            flag = false;
            for (int j = 0; j < arr.length - 1 - i; ++j) {
                if (arr[j].compareTo(arr[j+1]) > 0) {
                    swap(arr, j, j+1);
                    flag = true;
                }

            }
            // 如果没有进行交换,说明已经排好序
            if (!flag) break;
        }
    }

    /**
     * 冒泡排序算法
     * @param arr 要排序的数组
     * @param comparator 类比较器
     * @param <T> 泛型参数
     */
    public static <T> void bubbleSort(T [] arr, Comparator comparator) {
        int N = arr.length;
        boolean flag = false;

        for (int i = 0; i < arr.length; ++i) {
            flag = false;
            for (int j = 0; j < arr.length - 1 - i; ++j) {
                if (comparator.compare(arr[j], arr[j+1]) > 0) {
                    swap(arr, j, j+1);
                    flag = true;
                }

            }
            // 如果没有进行交换,说明已经排好序
            if (!flag) break;
        }
    }

    /**
     * 插入排序
     * @param arr 要排序的数组
     */
    public static void insertSort(Comparable [] arr) {
        for (int i = 1; i < arr.length; ++i) {
            Comparable key = arr[i];
            int j = i - 1;
            while (j >= 0 && arr[j].compareTo(key) > 0) {
                arr[j+1] = arr[j];
                --j;
            }
            arr[j+1] = key;
        }
    }

    /**
     * 插入排序
     * @param arr 要排序的数组
     * @param comparator 比较器
     * @param <T> 泛型参数
     */
    public static <T> void insertSort(T [] arr, Comparator comparator) {
        for (int i = 1; i < arr.length; ++i) {
            T key = arr[i];
            int j = i - 1;
            while (j >= 0 && comparator.compare(arr[j], key) > 0) {
                arr[j+1] = arr[j];
                --j;
            }
            arr[j+1] = key;
        }
    }


    /**
     * 希尔排序
     * @param arr 要排序的数组
     */
    public static  void shellSort(Comparable [] arr) {
        int N = arr.length;
        int k = 1, hibb;

        // 逆序求出Hibbard增量序列
        List<Integer> gaps = new ArrayList<>();
        while(true) {
            hibb = (int)(Math.pow(2, k) - 1);
            if (hibb > N) break;
            gaps.add(hibb);
            ++k;
        }

        for (k = gaps.size()-1; k >= 0; --k) {
            int gap = gaps.get(k);
            for (int i = gap; i < N; ++i) {
                Comparable key = arr[i];
                int j = i;
                while (j >= gap && arr[j-gap].compareTo(key) >= 0) {
                    swap(arr, j, j - gap);
                    j -= gap;
                }
                arr[j] = key;
            }
        }
    }


    public static double time(String alg) {

        Double [] arr = new Double[100000];
        for (int i = 0; i < arr.length; ++i)
            arr[i] = Math.random();

        long callTime = System.currentTimeMillis();
        if (alg.equals("bubbleSort")) bubbleSort(arr);
        else if (alg.equals("insertSort")) insertSort(arr);
        else if (alg.equals("shellSort")) shellSort(arr);
        callTime = System.currentTimeMillis() - callTime;
        return callTime*1.0/1000;
    }


    public static void main(String [] args) {
        Integer [] arr = new Integer[10];
        Random random = new Random(123);
        for (int i = 0; i < arr.length; ++i)
            arr[i] = random.nextInt(100);

        insertSort(arr, new Comparator<Integer>() {
            @Override
            public int compare(Integer a, Integer b) {
                return Integer.compare(a, b);
            }
        });
        out.println(Arrays.toString(arr));
        //随进产生100000个元素,三种排序算法的运行时间为
        out.println("shell sort : " + time("shellSort") + "s");
        out.println("insert sort : " + time("insertSort") + "s");
        out.println("bubble sort: " + time("bubbleSort") + "s");
    }
}

附上Java算法gitHub
https://github.com/liangpj/AlgorithmsJava.git

Reference:
[1] Wikipedia, Bubble Sort
[2]. 《算法导论》第三版 Thomas H.Cormen et al.
[3]. 数据结构–陈越、何钦铭

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值