排序算法笔记 - 简单排序

目录

1. 冒泡排序

1.1 排序原理

1.2 AIP设计

1.3 代码实现

1.4 时间复杂度分析

2. 选择排序

2.1 排序原理

 2.2 API设计

2.3 代码实现

2.4 时间复杂度分析

3. 插入排序

3.1 排序原理

 3.2 API设计

3.3 代码实现

 3.4 时间复杂度分析


1. 冒泡排序

1.1 排序原理

1. 从第一个元素开始, 比较相邻的元素. 如果前一个元素比后一个元素大, 就交换这两个元素的位置
2. 对每一个对相邻的元素做同样的工作,  最终最后位置的元素就是最大值.
3. 重复如此, 知道整个队列完成排序

1.2 AIP设计

类名Bubble
构造方法Bubble()
成员方法1. public static void sort(Comparable[] a): 对数组内的元素进行排序
2. private static boolean greater(Comparable v, Comparable w): 判断v是否大于w
3. private static void exch(Comparable[] a, int i, int j) : 交换数组a中索引i和j处的值

1.3 代码实现

package sort;

public class Bubble {
    /*
    对数组a中的元素进行排序
     */
    public static void sort(Comparable[] a){
        for (int i = a.length - 1; i > 0; i--) {
            for(int j = 0; j < i; j++){
                // 比较j和j+1处元素的值, 如果j处的元素比j+1处的元素大, 则交换
                if(greater(a[j], a[j+1])){
                    exch(a, j, j + 1);
                }
            }
        }
    }

    /*
    判断元素v是否大于元素w
     */
    private static boolean greater(Comparable v, Comparable w){
        return v.compareTo(w) > 0;
    }

    /*
    交换数组a中, 索引i和j处的值
     */
    private static void exch(Comparable[] a, int i, int j){
        Comparable temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

1.4 时间复杂度分析

冒泡排序使用了双层的for循环, 其中内层循环体是真正完成排序的代码. 因此, 分析时间复杂度只需要分析内层循环的执行次数即可.
在最坏的情况下, 也即需要对一个降序数列进行排序, 那么, 元素比较的次数为:
(N-1) + (N-2) + (N-3) + ... + 2 + 1 = ((N-1) + 1) * (N-1)/2 = N^2/2 - N/2
元素交换的次数同样也为
(N-1) + (N-2) + (N-3) + ... + 2 + 1 = ((N-1) + 1) * (N-1)/2 = N^2/2 - N/2
因此, 总执行次数为
N^2 - N, 也即时间复杂度为O(N^2).

2. 选择排序

2.1 排序原理

1. 每次遍历的过程中, 都假定第一个索引处的元素是最小值, 和其他索引处的值一次进行比较. 如果当前索引处的值大于其他索引处的值, 则将最小值更新为另一个索引处的值, 直到遍历至队列末尾.
2. 交换第一个索引处和最小值所在的索引处的值.

 2.2 API设计

类名Selection
构造方法Selection()
成员方法1. public static void sort(Comparable[] a): 对数组内的元素进行排序
2. private static boolean greater(Comparable v, Comparable w): 判断v是否大于w
3. private static void exch(Comparable[] a, int i, int j) : 交换数组a中索引i和j处的值

2.3 代码实现

package sort;

public class Selection {
    /*
    对数组a中的元素进行排序
     */
    public static void sort(Comparable[] a){
        /*
        i指针指向每一次排序时队列中第一个元素
        for循环结束的条件是i < a.length - 1是因为只需要比较至倒数第二个元素, 
        因为最后一个元素只能是最大的
         */
        for (int i = 0; i < a.length - 1; i++) {
            // 首先假定本次遍历, 最小值的索引为i
            int minIndex = i;
            for(int j = i + 1; j < a.length; j++){
                // 如果j处的元素比最小索引处的值还要小, 则更新minIndex
                if (greater(a[minIndex], a[j])){
                    // 更新minIndex
                    minIndex = j;
                }
            }
            // 一次扫描结束, 将minIndex处的值和第一个未排序的值(i)进行交换
            exch(a, i, minIndex);
        }
    }

    /*
    判断元素v是否大于元素w
     */
    private static boolean greater(Comparable v, Comparable w){
        return v.compareTo(w) > 0;
    }

    /*
    交换数组a中, 索引i和j处的值
     */
    private static void exch(Comparable[] a, int i, int j){
        Comparable temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

2.4 时间复杂度分析

选择排序同样使用了一个双层for循环进行排序, 外层循环完成了数据交换, 内层循环则完成了数据比较, 所以我们分别统计数据交换和比较次数:
数据比较次:
(N-1) + (N-2) + (N-3) + ... + 2 + 1 = ((N-1) + 1) * (N-1)/2 = N^2/2 - N/2
数据交换此时:
N-1
因此, 其时间复杂度和冒泡排序相同, 也为O(N^2).

3. 插入排序

3.1 排序原理

1. 把所有的元素分为两组, 已经排序的和未排序的.
2. 找到未排序的组中的第一个元素, 向已经排序的组中进行插入
3. 倒序遍历已经排序的元素, 依次和待插入的元素进行比较, 直到找到一个元素小于等于待插入元素, 那么就把待插入的元素放到这个位置, 其他元素向后移动一位(实际上, 是将待插入的元素从已排序的元素的末尾依次进行比较, 若待插入的元素比比较的元素小, 则将这两个元素进行交换, 以此实现对其他元素的向后移动).
*如下图所示, 其中红色表示需要向后移动一位的元素, 绿色表示待插入的元素

 3.2 API设计

类名Insertion
构造方法Insertion()
成员方法1. public static void sort(Comparable[] a): 对数组内的元素进行排序
2. private static boolean greater(Comparable v, Comparable w): 判断v是否大于w
3. private static void exch(Comparable[] a, int i, int j) : 交换数组a中索引i和j处的值

3.3 代码实现

package sort;

public class Insertion {
    /*
    对a数组中的元素进行排序
     */
    public static void sort(Comparable[] a){
        /*
        i指针指向待插入的元素的初始位置
        i的初始值为1, 因为初始状态时, 默认第一个元素已经排好序,
        因此从第二个元素(i=1)开始进行比较
         */
        for (int i = 1; i < a.length; i++) {
            /*
            j指针指向的则是待插入元素变化后的实时位置
             */
            for (int j = i; j > 0; j--){
                // 如果待插入元素比前一个元素要小, 则进行交换
                if (greater(a[j-1], a[j])){
                    exch(a, j-1, j);
                }else {
                    // 如果待插入元素大于或等于前一个元素, 则说明找到位置
                    break;
                }
            }
        }
    }

    /*
    比较元素v是否大于元素w
     */
    private static boolean greater(Comparable v, Comparable w){
        return v.compareTo(w) > 0;
    }

    /*
    交换数组a中索引i, j处的值
     */
    private static void exch(Comparable[] a, int i, int j){
        Comparable temp;
        temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

 3.4 时间复杂度分析

插入排序使用双层for循环, 其中内层循环是真正完成排序的代码.
最坏情况下, 需要对一个降序数组进行排序, 那么
需要进行比较的次数:
(N-1) + (N-2) + (N-3) + ... + 2 + 1 = ((N-1) + 1) * (N-1)/2 = N^2/2 - N/2
交换的次数:
(N-1) + (N-2) + (N-3) + ... + 2 + 1 = ((N-1) + 1) * (N-1)/2 = N^2/2 - N/2
总的执行次数为N^2-N, 因此时间复杂度为O(N^2).

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值