6种经典排序算法总结(冒泡,选择,插入,归并,快排,堆排)

本文探讨了学习复杂度为O(N^2)排序算法的原因,介绍了冒泡、选择、插入排序的思想及复杂度,还给出改进冒泡排序和冒泡排序衍生思路的解法。同时阐述了复杂度为O(NlogN)的归并、快速、堆排序的原理,以及快速排序在特殊情况的优化和堆排序的适用场景。

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

为什么要学习复杂度为O(N^2)的排序算法?

1.基础,编码简单,易于实现,是一些简单情景的首选

2.在一些特殊情况下,简单的排序算法更有效(插入)

3.简单排序算法的思想衍生出的其他排序方式

4.作为子过程,改进更复杂的排序算法

 

冒泡排序

思想:每次迭代通过比较相邻两个元素的大小,进行元素位置的调整,直到所有元素全部排好,代码如下

void bubbleSort (int *arr, int len) {
    int i, j;
    for (i=0; i<len-1; i++)//控制排序趟数
        for (j=0; j<len-1-i; j++) {//控制每趟排序比较多少次
            if (arr[j] > arr[j+1]) {
                swap(arr[j],arr[j+1]);
            }
        }
}

复杂度分析:第一次迭代需要比较的次数为N次,第二次迭代为N-1次,依次类推,复杂度为N+(N-1)+(N-2)+……+1=N(N-1)/2=O(N^2)

改进后的冒泡排序,复杂度为O(N):

我们在原本的冒泡排序中加入一个布尔变量,发生一次交换则置为true,这样如果数组变得有序(经过某次循环后布尔变量依然为false),就可以提前终止循环。代码如下:

void bubbleSort (int arr[], int len) {
    bool didswap;
    for (i=0;i<len-1; i++){ 
        for (j=0;j<len-1-i; j++) { 
            if (arr[j] > arr[j+1]) {
                swap(arr[j],arr[j+1]);
                didswap=true;
            }
        }
        if(didswap==flase){
            return;
        }
    }
}

冒泡排序衍生出的排序思路:

如某公司面试题:

[1,2,3,-2,-4,5,3,-2,4,1,-5,3]数组排序

要求:1.正数在左,负数在右 2.相对顺序不变 3.空间复杂度为O(1)

解题思路:采用双指针的策略会改变元素的相对顺序,利用冒泡排序的思想,比较前后两个元素的正负,若负数在前,正数在后,则交换两个元素。

代码如下:

void bubbleSort (int arr[], int len) {
    for (i=0;i<len-1; i++){ 
        for (j=0;j<len-1-i; j++) { 
            if (arr[j]<0 && arr[j+1]>0) {
                swap(arr[j],arr[j+1]);
            }
        }
    }
}

 

选择排序

思想:每次迭代从剩余元素中选择最大(小)的一个,放在数组的末尾(头部)。

void selectSort (int arr[], int len) {
    for (i=0;i<len; i++){ 
        int minIndex=i;
        for (int j=i+1;j<n;j++) { 
            if (arr[j]<arr[minIndex]) {
                minIndex=j;
            }
        }
        swap(arr[i],arr[minIndex]);
    }
}

插入排序

类比打牌时的排牌

每步将一个待排序的记录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。

void insertionSort (int arr[], int len) {
    for (i=0;i<len; i++){ 
        int tmp=arr[i];
        int j;
        for (int j=i;j>0 && arr[j-1]>tmp;j--) { 
            arr[j]=arr[j-1];
        }
        arr[j]=tmp;
    }
}

重要性质:当数组近乎有序的时候,可以提前终止内层循环,具有非常快的排序速度,复杂度为O(N)

复杂度为O(NlogN)的排序算法

归并排序

思想:分治,将数据化成logN层级,每一层用O(N)的复杂度去解决

第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置

第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置

重复步骤3直到某一指针超出序列尾,将另一序列剩下的所有元素直接复制到合并序列尾

代码如下:

void mergeSort (int arr[], int l,int r) {
    if(l>=r){
        return;
    }
    int mid=l+(r-1)/2;
    mergeSort(arr,l,mid);
    mergeSort(arr,mid+1,r);
    merge(arr,l,mid,r);
}
void merge(int arr[],int l,int mid,int r){
    int aux[r-l+1];
    for(int i=l;i<=r;i++){
        aux[i-l]=arr[i];
    }
    int i=l,j=mid+1;
    for(int k=l;k<=r;k++){
        if(i>mid){
            arr[k]=aux[j-l];j++;
        }
        else if(j>r){
            arr[k]=aux[i-l];i++;
        }
        else if(aux[i-l]<aux[j-l]){
            arr[k]=aux[i-l];i++;
        }
        else{
            arr[k]=aux[j-l];j++;
        }
    }
}

快速排序

思想:

1.先从数列中取出一个数作为基准

2.分区,该数左边的都比该数小,右边的都比该数大

3.再对左右区间递归第二步

代码如下:

void quickSort(int arr[],int l,int r){
    swap(arr[l],arr[rand()%(r-l+1)+l]);//将数组第一个元素与数组中随机一个元素交换,目的是避免数组有序导致的极端情况
    int lt=l;
    int gt=r+1;
    int i=l+1;
    while(i<gt){
        if(arr[i]<arr[l]){
            swap(arr[i],arr[lt+1]);
            lt++;
            i++;
        }
        else if(arr[i]>arr[l]){
            swap(arr[i],arr[gt-1]);
            gt--;
        }
        else{
            i++;
        }
    }
    swap(arr[l],arr[lt+1]);
    quickSort(arr,l,lt-1);
    quickSort(arr,gt,r);
}

当数组为近乎有序的时候,快排的时间复杂度降为O(N^2),因为当数组有序的时候会执行N次分区操作。

解决方法:在排序之前交换标定值和随机一个数的位置

堆排序

思想:首先建立一个堆,然后不断地比较非叶子节点和堆顶元素的大小,同时调整堆,完成对元素的排序。

堆排序主要用于动态数据的维护,如在1000000个元素中选出前100名这种N个元素中选出前M个元素的问题,用堆排序最好解决,其复杂度为NlogM

PS:建堆的复杂度为O(N)

代码如下:

void heap_adjust(vector<int>& num, int i, int n)
{
 int j = i * 2 + 1;
 while (j < n)
 {
  if (j + 1 < n && num[j] < num[j + 1])
  {
   j++;
  }
  if (num[i] > num[j])//已符合父节点大于子节点,无需交换,跳出循环
   break;
  swap(num[i], num[j]);
  i = j;//不符合,交换后,shiftdown向下调整
  j = i * 2 + 1;
 }
}

void heap_sort(vector<int>&num, int n)
{
 int i;
 for (int i = n / 2-1; i >= 0; i--)//对每一个非叶结点进行堆调整(从最后一个非叶结点开始),建堆的过程
 {
  heap_adjust(num, i, n);
 }
 for ( i = n-1; i >= 0; i--)
 {
  swap(num[0], num[i]);// 将当前最大的放置到数组末尾
  heap_adjust(num, 0, i);// 将未完成排序的部分继续进行堆排序
 }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值