C++ 中快排的递归和非递归实现

下面是关于C++中快排的递归和非递归实现的详细攻略。

快速排序

快速排序是一种基于分治的排序算法,其主要思想是将待排序序列划分为三部分,左边是小于等于基准值的部分,右边是大于等于基准值的部分,中间是分界点,基准值一般选取序列的第一个数或者随机选取一个数。然后对左右两个部分递归调用快排算法,直到每个小部分只有一个数或为空。

递归实现

递归实现快速排序的核心是 partition 函数,它负责将待排序数组划分成左右两部分并返回分界点位置。以下是 C++ 代码:

int partition(int nums[], int left, int right) {
    int pivot = nums[left]; // 选取第一个数作为枢轴
    int i = left + 1, j = right;
    while (i <= j) {
        if (nums[i] > pivot && nums[j] < pivot) {
            swap(nums[i++], nums[j--]);
        }
        if (nums[i] <= pivot) i++;
        if (nums[j] >= pivot) j--;
    }
    swap(nums[left], nums[j]);
    return j;
}

void quicksort(int nums[], int left, int right) {
    if (left >= right) return;
    int pivotIndex = partition(nums, left, right);
    quicksort(nums, left, pivotIndex - 1);
    quicksort(nums, pivotIndex + 1, right);
}

partition 函数中,ij 指针分别从左右两端开始扫描,只要 i 所指向的数大于枢轴(即该数应该在右边部分),j所指向的数小于枢轴(即该数应该在左边部分),就将他们交换位置。最终 j就是分界点的位置,将枢轴值与其交换即将数组划分为两部分。

quicksort 函数中的 if (left >= right) return; 是递归终止条件。 pivotIndex是分界点,根据分界点将数组划分为两部分,分别递归调用 quicksort 函数。

非递归实现

快排递归实现算法虽然简单易懂,但是递归调用会占用大量的函数调用栈空间,对于大规模数据来说,如果采用递归实现会导致栈溢出,因此需要采用非递归实现。

非递归实现快速排序需要用到栈,可以手动模拟函数调用栈的操作。以下是 C++ 代码:

void quicksort(int nums[], int left, int right) {
    stack<int> stk;
    if (left < right) {
        stk.push(right);
        stk.push(left);
        while (!stk.empty()) {
            int l = stk.top(); stk.pop();
            int r = stk.top(); stk.pop();
            int p = partition(nums, l, r);
            if (l < p - 1) {
                stk.push(p - 1);
                stk.push(l);
            }
            if (p + 1 < r) {
                stk.push(r);
                stk.push(p + 1);
            }
        }
    }
}

使用栈模拟函数调用栈的过程就是在分治过程中选用堆栈换取递归的便利性。将堆栈初始化为整个序列,然后逐步缩小范围,最终使序列有序。

示例说明

下面用两个示例说明递归和非递归实现快速排序的过程。

假设有一个数组 arr = [3, 1, 5, 2, 4, 6, 7],首先是递归实现:

  1. 选取第一个数 3 作为枢轴,执行 partition 操作后,得到数组状态为 arr = [2, 1, 3, 5, 4, 6, 7],分界点为2,左边是 [2, 1],右边是 [5, 4, 6, 7]
  2. 对左边的 [2, 1],选取第一个数 2 作为枢轴,执行 partition 操作后,得到数组状态为 arr = [1, 2, 3, 5, 4, 6, 7],分界点为1,左边是 [1],右边是 [2, 3, 5, 4, 6, 7]
  3. 对右边的 [2, 3, 5, 4, 6, 7],选取第一个数 2 作为枢轴,执行 partition 操作后,得到数组状态为 arr = [1, 2, 3, 4, 5, 6, 7],分界点为4,左边是 [2, 3, 4], 右边是 [5, 6, 7]

最终数组 arr 已经有序。

再来看非递归实现。按照相同的过程,使用栈来模拟分治的过程:

  1. 选取第一个数 3 作为枢轴,执行 partition 操作后,得到数组状态为 arr = [2, 1, 3, 5, 4, 6, 7],分界点为2,左边是 [2, 1],右边是 [5, 4, 6, 7],将 [5, 4, 6, 7] 和 [2, 1] 两个区间分别入栈
  2. 取出栈顶区间 [5, 4, 6, 7],选取第一个数 5 作为枢轴,执行 partition 操作后,得到数组状态为arr = [2, 1, 3, 4, 5, 6, 7],分界点为5,左边是 [4],右边是 [6, 7]。将右边的区间 [6, 7] 和原来的区间 [5, 4, 6, 7] 继续入栈,栈内状态为 [7, 6, 2, 1]
  3. 取出栈顶区间 [7, 6],选取第一个数 7 作为枢轴,执行 partition 操作后,得到数组状态为 arr = [2, 1, 3, 4, 5, 6, 7],区间已经有序,不需要入栈。取出栈顶区间 [5, 4, 6],选取第一个数 5 作为枢轴,执行 partition 操作后,得到数组状态为 arr = [2, 1, 3, 4, 5, 6, 7],分界点为4,左边是 [4],右边是 [6],将右边的区间 [6] 和原来的区间 [5, 4, 6] 继续入栈。栈内状态为 [6, 2, 1]
  4. 取出栈顶区间 [6],这是一个只有一个元素的区间,不需要排序。

最终数组 arr 已经有序。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++ 中快排的递归和非递归实现 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • npm run dev失败的简单解决办法

    解决 \”npm run dev\” 失败的简单方法攻略 当你运行 npm run dev 命令时,如果出现错误,可能是由于多种原因引起的。下面是一些常见的问题和解决方法,希望能帮助你解决问题。 1. 检查依赖项 首先,确保你的项目的依赖项已经正确安装。你可以运行以下命令来安装依赖项: npm install 如果依赖项已经安装,你可以尝试删除 node_m…

    other 2023年8月3日
    00
  • Python二进制数据结构Struct的具体使用

    Python二进制数据结构Struct的具体使用 什么是Struct Struct是Python标准库中提供的一个二进制数据结构处理模块,可以使用它来实现二进制流数据的打包与解包。通过Struct,我们可以快速且方便地处理各种二进制数据格式,例如进行网络传输的数据包、读写二进制文件等。在Python中使用Struct可以显著提高二进制数据处理的效率。 Str…

    other 2023年6月27日
    00
  • PHP实现无限级分类(不使用递归)

    下面我会详细讲解如何使用 PHP 实现无限级分类,并且不使用递归的方式。 什么是无限级分类 无限级分类是指分类与分类之间存在父子关系,每个分类下都可以包含多个子分类,而每个子分类又可以包含多个子分类,以此类推,可以无限延伸下去的分类体系。它在很多网站的分类功能中都有使用,比如商品分类、文章分类等。 不使用递归的无限极分类实现 从数据库中获取所有分类的数据。 …

    other 2023年6月26日
    00
  • 【Unity】3.1 利用内置的3D对象创建三维模型

    以下是利用内置的3D对象创建三维模型的完整攻略,包括使用步骤和两个示例说明。 使用步骤 使用内置的3D对象创建三维模型的步骤如下: 打开Unity编辑器,创建一个新的3D项目。 在场景中创建一个空对象,作为模型的父对象。 从菜单栏中选择GameObject > 3D Object,选择一个内置的3D对象,例如Cube、Sphere或Cylinder。 …

    other 2023年5月7日
    00
  • java-如何在java中使用csvreaderapi返回数据类型

    以下是关于“Java如何在Java中使用CSVReader API返回数据类型”的完整攻略,包括定义、使用方法、示例说明和注意事项。 定义 CSVReader API是一种Java库,用于取和解析CSV文件。CSV文件是一种常见的数据格式,通常用于存储和传输表格数据。CSVReader API可以将CSV文件中的数据读取到Java程序中,并将其转换为Java…

    other 2023年5月8日
    00
  • javascript嵌套函数和在函数内调用外部函数的区别分析

    JavaScript嵌套函数和在函数内调用外部函数的区别分析 在JavaScript中,函数可以嵌套在其他函数内部,也可以在函数内部调用外部函数。虽然这两种方式都可以实现类似的功能,但它们之间存在一些区别。下面将详细讲解这两种方式的区别,并提供两个示例说明。 嵌套函数 嵌套函数是指在一个函数内部定义另一个函数。嵌套函数可以访问外部函数的变量和参数,这种特性称…

    other 2023年7月28日
    00
  • 织梦dedecms 本地模板安装图文方法

    关于“织梦dedecms 本地模板安装图文方法”的完整攻略,我将分步骤进行讲解,并提供两个示例说明。 步骤1:下载模板 在安装模板之前,首先需要下载所需要的模板。可以在官方网站上下载,也可以在第三方网站上下载,需要注意的是,下载的模板文件必须是zip压缩格式。 步骤2:解压缩模板文件 将下载的zip压缩文件解压缩,可以使用压缩软件,比如WinRAR等。解压缩…

    other 2023年6月27日
    00
  • js实现自定义路由

    下面为您详细讲解JavaScript实现自定义路由的完整攻略。 1. 什么是自定义路由? 自定义路由是指通过JS实现自己的路由系统,将URL请求与相应的处理函数相匹配,实现URL跳转的过程。 2. 实现步骤 2.1 步骤一:设置路由数组 在JS文件中我们需要设置一个包含所有路由规则的路由数组,该数组中的每一项都包含了一个URL路径和匹配该路径的处理函数。例如…

    other 2023年6月25日
    00
合作推广
合作推广
分享本页
返回顶部