探索经典排序算法:冒泡、选择、插入、归并、快速排序

探索经典排序算法:冒泡、选择、插入、归并、快速排序

排序算法是计算机科学中的一个重要领域,它们用于将一组元素按照特定的顺序排列。在这篇博客中,我们将探讨五种经典的排序算法:冒泡排序、选择排序、插入排序、归并排序和快速排序。每种算法都有其独特的思想和实现方式,让我们一一了解它们。

冒泡排序

public Integer[] sortAsc(Integer[] arrNum) {
    for (int i = 1; i < arrNum.length ; i++) {
        boolean flag = false;
        for (int j = 0; j < arrNum.length - 1; j++) {
            if (arrNum[j] > arrNum[j+1]){
                int temp = arrNum[j];
                arrNum[j] = arrNum[j+1];
                arrNum[j+1] = temp;
                flag = true;
            }
        }
        if (!flag) {
            break;
        }
    }
    return arrNum;
}

冒泡排序(Bubble Sort)是一种简单的比较排序算法,其基本思想可以概括为以下步骤:

  1. 比较相邻元素:从第一个元素开始,比较相邻的两个元素,将较大(或较小)的元素交换到右侧,使较大的元素向右“冒泡”。

  2. 重复步骤1:重复上述比较和交换的过程,直到遍历整个数组。这一轮比较会将最大(或最小)的元素移动到数组的末尾。

  3. 重复步骤1和2:重复执行步骤1和2,每次迭代都会将一个更大(或更小)的元素放到已排序部分的末尾,同时减少未排序部分的长度。

  4. 完成排序:当一轮比较中没有进行任何交换操作时,说明整个数组已经有序,排序完成。

冒泡排序的名称来源于元素通过不断交换向上或向下“冒泡”的过程。它属于基本的排序算法,虽然简单,但在实际应用中通常不是最优的选择,因为其平均时间复杂度为O(n^2),其中n是数组的长度。对于较大的数据集,冒泡排序的性能相对较差。

尽管如此,冒泡排序仍然有教育和理解排序算法的价值,因为它易于理解和实现,并且在某些特定情况下可能是一个合适的选择。但在实际应用中,通常会选择更高效的排序算法,如快速排序、归并排序或堆排序,以处理大规模数据集。

选择排序

public Integer[] sortAsc(Integer[] arrNums) {
    for (int i = arrNums.length-1; i > 0; --i) {
        int k = 0;
        for (int j = 1; j <= i; j++) {
            if (arrNums[j] > arrNums[k]) {
                k = j;
            }
        }
        int  temp = arrNums[i];
        arrNums[i] =  arrNums[k];
        arrNums[k] = temp;
    }
    return arrNums;
}

选择排序(Selection Sort)是一种简单的比较排序算法,其基本思想可以概括为以下步骤:

  1. 分为已排序部分和未排序部分:算法维护两个子数组,一个是已排序的部分,另一个是未排序的部分。一开始,已排序部分为空,整个数组都属于未排序部分。

  2. 寻找最小元素:从未排序部分中寻找最小的元素。

  3. 将最小元素移到已排序部分:将找到的最小元素与未排序部分的第一个元素进行交换,将最小元素放入已排序部分的末尾。

  4. 重复:重复上述步骤,直到整个数组都被排序。每一次迭代都会将一个最小元素添加到已排序部分。

  5. 完成排序:当未排序部分为空时,整个数组就已经排好序了。

选择排序的关键思想是在未排序部分中找到最小(或最大)的元素,然后将其交换到已排序部分。这个过程不断迭代,每次迭代都会确定一个最小元素的位置,直到整个数组排序完成。

选择排序的优点是实现简单,代码短小,适用于小型数据集。然而,它的时间复杂度是O(n^2),因此对于大型数据集来说效率较低。在实际应用中,通常会选择更高效的排序算法,如快速排序或归并排序,来处理大规模数据集。

插入排序

public Integer[] sortAsc(Integer[] arrNums) {
    for (int i = 1; i < arrNums.length; i++) {
        for (int j = i; j > 0; j--) {
            if (arrNums[j] < arrNums[j-1]){
                int temp = arrNums[j];
                arrNums[j] = arrNums[j-1];
                arrNums[j-1] = temp;
            }
        }
    }
    return arrNums;
}

插入排序(Insertion Sort)是一种简单但有效的比较排序算法,其基本思想可以概括为以下步骤:

  1. 分为已排序部分和未排序部分:算法开始时,将第一个元素视为已排序部分,其余元素视为未排序部分。

  2. 逐个将未排序部分的元素插入已排序部分:从未排序部分取出一个元素,将其插入已排序部分的合适位置,使得已排序部分仍然保持有序。这涉及到比较和移动元素的操作。

  3. 重复步骤2:重复上述插入操作,直到未排序部分为空,整个数组就被排序完成。

插入排序的核心思想是将未排序部分的元素逐个插入已排序部分的合适位置,以构建有序序列。在每次插入操作中,当前元素与已排序部分的元素进行比较,然后根据比较结果找到插入的位置。这个过程类似于打扑克牌时按顺序将一张牌插入到已排序的牌堆中。

插入排序是一个稳定的排序算法,它的时间复杂度取决于输入数据的初始顺序。在最坏情况下(逆序输入),插入排序的时间复杂度为O(n^2),其中n是数组的长度。然而,在实际应用中,当输入数据接近有序时,插入排序表现出色,时间复杂度接近O(n)。

尽管插入排序不如一些高级排序算法(如快速排序或归并排序)的性能好,但由于其简单性和稳定性,它在某些特定场景下仍然有用,特别是对于小型数据集或部分有序的数据。

归并排序

public Integer[] sortAsc(Integer[] arrNums) {
    sortPart(arrNums, 0, arrNums.length -1);
    return arrNums;
}
​
private void sortPart(Integer[] arrNums, int left, int right) {
    if (left >= right) {
        return;
    }
    int middle = (left +right) / 2;
    sortPart(arrNums,  left, middle);
    sortPart(arrNums,  middle+1, right);
    merge(arrNums,  left, right, middle);
}
​
private void merge(Integer[] arrNums, int left, int right, int middle) {
    int l = left, r = middle + 1, start = 0;
    Integer[] resultArr = new Integer[right-left+1];
    while (l <= middle && r <= right) {
        if (arrNums[l] < arrNums[r]) {
            resultArr[start] = arrNums[l++];
        } else {
            resultArr[start] = arrNums[r++];
        }
        start++;
    }
    while (l <= middle) {
        resultArr[start] = arrNums[l++];
        start++;
    }
    while (r <= right) {
        resultArr[start] = arrNums[r++];
        start++;
    }
    for (int v : resultArr) {
        arrNums[left++] = v;
    }
}

归并排序(Merge Sort)是一种高效的比较排序算法,其基本思想可以概括为以下步骤:

  1. 分治策略:将待排序的数组分成两个大致相等的子数组,这个过程一直持续到子数组的长度为1或0。也就是将问题分解成一系列更小的子问题。

  2. 递归排序:对每个子数组递归地应用归并排序算法,直到子数组长度为1或0。这是递归的基本情况。

  3. 合并操作:将已排序的子数组合并为一个更大的已排序数组。这个步骤是归并排序的核心操作,它要求将两个已排序的子数组合并成一个新的已排序数组。

  4. 重复合并:重复合并操作,将子数组合并成更大的子数组,直到最终合并成一个完全排序的数组。

关键思想:

  • 归并排序的关键思想是将一个大问题拆分成更小的子问题,然后将这些子问题解决并合并以获得最终的解决方案。

  • 合并操作是归并排序的核心,它要求两个已排序的子数组合并成一个已排序的数组。这是通过比较两个子数组中的元素,并按顺序将它们合并到一个新数组中来完成的。

  • 归并排序是一个稳定的排序算法,它的时间复杂度为O(n log n),其中n是数组的长度。这使得归并排序在处理大规模数据集时表现出色。

归并排序的主要优点是稳定性和稳定的时间复杂度,使得它在实际应用中广泛使用。虽然归并排序需要额外的内存空间来存储子数组和合并操作的结果,但其性能和可靠性使其成为一种常见的排序算法选择

快速排序

public Integer[] sortAsc(Integer[] arrNums) {
    sortPart(arrNums, 0, arrNums.length-1);
    return arrNums;
}
​
private void sortPart(Integer[] arrNums, int start, int end) {
    if (start >= end) {
        return;
    }
    int middle = sort(arrNums, start, end);
    sortPart(arrNums, start, middle-1);
    sortPart(arrNums, middle+1, end);
}
​
private int sort(Integer[] arrNums, int start, int end) {
    int consult = arrNums[start];
    int md = start;
    for (int i = start+1; i <= end; i++) {
        if (arrNums[i] < consult) {
            arrNums[md++] = arrNums[i];
            arrNums[i] = arrNums[md];
            arrNums[md] = consult;
        }
    }
    return md;
}

快速排序(Quick Sort)是一种高效的比较排序算法,其基本思想可以概括为以下步骤:

  1. 选择一个基准元素:从待排序的数组中选择一个元素作为基准元素。通常选择数组的第一个元素、最后一个元素或中间元素作为基准元素。

  2. 分区(Partition):重新排列数组,将比基准元素小的元素放在基准元素的左边,将比基准元素大的元素放在基准元素的右边。基准元素的位置被确定,这个过程被称为分区。

  3. 递归排序:对分区后的左子数组和右子数组分别递归地应用快速排序算法。重复这个过程直到子数组的大小为1或0,因为这些子数组已经有序。

  4. 合并:不需要显式合并操作,因为在分区过程中已经完成了排序。

关键思想:

  • 快速排序的关键思想是分治策略。它将大问题拆分成更小的子问题,然后递归地解决这些子问题。分区操作是关键,它将数组划分为两个子数组,一个包含较小的元素,另一个包含较大的元素。

  • 快速排序是一个原地排序算法,它不需要额外的内存空间来存储子数组或中间结果。

  • 选择合适的基准元素是快速排序性能的关键。不同的基准元素选择策略可以影响快速排序的性能。通常情况下,选择中间元素作为基准元素是一种常见的策略。

  • 快速排序的平均时间复杂度为O(n log n),其中n是数组的长度。在大多数情况下,它是一种非常高效的排序算法,但在最坏情况下,时间复杂度为O(n^2)。为了减少最坏情况的发生,可以采用随机选择基准元素或三数取中法等策略。

快速排序是一种常用的排序算法,广泛应用于各种应用场景中,因其高效性和原地排序的特性而受欢迎。虽然它在最坏情况下的性能不如归并排序等算法,但在实践中通常表现良好。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值