手撕排序算法3:优化版冒泡排序和快速排序的三种方法(包括三数取中,小区间优化,递归版本)(上)

简介: 手撕排序算法3:优化版冒泡排序和快速排序的三种方法(包括三数取中,小区间优化,递归版本)(上)

一.冒泡排序

1.算法思想

冒泡排序,顾名思义,就是在每一趟中将最大的数沉到水底,也就是将最大的数移动到末尾位置,一共进行n-1次

冒泡排序的整体思想是:

1.左右两两比较,大的向后移动,小的向前移动

2.每一趟都会使当前所比较过的元素中最大的那个移动到最后位置

3.一共n-1趟即可,因为对于一个数组来说,如果已经确定好了n-1个数字,又因为一个数组只有n个数字,所以剩下的那一个数字的位置也就唯一确定了

2.代码实现

void BubbleSort(int* a, int n)
{
  int i = 0;
  for (i = 0; i < n - 1; i++)//一共冒泡n-1趟(i控制趟数)
  {
    int flag = 1;//假设有序
    int j = 0;
    for (j = 0; j < n - 1 - i; j++)//(j控制每一趟比较的次数)
      //第一趟比较n-1次,第二趟比较n-2次(因为第二趟就不需要比较最后一个数字了,因为最后一个数字已经是最大的了)....,归纳为n-1-i次
    {
      if (a[j] > a[j + 1])
      {
        Swap(&a[j], &a[j + 1]);
        flag = 0;//发生交换,改为无序
      }
    }
    if (flag == 1)//有序,所以无需再进行排序,直接break
    {
      break;
    }
  }
}

这是冒泡排序的一种优化版本,使用flag标志数组是否已经有序,如果有序,则跳出循环,排序成功.

3.冒泡排序的时间复杂度与稳定性

冒泡排序的时间复杂度是O(N^2),具有稳定性

至于为什么冒泡排序具有稳定性呢?

是因为冒泡排序在每一趟的比较过程中只有当左边元素大于右边元素时才会发生交换,而当左边元素等于右边元素时并不会发生交换,所以冒泡排序具有稳定性

4.冒泡排序,插入排序,选择排序的优劣介绍

插入排序比冒泡排序好,冒泡排序比选择排序好

前面提到过,当数组是有序或者部分有序时,插入排序有奇效(时间复杂度能达到O(N)!!!)

可是我们刚才进行的冒泡排序的优化也可以减少一些不必要的比较啊,那么它们两个到底谁更好呢?

下面给大家举个例子.

总结:优化版本冒泡排序

时间复杂度:O(N^2)

最好排序:O(N)

最差:O(N^2)

1.跟直接插入排序相比来说,直接插入更好

因为:

插入排序的任意性更强,对有序,接近有序,局部有序的适用性更强,所以说直接插入排序是在O(N^2)中的排序算法中非常好的一个算法了

2.很直接选择排序相比来说,冒泡排序更好,因为直接选择排序无论数组是否有序,都进行N^2次,而冒泡排序针对有序或部分有序有一些优化,避免了一些没有任何必要的比较

3.但是数组无序的情况下,冒泡排序还是效率比较差的

二.快速排序

1.算法思想

快速排序是一个非常强大的排序算法,能够很快地解决大多数排序问题,但是要理解起来不是很容易,我们先来看一下快排的单步思想

那么我们该如何实现让6的左边都比6小,6的右边都比6大呢?

下面我们介绍一下"挖坑"法,pivot:英文单词"坑"

我们来看这几张图片

这就是单趟快排的示意图.

2.单趟快排的代码实现

接下来,我们先写一下单趟快排的代码

void QuickSort(int* a, int n)
{
  int begin = 0;
  int end = n - 1;
  int pivot = begin;
  int key = a[begin];
  while (begin < end)
  {
    //左边有坑,右边找小,放到左边
    while (a[end] >= key && begin < end)
    {
      end--;
    }
    if (begin < end)
    {
      //小的放到左边的坑里,自己形成了新的坑位
      a[pivot] = a[end];
      pivot = end;
    }
    //右边有坑,左边找小,放到右边
    while (a[begin] <= key && begin < end)
    {
      begin++;
    }
    if (begin < end)
    {
      //大的放到右边的坑里,自己形成了新的坑位
      a[pivot] = a[begin];
      pivot = begin;
    }
  }
  pivot = begin;
  a[pivot] = key;
}

可见,单趟排序的时间复杂度是O(N),因为begin和end要相遇,且每轮循环只能有一个"指针"在变动,这里所说的"指针"不是C语言所说的指针,而是一种标记而已

3.快排整体思想和代码实现

这时,我们成功让6的左边都小于6,6的右边都大于6,

此时我们想:

如果能让6的左边变为有序,右边也变为有序,那么不就整体有序了吗?

所以在这里我们呢考虑使用分治的思想,也就是递归的方法,也就是缩短区间(大事化小,将一个大问题拆解为若干个子问题,然后逐个击破)

因为当区间缩小到只剩一个值的时候,它一定有序

下面我们来看一张图片

我们发现,只需要对第一次分割出来的左区间和右区间分别进行多次挖坑法,就可以实现整体有序,

其实我们发现,这个快排的递归思想就像是二叉树的深度优先遍历.

下面我们用代码实现一下,如果感觉理解不了下面代码中的这种递归的方式,请仔细看一下上面这张图片,上面把递归调用的整个过程划分的很详细

void QuickSort(int* a, int left,int right)//这里为了进行函数递归,把形参改为左右"指针",方便进行递归
{
  if (left >= right)//当区间长度小于等于1时,无需进行排序了,如果不加这一条,函数将进入死递归
  {
    return;
  }
  int begin = left;
  int end = right;
  int pivot = begin;
  int key = a[begin];
  while (begin < end)
  {
    //左边有坑,右边找小,放到左边
    while (a[end] >= key && begin < end)
    {
      end--;
    }
    if (begin < end)
    {
      //小的放到左边的坑里,自己形成了新的坑位
      a[pivot] = a[end];
      pivot = end;
    }
    //右边有坑,左边找小,放到右边
    while (a[begin] <= key && begin < end)
    {
      begin++;
    }
    if (begin < end)
    {
      //大的放到右边的坑里,自己形成了新的坑位
      a[pivot] = a[begin];
      pivot = begin;
    }
  }
  pivot = begin;
  a[pivot] = key;
  //这里我们将[left,right]这个区间分为这么三个部分
  //[left,pivot-1]  pivot [pivot+1,right]
  QuickSort(a, left, pivot - 1);
  QuickSort(a, pivot + 1, right);
  //快排的递归思想就像是二叉树的深度优先遍历.
  //根 左区间 右区间 根 左区间 右区间 根 左区间 右区间 根 左区间 右区间 .........,
  //单个区间就像是叶子节点
}

上面就是快排的基础代码(还未经过优化的)

相关文章
|
3天前
|
搜索推荐 算法 Java
快速排序------一种优雅的排序算法
快速排序------一种优雅的排序算法
|
4天前
|
机器学习/深度学习 自然语言处理 算法
深度解析深度学习中的优化算法:从梯度下降到自适应方法
【4月更文挑战第28天】 在深度学习模型训练的复杂数学迷宫中,优化算法是寻找最优权重配置的关键导航者。本文将深入探讨几种主流的优化策略,揭示它们如何引导模型收敛至损失函数的最小值。我们将比较经典的批量梯度下降(BGD)、随机梯度下降(SGD)以及动量概念的引入,进一步探索AdaGrad、RMSProp和Adam等自适应学习率方法的原理与实际应用。通过剖析这些算法的理论基础和性能表现,我们旨在为读者提供一个关于选择合适优化器的参考视角。
|
6天前
|
算法 索引
数据结构与算法-并查集多种实现以及优化步骤
数据结构与算法-并查集多种实现以及优化步骤
7 0
|
6天前
|
算法
数据结构与算法-递归思想
数据结构与算法-递归思想
6 0
|
7天前
|
机器学习/深度学习 人工智能 算法
揭秘深度学习中的优化算法
【4月更文挑战第24天】 在深度学习的广阔天地中,优化算法扮演着至关重要的角色。本文将深入探讨几种主流的优化算法,包括梯度下降法、随机梯度下降法、Adam等,并分析它们的特点和适用场景。我们将通过理论分析和实例演示,揭示这些优化算法如何帮助模型更高效地学习参数,从而提高模型的性能。
|
8天前
|
人工智能 达摩院 算法
什么是优化技术?给算法小白同学的快速讲解和上手文
本文作者用一个曾经小白学习的视角,来讲解什么是优化问题,以及要如何用这个优化技术。
|
14天前
|
算法
PID算法原理分析及优化
这篇文章介绍了PID控制方法,这是一种广泛应用的控制算法,具有结构简单、鲁棒性强等特点。PID通过比例、积分和微分三个部分调整控制量,以减少系统输出与目标值的偏差。文章详细阐述了PID的基本原理,包括比例、积分和微分调节的作用,并提到积分饱和和微分项振荡的问题以及对应的优化策略,如积分分离、变速积分和微分先行等。此外,还提到了数字PID的实现形式,如位置式、增量式和步进式,以及串级PID在电机控制等领域的应用。
24 10
|
11天前
|
机器学习/深度学习 人工智能 算法
基于DCT和扩频的音频水印嵌入提取算法matlab仿真
本文介绍了结合DCT和扩频技术的音频水印算法,用于在不降低音质的情况下嵌入版权信息。在matlab2022a中实现,算法利用DCT进行频域处理,通过扩频增强水印的隐蔽性和抗攻击性。核心程序展示了水印的嵌入与提取过程,包括DCT变换、水印扩频及反变换步骤。该方法有效且专业,未来研究将侧重于提高实用性和安全性。
|
1天前
|
算法 数据安全/隐私保护 计算机视觉
基于DCT变换的彩色图像双重水印嵌入和提取算法matlab仿真
**算法摘要:** - 图形展示:展示灰度与彩色图像水印应用,主辅水印嵌入。 - 软件环境:MATLAB 2022a。 - 算法原理:双重水印,转换至YCbCr/YIQ,仅影响亮度;图像分割为M×N块,DCT变换后嵌入水印。 - 流程概览:两步水印嵌入,每步对应不同图示表示。 - 核心代码未提供。
|
2天前
|
机器学习/深度学习 算法 数据可视化
Matlab决策树、模糊C-均值聚类算法分析高校教师职称学历评分可视化
Matlab决策树、模糊C-均值聚类算法分析高校教师职称学历评分可视化
10 0