研磨算法:排序之初级排序(选择 插入 冒泡)

研磨算法:排序之初级排序(选择 插入 冒泡)

标签(空格分隔): 研磨算法


排序算法是在基础面试中经考到的算法,也常常是我们解决问题的第一步。虽然在实际项目开发中很小几率会需要我们手动实现,但是这些思想是我们需要学习的。本文将会剖析三种最出击的排序算法:选择、插入、冒泡

选择排序

理解算法

选择排序是最简单直观的一种算法,之所以叫做选择排序,是因为不断地选择剩余元素中最小(大)的:

基本思想为每一趟从待排序的数据元素中选择最小(或最大)的一个元素作为首元素,直到所有元素排完为止。

在算法实现时,我们通过设置一个变量min,在排序的内循环中每一次比较仅存储较小元素的数组下标,当轮循环结束之后,那这个变量存储的就是当前最小元素的下标。此时再在外循环中执行交换操作即可。

代码示例

双层循环:

  • 外层循环: 对于N个数字,将最小的元素和当前元素交换
  • 内层循环: 在剩余元素中找出最小元素
public static void selectSort(int[] a) {
        int N = a.length;
        // 依次选择出N个数字
        for (int i = 0 ; i < N ; i++) {
            int min = i;    // 每一趟循环比较时,min用于存放较小元素的数组下标,这样当前批次比较完毕最终存放的就是此趟内最小的元素的下标

            // 找出最小的元素
            for (int j = i+1 ; j < N ; j++) {
                if (a[j] < a[min]) {
                    min = j;    // 最小元素的下标
                }
            }
            swap(a,i,min);
        }
        show(a);
    }

算法分析

总的来说,选择排序是一种容易理解和实现的简单排序算法,它有两个鲜明的特点:

  1. 运行时间与初始的顺序无关。因为上一次的扫描不能为下一次扫描提供信息。

  2. 数据移动最少,N次交换,与数组的大小是线性关系。

    扫描二维码关注公众号,回复: 1241312 查看本文章

对于选择排序,大约需要N^2/2次比较和N次交换。综合下来,时间复杂度为O(n2)

插入排序

理解算法

在整理手中的扑克牌时,我们通常没抓到一张,就将其插入到已经有序的手牌中适当的位置。这就是插入排序的基本思想:

每一步将一个待排序的记录,插入到前面已经排好序的有序序列中去,直到插完所有元素为止。

与选择排序一样,当前索引左边的所有元素都是有序的,但还不是最终的位置,再插入元素时,需要给插入的元素腾出空间,在数组中就是将后面的元素都右移。当索引达到数组的最右端时,排序结束。

代码演示

两层循环:

  • 外层循环: 从第二个元素开始遍历(相当于抓牌)
  • 内层循环: 将该元素和前面的元素比较,如果比前一个元素小,就交换。再和前一个元素比较……循环此过程,直到放到合适的位置
 public static void insertSort(int[] a) {
        int N = a.length;
        // 从第2个元素开始,相当于一个抓扑克牌的过程
        for (int i = 1 ; i < N ; i++) {
            // 对于当前的元素,如果比前一个元素小,就与前一个元素交换
            for (int j = i-1 ; a[j] > a[i] && j > 0 ; j--) {
                swap(a,i,j);
            }
        }
        System.out.println("insert sort");
    }

算法分析

插入排序在最好情况下,需要比较n-1次,无需交换元素,时间复杂度为O(n);在最坏情况下,时间复杂度依然为O(n2)。

与选择排序不同,插入排序所需的时间取决于输入中元素的初始顺序。对于部分有序的数组会很有效。也很适合小规模数组。

这些类型的数组会在十几种经常竖线,也是高级排序算法中的中间过程。所以在学习高级配需算法时会使用。

算法改进

在内循环中将较大的元素都向右移动而不是交换两个元素,这样能够使访问数组的次数减半:

public static void insertSort(int[] a) { 
    int N = a.length;  
    for(int i=1;i<N;i++){  
        int temp = a[i];  
        for(int j=i-1;j>=0 && a[j] > a[i]; j--){  
            a[j+1] = a[j];  
        }  
        a[j+1] = temp;  
    }  
}  

冒泡排序

理解算法

冒泡排序的基本思想是,对相邻的元素进行两两比较,顺序相反则进行交换,这样,每一趟会将最小或最大的元素“浮”到顶端,最终达到完全有序

代码演示

在冒泡排序的过程中,如果某一趟执行完毕,没有做任何一次交换操作,比如数组[5,4,1,2,3],执行了两次冒泡,也就是两次外循环之后,分别将5和4调整到最终位置[1,2,3,4,5]。此时,再执行第三次循环后,一次交换都没有做,这就说明剩下的序列已经是有序的,排序操作也就可以完成了,来看下代码 

public static void bubbleSort(int[] a) {
        int N = a.length;
        for (int i = 0; i < N; i++) {
            //设定一个标记,若为true,则表示此次循环没有进行交换,也就是待排序列已经有序,排序已然完成。
            boolean flag = true;
            for (int j = 0; j < N - i; j++) {
                if (a[j] > a[j + 1]) {
                    swap(a,j,j+1);
                    flag = false;
                }
            }
            if (flag) {
                break;
            }
        }
    }

算法分析

根据上面这种冒泡实现,若原数组本身就是有序的(这是最好情况),仅需n-1次比较就可完成;若是倒序,比较次数为 n-1+n-2+…+1=n(n-1)/2,交换次数和比较次数等值。所以,其时间复杂度依然为O(n2)。

猜你喜欢

转载自blog.csdn.net/japson_iot/article/details/80467468