【算法】--- 几分钟了解直接选择排序(排序中最简单的排序)+快排(解决一切的优质算法)(中)

简介: 算法第二弹——排序算法

🌟一、常见的排序算法:


前面的插入排序可以回顾【算法】— 几分钟带你走进排序算法大门(上)

选择排序中的堆排序可以回顾【数据结构】—堆排序+TOP-K问题(了解游戏排行底层原理)

交换排序中的冒泡排序可以回顾C-指针的进阶(下)+qsort库函数

所以本章讲解选择排序中的直接选择排序和交换排序中的快速排序

🌟二、选择排序—直接选择排序:


🌏2.1.1 基本思想:


每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。

🌏2.1.2 直接选择排序:


在元素集合array[i]–array[n-1]中选择关键码最大(小)的数据元素

若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换

在剩余的array[i]–array[n-2](array[i+1]–array[n-1])集合中,重复上述步骤,直到集合剩余1个元素

🌏2.1.3 直接选择排序的特性总结:


直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用

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

空间复杂度:O(1)

稳定性:不稳定

🌏2.1.4 思路:


遍历比较选取一个最大的数,一个最小的数,将最小的数和开始位置值交换,最大的数和末尾位置值交换。

🌏2.1.5 代码:


#include<stdio.h>
void Swap(int*  p1, int* p2)
{
  int* tmp = *p1;
  *p1 = *p2;
  *p2 = tmp;
}
void SelectSort(int* a, int n)
{
  int begin = 0;
  int end = n - 1;
  while (begin < end)
  {
    int mini = begin, maxi = begin;
    for (int i = begin; i <= end; i++)
    {
      if (a[i] < a[mini])
      {
        mini = i;
      }
      if (a[i] > a[maxi])
      {
        maxi = i;
      }
    }
    Swap(&a[begin],& a[mini]);
    if (begin == maxi)
    {
      maxi = mini;
    }
    Swap(&a[maxi],& a[end]);
    begin++;
    end--;
  }
}
int main()
{
  int a[] = { 9,5,1,7,4,2,6,8![请添加图片描述](https://ucc.alicdn.com/images/user-upload-01/98bf39b9ed01460b83de60cbbe6f3a74.png)
,0,8 };
  int n = sizeof(a)/sizeof(a[0]);
  SelectSort(a, n);
  for (int i = 0; i < n; i++)
  {
    printf("%d ", a[i]);
  }
  return 0;
}

🌏2.1.6 注意易错点:


这一部分是必须要加上的,不然会出现一个bug,当交换开始位置值与最小值之后,要想一下若最大值就是开始位置值,再交换最大值和末尾值那最大值已经和最小值位置交换了不懂可以看下图:

    Swap(&a[begin],& a[mini]);
    if (begin == maxi)
    {
      maxi = mini;
    }
    Swap(&a[maxi],& a[end]);
    begin++;
    end--;

🌟三、交换排序—快速排序(上):


🌏3.1.1 基本思想:


所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

🌏3.1.2 快速排序


快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

💫3.1.2.1 第一种—挖坑填补法:


📒思路:


快速排序算法是基于分治策略的另一个排序算法。


该方法的基本思想是:

1.先从数列中取出一个数作为基准数,记为key。

2.分区过程,将小于key的数全放到它的左边,大于key的数全放到它的右边。

📒代码:


#include<stdio.h>
void QuickSort(int* a,int left ,int  right)
{
  //剩下一个数或者没有数就达到了排序就不用排了
  if (left >= right)
  {
    return;
  }
  int begin = left, end = right;
  int pivot = begin;
  int key = a[begin];
  while (begin < end)
  {
    //右边找小,放到左边
    while (begin < end && a[end]>=key)
    {
      end--;
    }
    //找到放在左边的坑里
    a[pivot] = a[end];
    pivot = end;
    //左边找小,放到右边
    while (begin < end && a[begin] <= key)
    {
      begin++;
    }
    a[pivot] = a[begin];
    //找到放在右边的坑里
    pivot = begin;
  }
  //相遇了
  pivot = begin;
  a[pivot] = key;
  //分治策略,分区间将key左边和右边分别再次找关键key然后再次左小右大
  //直到剩下一个数或者没有数就达到了排序的目的
  QuickSort(a, left, pivot - 1);//注意传递的区间
  QuickSort(a, pivot +1,right);//注意传递的区间
}
int main()
{
  int a[] = { 9,5,1,7,4,2,6,3,0,8 };
  int n = sizeof(a) / sizeof(a[0]);
  QuickSort(a, 0,n-1);
  for (int i = 0; i < n; i++)
  {
    printf("%d ", a[i]);
  }
  return 0;
}

📒流程图:


然后再分为左区间,右区间然后再从左区间中找关键数字再次进行下图这种流程,右区间同理就类似二叉树

📒时间复杂度:


🔑最好的情况:接近二分

快排最好的情况就是二分(选取的key值正确位置刚好接近中间位置):每一次将一个数排到正确位置,分治算法就相当于一个完全二叉树高度为logN,且每一次时间复杂度O(N),所以总共时间复杂度是O(NlogN)*

🔑最坏的情况:有序

快排最坏情况就是有序:当有序时选取最左边(最右边)的数那么都小于(大于)其他数,就会出现左边(右边)没有要排的数,这样每一次都只排一个不像上面的情况类似一个二叉树

📒解决方法:三数取中—解决了快排中最坏的情况


🔑代码:

#include<stdio.h>
void Swap(int*  p1, int* p2)
{
  int* tmp = *p1;
  *p1 = *p2;
  *p2 = tmp;
}
int GetMidIndex(int* a, int left, int right)
{
  int mid = (left + right) >> 1;
  if (a[left] < a[mid])
  {
    if (a[mid] < a[right])
    {
      return mid;
    }
    else if (a[left] > a[right])
    {
      return left;
    }
    else
    {
      return right;
    }
  }
  else//a[left]>a[mid]
  {
    if (a[mid] > a[right])
    {
      return mid;
    }
    else if (a[left] < a[right])
    {
      return left;
    }
    else
    {
      return right;
    }
  }
}
void QuickSort(int* a,int left ,int  right)
{
  //剩下一个数或者没有数就达到了排序就不用排了
  if (left >= right)
  {
    return;
  }
  int index = GetMidIndex(a, left, right);
  Swap(&a[left], &a[index]);
  int begin = left, end = right;
  int pivot = begin;
  int key = a[begin];
  while (begin < end)
  {
    //右边找小,放到左边
    while (begin < end && a[end]>=key)
    {
      end--;
    }
    //找到放在左边的坑里
    a[pivot] = a[end];
    pivot = end;
    //左边找小,放到右边
    while (begin < end && a[begin] <= key)
    {
      begin++;
    }
    a[pivot] = a[begin];
    //找到放在右边的坑里
    pivot = begin;
  }
  //相遇了
  pivot = begin;
  a[pivot] = key;
  //分治策略,分区间将key左边和右边分别再次找关键key然后再次左小右大
  //直到剩下一个数或者没有数就达到了排序的目的
  QuickSort(a, left, pivot - 1);
  QuickSort(a, pivot +1,right);
}
int main()
{
  //int a[] = { 0.1,2,3,4,5,6,7,8,9 };
  int a[] = { 9,5,1,7,4,2,6,3,0,8 };
  int n = sizeof(a) / sizeof(a[0]);
  QuickSort(a, 0,n-1);
  for (int i = 0; i < n; i++)
  {
    printf("%d ", a[i]);
  }
  return 0;
}

📒补充知识点:>>1与/2的关系


n为非负数时,>> 1和/ 2的结果是一样的

n为负数且还是偶数时,>> 1和/ 2的结果是一样的

n为负数且还是奇数时,>> 1和/ 2的结果是不一样的

📒小区间优化:


🔑代码:

当每次调用越到最后递归调用越多,所以可以将最后几层递归调用消除掉,越到最后所排序的区间越小所以采用直接插入排序接可以了

对于直接插入排序可以回顾【算法】— 几分钟带你走进排序算法大门(上)

if (pivot - 1 - left > 10)
  {
    QuickSort(a, left, pivot - 1);
  }
  else
  {
    InsertSort(a + left, pivot - 1 - left + 1);
  }
  if (right-(pivot+1) > 10)
  {
    QuickSort(a, pivot + 1, right);
  }
  else
  {
    InsertSort(a + pivot+1, right-(pivot+1) + 1);
  }

😽总结


😽Ending,今天的直接选择排序+快排(中)的内容就到此结束啦~,如果后续想了解更多,就请关注我吧

相关文章
|
2月前
|
算法 C++
【洛谷 P1223】排队接水(贪心算法+结构体排序)
该问题要求安排$n$个人的排队顺序以最小化平均等待时间。每个人接水需时$T_i$,解决方案是让接水时间短的人优先。给定$n\leq1000$和$t_i\leq10^6$,代码示例使用C++实现,通过排序使时间从小到大排列,然后计算平均等待时间。样例输入为10个人的时间数组,输出为优化后的排队顺序及平均等待时间(291.90)。
27 0
|
5天前
|
算法
【算法】二分查找——在排序数组中查找元素的第一个和最后一个位置
【算法】二分查找——在排序数组中查找元素的第一个和最后一个位置
|
6天前
|
搜索推荐 算法 Java
现有一个接口DataOperation定义了排序方法sort(int[])和查找方法search(int[],int),已知类QuickSort的quickSort(int[])方法实现了快速排序算法
该博客文章通过UML类图和Java源码示例,展示了如何使用适配器模式将QuickSort类和BinarySearch类的排序和查找功能适配到DataOperation接口中,实现算法的解耦和复用。
现有一个接口DataOperation定义了排序方法sort(int[])和查找方法search(int[],int),已知类QuickSort的quickSort(int[])方法实现了快速排序算法
|
6天前
|
搜索推荐 算法 Java
经典排序算法之-----选择排序(Java实现)
这篇文章通过Java代码示例详细解释了选择排序算法的实现过程,包括算法的基本思想、核心代码、辅助函数以及测试结果,展示了如何通过选择排序对数组进行升序排列。
经典排序算法之-----选择排序(Java实现)
|
12天前
|
算法 关系型数据库 MySQL
揭秘MySQL中的版本号排序:这个超级算法将颠覆你的排序世界!
【8月更文挑战第8天】在软件开发与数据管理中,正确排序版本号对软件更新及数据分析至关重要。因MySQL默认按字符串排序版本号,可能出现&#39;1.20.0&#39;在&#39;1.10.0&#39;之前的不合理情况。解决办法是将版本号各部分转换为整数后排序。例如,使用`SUBSTRING_INDEX`和`CAST`函数从`software`表的`version`字段提取并转换版本号,再按这些整数排序。这种方法可确保版本号按逻辑正确排序,适用于&#39;major.minor.patch&#39;格式的版本号。对于更复杂格式,需调整处理逻辑。掌握此技巧可有效应对版本号排序需求。
39 3
|
4天前
|
算法 搜索推荐
算法设计 (分治法应用实验报告)基于分治法的合并排序、快速排序、最近对问题
这篇文章是关于分治法应用的实验报告,详细介绍了如何利用分治法实现合并排序和快速排序算法,并探讨了使用分治法解决二维平面上的最近对问题的方法,包括伪代码、源代码实现及时间效率分析,并附有运行结果和小结。
|
11天前
|
存储 算法 Java
LeetCode初级算法题:反转链表+统计N以内的素数+删除排序数组中的重复项Java详解
LeetCode初级算法题:反转链表+统计N以内的素数+删除排序数组中的重复项Java详解
10 0
|
1月前
|
存储 算法 搜索推荐
算法进阶之路:Python 归并排序深度剖析,让数据排序变得艺术起来!
【7月更文挑战第12天】归并排序是高效稳定的排序算法,采用分治策略。Python 实现包括递归地分割数组及合并已排序部分。示例代码展示了如何将 `[12, 11, 13, 5, 6]` 分割并归并成有序数组 `[5, 6, 11, 12, 13]`。虽然 $O(n log n)$ 时间复杂度优秀,但需额外空间,适合大规模数据排序。对于小规模数据,可考虑其他算法。**
57 4
|
2月前
|
机器学习/深度学习 算法 搜索推荐
数据结构算法--2 冒泡排序,选择排序,插入排序
**基础排序算法包括冒泡排序、选择排序和插入排序。冒泡排序通过相邻元素比较交换,逐步将最大值“冒”到末尾,平均时间复杂度为O(n^2)。选择排序每次找到剩余部分的最小值与未排序部分的第一个元素交换,同样具有O(n^2)的时间复杂度。插入排序则类似玩牌,将新元素插入到已排序部分的正确位置,也是O(n^2)复杂度。这些算法适用于小规模或部分有序的数据。**
|
2月前
|
搜索推荐
排序算法---选择排序-----详解&&代码
排序算法---选择排序-----详解&&代码

热门文章

最新文章