常见排序算法(1):冒泡排序、选择排序、插入排序、希尔排序、快速排序

选择排序(Selection Sort)

1.什么是选择排序?


  • 选择排序算法是一种简单的排序算法,它将待排序的序列分为两个部分:已排序和未排序。其中,已排序的部分是在算法的执行过程中逐渐增加的,而未排序的部分则逐渐减少。选择排序的核心思想是在未排序部分中找到最小(或最大)元素,然后将其放到已排序的末尾。由此,选择排序的时间复杂度为 O ( n 2 ) 。 O(n^2)。 O(n2)

在这里插入图片描述

选择排序 解析
外循环 依次将内循环找到的最小值放到已排序的位置,执行n-1次,取决于元素的数量
内循环 找到未排序中的最小值,执行n-i-1次,取决于未排序元素的数量

2.代码实现


for (i = 0; i < n-1; i++) {
    
    
        int min = i;//记录最小值的下标
        for (j = i+1; j < n; j++) {
    
    
            if (nums[j] < nums[min]) {
    
    
                min = j;
            }
        }
        int temp = nums[i];
        nums[i] = nums[min];
        nums[min] = temp;
    }

冒泡排序(Bubble Sort)

1.什么是冒泡排序?


  • 冒泡排序是一种简单的排序算法,它基于元素比较并交换。它通过迭代数组,对相邻元素进行比较,如果它们的顺序不正确(即是否递增或递减),就交换它们的位置。在每次迭代中,最大或最小的元素“冒泡”到数组的末端(内循环)。这个过程一直重复进行(外循环),直到排序完成。时间复杂度为 O ( n 2 ) O(n^2) O(n2)

在这里插入图片描述

冒泡排序 解析
外循环 重复执行内循环操作,每次执行至少有相邻的元素交换顺序,使本次内循环最值移到数组最右端,至少执行n-1次,取决于元素的数量
内循环 内层循环从未排序元素迭代,如果相邻元素不是指定顺序,则交换它们。随着每次迭代,最大的元素会“冒泡”到数组的末端。执行n-i-1次,取决于未排序元素的数量

2.代码实现


int i, j;
    for (i = 0; i < n-1; i++) {
    
    
        for (j = 0; j < n-i-1; j++) {
    
    
            if (arr[j] > arr[j+1]) {
    
    
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }

插入排序(Insertion Sort)

1.什么是插入排序?


  • 插入排序的思想是将待排序元素逐个插入已排序序列的合适位置,从而构成一个新的有序序列。该算法是一种简单的排序算法,虽然在最坏情况下的时间复杂度为 O ( n 2 ) O(n^2) O(n2),但在插入元素的数量少或者待排序序列基本有序的情况下,其时间复杂度会近似为0(n)。插入排序是稳定的,也就是说对于值相同的元素,排序前后它们的相对位置不会发生改变。
    在这里插入图片描述
插入排序 解析
外循环 重复执行内循环操作,每次执行将未排序的一个元素按顺序放置已排序序列中,至少执行n-1次,即第一个元素可事先视为已排序,取决于元素的数量
内循环 内层循环从未排序的元素中选中元素,依次与已排序的序列的元素比较,若符合顺序(递增或递减),则交换它们。若外循环已执行i次,则本次内循环至多执行i次,至少执行1次

2.代码实现

int i, j, key;
    for (i = 1; i < n; i++) {
    
    
        key = arr[i];
        j = i - 1;
        
        //将元素逐个移动,直到找到合适的位置
        while (j >= 0 && arr[j] > key) {
    
    
            arr[j + 1] = arr[j];
            j -= 1;
        }
        
        //插入指定位置
        arr[j + 1] = key;
    }

希尔排序

什么是希尔排序?


  • 希尔排序是插入排序的一种优化版本,它的主要思想是将待排序的数组分成若干个子序列进行插入排序,然后再对整个序列进行插入排序。实现过程中,需要预先设定一个增量序列(间隔),不同的增量序列会对排序效果产生不一样的影响。

  • 具体实现时,可以使用一个外层循环来控制增量序列的选择,一个内层循环来对每组子序列进行插入排序。每次循环,我们将数组中的数据按照指定增量分组,然后对每组数据进行插入排序,直至将所有数据排序完毕。

  • 在这里插入图片描述

  • 为什么不直接对待排序数组进行插入排序?,当元素基数较少时,快速排序的时间复杂度为 O ( n 2 ) O(n^2) O(n2),但当元素较多时,其时间复杂度接近 O ( n 2 3 ) O(n^{2\over3}) O(n32),所以希尔排序是一个不稳定的排序方法,下面是个例子可做参考:

对长度10000的数组排序

插入排序 希尔排序
比较次数 25267717 295559
交换次数 25257717 165559

所以对于较多元素的数组排序,相比于插入排序,我们可以选择希尔排序。

  • 解析
希尔排序 解析
外循环 重复定义间隔gap,每次间隔为上次的 1 2 1\over2 21
内循环 内层循环从下标gap开始进行插入排序,每次比较与对应组内元素比较,如选定元素下标为i,则将其与下标i-gap的元素比较。

代码实现
在这里插入图片描述

for (gap = n / 2; gap > 0; gap /= 2) {
    
    
    for (i = gap; i < n; i++) {
    
    
        key = arr[i];
        j = i - gap;
        while (j >= 0 && arr[j] > key) {
    
    
            arr[j + gap] = arr[j];
            j -= gap;
        }
        arr[j + gap] = key;
    }
}

快速排序(Quick Sort)

1.什么是快速排序?


  • 快速排序是一种高效的排序算法,它的基本思想是通过分治的策略将原问题分解成若干子问题,再将子问题的解进行合并以得到原问题的解。其时间复杂度为 O ( n l o g n ) O(n_{log}n) O(nlogn)~ O ( n 2 ) O(n^2) O(n2)

  • 分治思想:

原问题
一级子问题
一级子问题
二级子问题
二级子问题
二级子问题
二级子问题
  • 递归思想:
    每个子问题在将解归并前,都将进行快速排序,调用快速排序的函数的动作一致,因此可改变给定条件递归函数。

  • 快速排序步骤:

  1. 选择基数
    在快速排序中,我们首先要选择一个基数。一般选择待排序数组中的第一个元素,也可以按照一定的规则选择。选择主元的目的是将原数组分成两部分,一部分比基数小,另一部分比基数大。
  2. 划分数组(核心)
    选择主元后,将数组划分成两个子序列。这时需要使用两个指针,一个指向待排序数组的起始位置,另一个指向数组的结束位置。从前往后扫描数组,如果某个元素比主元小,就将其交换到数组的左侧;否则,就将该元素交换到数组的右侧。这样,我们就得到了两个子序列:一个是比主元小的,一个是比主元大的。
  3. 递归调用
    接下来,将分别对两个子序列进行递归调用。也就是按照快速排序的方法对子序列进行排序,直到子序列的长度为1时终止递归(递归终止条件)。
    在这里插入图片描述
    2.代码实现
void quick_sort(int arr[], int left, int right) {
    
    
    if (left >= right) {
    
      // 如果序列长度小于等于1,则无需排序
        return;
    }
    int i = left;   // 定义左右指针
    int j = right;
    int pivot = arr[left];   // 定义基数,选择左端点元素
    while (i < j) {
    
       // 划分数组
        while (i < j && arr[j] >= pivot) {
    
    
            j--;
        }
        arr[i] = arr[j];//交换
        while (i < j && arr[i] <= pivot) {
    
    
            i++;
        }
        arr[j] = arr[i];
    }
    arr[i] = pivot;    // 将基数放在坑位
    quick_sort(arr, left, i - 1);   // 递归排序左边子序列
    quick_sort(arr, i + 1, right);  // 递归排序右边子序列
}

猜你喜欢

转载自blog.csdn.net/m0_73589720/article/details/129472741