数据结构之排序【冒泡排序和快速排序之一的实现及分析】内含动态演示图

简介: 数据结构之排序【冒泡排序和快速排序之一的实现及分析】内含动态演示图

引言:

今天分享一下一点小事迹,自从从学校回到家里,我已经好久没睡上一个好觉了,因为真的冷,莫名被窝总是感觉很冷,然后穿着袜袜的脚也是冰凉,所以每次早晨要起床的时候总是感觉非常的冷,更牛的是我昨天直接被冷醒了,可能是因为学校的床没有那么大,所以不容易把热量散发掉,所以每次在学校都睡的非常的香,所以今天我决定睡在地板上(当然是床和衣柜之间的地板),这样我就可以实现小床睡觉了(明天的这个时候,准时汇报具体睡觉情况),并且现在是北京时间 2022/12/25/22:25 ,我还有一篇期末论文没有写,我毅然决然的选择先搞定这个,其它的再说吧!

天大寒,砚冰坚,手指不可屈伸,码字还是不易,所以引言就这样吧!今天我们学习一下冒泡排序和快速排序,当然重点是快速排序(冒泡排序效率低,所以我们学一下思想就行)


1.冒泡排序

for循环和while循环代码实现并且进行它们之间的比较对比

动图演示:

28.gif像排序这种类型的东西,以后我们可以不要直接来就靠我们的记忆去写,可以先按照基本原理来

比如,此时我们就是想要把一个数组中的最大的那个数放到数组的最后一个位置处,应该怎样写,

怎样通过多躺来控制单趟,怎样控制次数和边界

然后此时的这个for循环代表多躺,用来控制我的单趟(所以我的单趟是思想,多躺是和单趟相似的)


void BubbleSort(int* arr, int n)
{
  int j;
  for (j = 0; j < n - 1; j++)
  {
    int exchange = 0;
    int i;
    for (i = 0; i < n - 1 - j; i++)//遍历
    {
      if (arr[i] > arr[i + 1])//每个数比较,大的数往后走
      {
        Swap(&arr[i], &arr[i + 1]);
        exchange = 1;
      }
    }
    if (exchange == 0)//此时就是防止数组已经是有序的,然后还进行比较,所以我们为了防止,就可以这样写,交换了就不满足这个条件,就正常比较,没有交换就满足这个条件,就跳出循环
    {
      break;
    }
  }
}

所以只要是有思想代码怎么写都是可以的,单趟(目的:为了让最大值到达数组的最后一个位置)

多躺:控制单趟和边界的处理

void BubbleSort(int* arr, int n)
{
  int end = n;
  while (end > 0)
  {
    int i;
    for (i = 0; i < n - 1; i++)
    {
      if (arr[i] > arr[i + 1])
      {
        Swap(&arr[i], &arr[i + 1]);
      }
    }
    //写完单趟,然后我为了控制次数,可以用上述的for循环,也可以直接用while循环,只要把比较的次数给控制好了,怎样写都行
    end--;//反正就是n次而已
  }
}

所以总的来说对比两种循环,多躺就是用来控制单趟的边界的(就是控制什么时候要继续,什么时候停下来,然后循环几次之类的问题)


2.重点:快速排序

2.1 快速排序(方法很多(主要涉及递归和非递归),复杂)

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


将区间按照基准值划分为左右两半部分的常见方式有: 1.挖坑法


上述的原理:1. 第一步就是要找一个key(关键值),6 5 1 2 7 9 3 4 10 8 => 此时就把arr[0]作为我的key(也就是把这个key作为一个pivot(坑)),然后把比key大的放在key的右边,比key小的放在key的左边


2.2 挖坑法的具体原理:我们把arr[0]的值给拿走了,此时key处就是一个坑(就是空位置的意思) ,


然后我就去遍历key右边的数组元素,比较,把比key小的数给放到arr[0]位置处,然后把我们刚刚找


到的那个比key小的元素的位置重新赋值为坑(因为此时这个元素已经是没用的了,这个位置就可以


相当于是空),然后再去遍历key左边的数组元素,把key左边比key大的数放到新的坑的位置,然后


把刚刚key左边找到比key大的元素的位置重新赋值为坑,然后重复这个步骤,此时我就实现单趟排序


把比key大的放在key的右边,比key小的放在key的左边,并且将key放在正确的位置处 => 5 1 2 4 3


6 9 7 10 8 变成这样就是我的单趟的目的 (虽然说是单趟但是实际是一个循环),只是后面还会涉


及到递归而已


具体动图演示:


image.gif然后我们以下面的图示来进行进一步的理解:


30.png


33.png


34.png

右边按照同样的操作进行,即可把排序完成(即我的分治递归)

2.3 代码实现(注释详解每一步)

//void QuickSort(int* arr, int n)
//为了使用我的分治递归现在我的这个函数的作用就不单单是一个值了,是一段数组中的值,所以就不可以按照上面这样写,应该要写成下面的形式
void QuickSort(int* arr, int left, int right)
{
  if (left >= right)//递归的停止条件(就是当我的区间只有一个值或不存在)大于表示只有一个值,等于表示区间不存在
  {
    return;
  }
  //int begin = 0;//就是最左边的意思
  //int end = n - 1;//就是最右边的意思
  //此时为了实现分治递归,我们就要把上面的begin和end写成下面的这个样子
  int begin = left;
  int end = right;
  int pivot = begin;//按照原理,此时的这个pivot可以给最左边的位置或者最右边的位置
  int key = arr[begin];//按照原理,此时的这个pivot可以给最左边的位置或者最右边的位置
  while (begin < end)//因为待会会进行begin++ end--的操作,所以当它们相等之后,我就已经实现了把比key大的放在key的右边,比key小的放在key的左边,所以只要begin!=end 这个循环就要继续
  {
    //右边找小,放到左边(在key的右边找,自然是从最右边开始)
    while (begin < end && arr[end] >= key)
    {
      end--;
    }
    //程序来到这里说明我找到比key小的元素了(然后此时按照原理:就是把找到的这个值放到key的pivot位置,然后重新把这个找到的元素的位置赋值成pivot(便于待会把左边的元素放过来))
    arr[pivot] = arr[end];
    pivot = end;
    //此时我的左边的坑被右边的元素占据了,所以现在就要在左边找比key大的数据放到右边的坑中,然后左边再形成一个坑
    while (begin < end && arr[begin] <= key)
    {
      begin++;
    }
    //程序来到这个位置说明我找到比key大的元素了,然后把它放到右边的坑中就行,顺便把自己原来的位置重新赋值为坑,使左边又有一个新的坑,便于待会后面比key小的值放进来
    arr[pivot] = arr[begin];
    pivot = begin;
  }
  //此时程序来到这个位置就是说明:begin和end相遇了(此时就可以把我的key的位置直接放到最后的这个pivot中)
  pivot = begin;//此时就是找最后一个坑的位置
  arr[pivot] = key;//然后把key放到这个坑里面去
  //此时这边我们为了可以使用我的分治递归,按照原理就是要把这个数组分成三部分:
  //[left,right]
  //                                                [pivot]
  //                                                   |                                     
  //[left,pivot-1][pivot][pivot+1,right] =>  5 1 2 4 3 6 9 7 10 8
  //因为此时只要左子区间有序了,右子区间有序了,我的整个数组就有序了,所以此时就是把左子区间和右子区间都拿去递归就行了
  QuickSort(arr, left, pivot - 1);
  QuickSort(arr, pivot + 1, right);
}

3.快排测试

35.png


相关文章
|
11月前
|
算法 搜索推荐
快速排序-数据结构与算法
快速排序(Quick Sort)是一种基于分治法的高效排序算法。其核心思想是通过选择基准(pivot),将数组划分为左右两部分,使得左侧元素均小于基准,右侧元素均大于基准,然后递归地对左右两部分进行排序。时间复杂度平均为 O(n log n),最坏情况下为 O(n²)(如数组已有序)。空间复杂度为 O(1),属于原地排序,但稳定性不佳。 实现步骤包括编写 `partition` 核心逻辑、递归调用的 `quickSort` 和辅助函数 `swap`。优化方法有随机化基准和三数取中法,以减少最坏情况的发生。
685 13
|
存储 机器学习/深度学习 算法
C 408—《数据结构》图、查找、排序专题考点(含解析)
408考研——《数据结构》图,查找和排序专题考点选择题汇总(含解析)。
973 29
|
存储 人工智能 算法
【C++数据结构——内排序】二路归并排序(头歌实践教学平台习题)【合集】
本关任务是实现二路归并算法,即将两个有序数组合并为一个有序数组。主要内容包括: - **任务描述**:实现二路归并算法。 - **相关知识**: - 二路归并算法的基本概念。 - 算法步骤:通过比较两个有序数组的元素,依次将较小的元素放入新数组中。 - 代码示例(以 C++ 为例)。 - 时间复杂度为 O(m+n),空间复杂度为 O(m+n)。 - **测试说明**:平台会对你编写的代码进行测试,提供输入和输出示例。 - **通关代码**:提供了完整的 C++ 实现代码。 - **测试结果**:展示代码运行后的排序结果。 开始你的任务吧,祝你成功!
419 10
|
搜索推荐 算法 数据处理
【C++数据结构——内排序】希尔排序(头歌实践教学平台习题)【合集】
本文介绍了希尔排序算法的实现及相关知识。主要内容包括: - **任务描述**:实现希尔排序算法。 - **相关知识**: - 排序算法基础概念,如稳定性。 - 插入排序的基本思想和步骤。 - 间隔序列(增量序列)的概念及其在希尔排序中的应用。 - 算法的时间复杂度和空间复杂度分析。 - 代码实现技巧,如循环嵌套和索引计算。 - **测试说明**:提供了测试输入和输出示例,帮助验证代码正确性。 - **我的通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了代码运行的测试结果。 通过这些内容,读者可以全面了解希尔排序的原理和实现方法。
296 10
|
搜索推荐 C++
【C++数据结构——内排序】快速排序(头歌实践教学平台习题)【合集】
快速排序是一种高效的排序算法,基于分治策略。它的主要思想是通过选择一个基准元素(pivot),将数组划分成两部分。一部分的元素都小于等于基准元素,另一部分的元素都大于等于基准元素。然后对这两部分分别进行排序,最终使整个数组有序。(第一行是元素个数,第二行是待排序的原始关键字数据。本关任务:实现快速排序算法。开始你的任务吧,祝你成功!
333 7
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
543 1
|
算法 搜索推荐 Java
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
基数排序是一种稳定的排序算法,通过将数字按位数切割并分配到不同的桶中,以空间换时间的方式实现快速排序,但占用内存较大,不适合含有负数的数组。
325 0
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
361 59
|
9月前
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
192 0
栈区的非法访问导致的死循环(x64)
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
692 77

热门文章

最新文章