老生常谈——选择排序和插入排序

本文介绍了两种基本的排序方法:选择排序和插入排序,并通过玩扑克牌的例子形象地解释了这两种算法的工作原理。同时,文章提供了两种算法的具体实现代码,并探讨了如何优化这些基本算法。

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

所谓的排序就是将一组对象按照某种逻辑顺序重新排列的过程.在研究排序算法时,我们需要考虑排序的成本:

需要计算比较和交换的次数.对于不交换元素的算法需要计算访问数组的次数.

我们每个人都有玩扑克牌的过程,如果你细心观察会发现在抽牌的过程中涉及到了两种最为简单和常见的排序算法:选择排序和插入排序:一次给你5张不同的牌,你会采用选择排序的思想使其有序,当你排序完这5张牌之后,如果再给你一张牌,此时你会采取插入排序的思想,将这张牌插入到合适的位置.


选择排序

选择排序是最简单,最基础的排序算法,通常是我们每个人”天生就会”的.其流程为:首先找到数组中最小的那个元素,其次将它和数组的第一个元素交换位置(如果第一个元素就是最小的元素那么他就和自己交换).再次,在剩余的元素中需要最小的元素,叫它与数组的第二个元素交换位置.如果往复,直到整个数组排序.选择排序的核心在于:不断的选择剩余元素中的最小者.

public class SelectSort {
    public static void main(String[] args) {
        int[] arr = {1, 4, 2, 5, 4, 63, 66, 32};
        sort(arr);
        for (int i : arr) {
            System.out.print(i + " ");
        }
    }


    public static void sort(int[] arr) {
        int n = arr.length;
        for (int i = 0; i < n; i++) {
            //这里我们不写for(int j=i;i<n;j++)的原因在于,一个数没有必要和自身进行比较
            for (int j = i + 1; j < n; j++) {
                //每找到一个比arr[i]小的数就进行一次交换,整个过程可能进行多次交换
                if (arr[j] < arr[i]) {
                    swap(arr, i, j);
                }
            }
        }
    }

    private static void swap(int[] arr, int i, int j) {
        int temp =arr[i];
        arr[i] = arr[j];
        arr[j]=temp;
    }
}

上面的算法是每个人都信手拈来的,但上面的算法还存在可改进的地方:在内循环中执行了多次交换.而实际上,我们只需要一次交换就够了:找到最小元素所在的位置min,然后交换min和i两处的元素即可.改进后的算法如下:

  public static void sort(int[] arr) {
        int n = arr.length;
        for (int i = 0; i < n; i++) {
            int min=i;//最小元素的位置
            for (int j = i + 1; j < n; j++) {
                if (arr[j] < arr[min]){
                    min=j;
                }
            }
            swap(arr,i,min);
        }
    }


插入排序

插入排序的基本思想是:将一个待排序的记录,按照其关键字的大小将其插入到前边已经排好序的子序列的适当的位置,直到全部插入完毕.这就像我们在玩扑克牌时,将每一张拍插入到其他已经有序的牌中适当的位置.在计算及中,为了给要插入的元素腾出位置,需要将其余所有元素在插入位置之前都向右移动一位.

public class InsertSort {
    public static void main(String[] args) {
        int[] arr = {4, 5, 2};
        sort(arr);
        for (int i : arr) {
            System.out.print(i + " ");
        }

    }


    public static void sort(int[] arr) {
        int n = arr.length;
        int i, j, k;
        for (i = 1; i < n; i++) {
            //在[0...i-1]区间内查找元素arr[i]待插入的位置
            for (j = i - 1; j >= 0; j--) {
                if (arr[j] < arr[i]) {
                    break;
                }
            }
            //如果找了待插入的位置
            if (j != i - 1) {
                int temp = arr[i];
                //比a[i]大的数据往后移动
                for (k = i - 1; k > j; k--) {
                    arr[k + 1] = arr[k];
                }
                //将a[i]放在待插入的位置
                arr[k + 1] = temp;
            }

        }

    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

以上代码是最基础的实现方式,首先查找待插入的位置,然后进行数据后移操作.现在我们来考虑能不能将这两个操作合二为一呢?先来思考一下我们刚才的思路,比如当前序列是
2 , 4 , 3
现在我们想要将元素3插入到已经有序的序列2 ,4中,按照上文的思路,其过程如下图:
这里写图片描述
如果我们换个角度,逆向思考之后,却可以这么做:
这里写图片描述
因此上述的代码可以被改写为:


    public static void sort(int[] arr) {
        int n = arr.length;

        for (int i = 1; i < n; i++) {
            for (int j = i; j > 0; j--) {
                //从后往前搜索,在数据交换的时候实现了数据的后移操作
                 if (arr[j] < arr[j - 1]) {
                    swap(arr, j, j - 1);
                }
            }
        }

    }

现在我们来抽象一下逆向思考的过程:如果arr[j]>arr[j-1],由于arr[0…j-1]已经是有序的,因此arr[0…j]也是有序的.否则,从j开始,从后往前,依次比较两个相邻的元素,如果arr[j]<arr[j-1]则交换两元素,然后继续往前比较调整,直到[0…j]区间内元素有序.


总结

选择排序和插入排序是两种基本的排序方法,和我们的生活紧密相关.适当的逆向思考将会帮助你更好的寻找解决问题的方法.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值