复杂度为O(N^2)的排序算法


前言

本章节主要讲解的是时间复杂度为O(n^2)的排序算法以及优化,帮助大家从动图等多个角度理解排序算法。


一、冒泡排序

1、时间复杂度:O(n^2)

2、排序逻辑:

我们以升序为例,整体的实现逻辑就是每次比较出一个最大值,将其放到右面,第二次找到一个第二大的值放到右面,以此类推,实现排序。

img

3、代码实现:

相邻数字进行比较,满足条件则交换,最终将极大值或极小值放到右面。

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

4、算法优化:

我们发现,在上面的代码中,无论这个数组是否在中途排好顺序,或者假设一种极端情况,我们传入的就是一个有序数组,逻辑上此时是不用排序的。但是我们刚才实现的冒泡排序完全无视了这样一种灵活的状态,只会机械地重复固定次数。

那么我们如何优化呢?

我们发现,如果一个数组已经有序了,那么不会发生任何一次交换,此时就说明已经有序了。那么我们只需要判断在某轮比较中是否发生过交换,只要没交换那么这个数列就已经有序了。

void bubble_sort(int* arr, int len)
{
    for (int i = 0; i < len - 1; i++)
    {
        int is_swap = 0;
        for (int j = 0; j < len - i - 1; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
        if (is_swap == 0)
        {
            return;
        }
    }
} 

二、选择排序

1、时间复杂度:O(n^2)

2、排序逻辑:

选择排序其实和冒泡排序非常的相似,同样以升序序列为例,选择排序则是将最小值放到最右端。以此类推,完成排序。

img

3、代码实现:

void select_sort(int* arr, int len)
{
    for (int i = 0; i < len; i++)
    {
        int k = i;
        for (int j = i + 1; j < len; j++)
        {
            if (arr[j] < arr[k])
            {
                k = j;
            }
        }
        int temp = arr[k];
        arr[k] = arr[i];
        arr[i] = temp;
    }
}

4、算法优化

我们发现选择排序的时候基本上会遍历每个未排序的元素。也就是说,在这个过程中我们不仅仅能选出最小值,我们还能够选出最大值。所以我们不妨在每一轮遍历过程中将两种极值都找到,其中最小的放左面,最大的放右面。

void select_sort(int* arr, int len)
{
    int left = 0;
    int right = len - 1;
    while (left < right)
    {
        int k_min = left;
        int k_max = left;
        for (int j = left; j <= right; j++)
        {
            if (arr[j] < arr[k_min])
            {
                k_min = j;
            }
            if (arr[j] > arr[k_max])
            {
                k_max = j;
            }
        }
        swap(arr + k_min, arr + left);
        
        /*排除一种特殊情况,即当最大值是最左侧数字的时候,因为如果是最大值是最左侧的数字,那么在最大值交换之前,这个位置已将被换  			成了该轮的最小值,这样就会导致我们这一轮的最小值又与最大值发生了交换。所以我们应该再次记录交换后的最大值位置,再发生交			换。*/
        
        if (k_max == left)
        {
            k_max = k_min;
        }
        
        swap(arr + k_max, arr + right);
        left++;
        right--;

    }
}

三、插入排序

1、时间复杂度:O(n^2)

2、排序逻辑:

img

与前面两种排序方式相比,在一个数列基本有序的情况下,插入排序可以大大减少比较的次数。

3、代码实现:

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

        }
    }
}

4、代码优化(希尔排序)

我们发现插入排序每次的跨度只是针对左右相邻的数据,当数据量很大的时候,插入排序就有点慢了。那么下面的希尔排序则是对插入排序进行了一定的优化。

四、希尔排序

1、时间复杂度:O(n^2)

2、排序逻辑:

img

优化版的插入排序,优化的地方在于,插入排序是移动一步,和前面的做对比。而希尔排序一步可以跨度很大,因此当一个数组非常大的时候,希尔排序大大增加了效率。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x0ZAvUVT-1666019685228)(E:\typroa's photos\image-20221017211724157.png)]

希尔排序就是将一组数字,分成若干小的数组,再分别进行插入排序,这样就能够大大的减少每次移动的步数。

3、代码实现:

void shell_sort(int* arr, int len)
{
    int step = len / 2;
    while (step >= 1)
    {
        for (int i = step; i < len; i++)
        {
            for (int j = i; j >= step; j -= step)
            {
                if (arr[j] < arr[j - step])
                {
                    int temp = arr[j];
                    arr[j] = arr[j - step];
                    arr[j - step] = temp;
                }
            }
        }
        step /= 2;
    }
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值