【初阶数据结构】打破递归束缚:掌握非递归版快速排序与归并排序

简介: 【初阶数据结构】打破递归束缚:掌握非递归版快速排序与归并排序

一、非递归实现快速排序

void QuickSortNonR(int* a, int begin, int end)
{
  ST s;
  STInit(&s);
  STPush(&s, end);
  STPush(&s, begin);
  while (!STEmpty(&s))
  {
    int left = STTOP(&s);//先拿出来
    STPop(&s);
    int right= STTOP(&s);//后拿出来
    STPop(&s);
        //单躺排序,一次调整,得到中间keyi值,划分填入
    int keyi = PartSort2(a, left, right);   
    //[left,keyi-1]keyi[keyi+1,right]
    
    if (left < keyi - 1)
    {
      STPush(&s, keyi - 1);
      STPush(&s, left);
    }
        
    if (keyi + 1 < right)
    {
      STPush(&s, right);
      STPush(&s, keyi+1);
    }
  }
  STDestroy(&s);
}

过程解析:非递归实现快速排序也是需要通过快速排序思想来走的,基本思想是以某数值为基准值,不断将待排序集合分割成两组子序列,采用前序遍历的方法根 左子树 右子树,对于递归的过程中我们知道左子树会演变为新的根,也会分为新根 新左子树 新右子树,然后我们将采用栈来模拟递归的过程,由于栈的特点是后进先出合前序遍历的特性。这里后进代表着左子树,新出代表着该左子树为根演变新左子树和新右子树的过程。然后这里需要范围去定义根(整体范围)、左子树(左边范围)、右子树(右边范围)。这里左子树会不断分为新的左子树和右子树,也意味着产生新的范围,一般来说先取左边(在上)再取右边(在下),对应着右边先压栈,左边再压栈。

单躺排序结束会返回中间位置下标keyi帮整体划分为两部分,不断重复该过程。当条件不满足时,说明暂时不需要继续分为左子树和右子树,可能出现其他两种结果。第一种相等,说明只有一个数据;第二种大于。说明不存在数据,不需要压栈,等到栈为空结束排序。

二、非递归实现归并排序

由于快速排序采用是前序遍历满足栈相关数据结构的特性,然后归并排序属于后序排序因此不是通过使用栈区模拟非递归实现归并排序。如果采用栈去模拟实现非递归归并排序,由于归并排序不像快速排序不是出栈既排序,而是等到栈为空,开始归并排序,然而没有归并操作这种做法。

基本思路:整个序列分为以gap为子序列,结束条件为gap < n(gap = n - 1 表示归并最后一次),将两个有序子序列,比较大小尾插到新数组中,(子序列中只有一个数据时,默认是有序的)。对于两个有序子序归并在一起,形成一个新的有序子序列,当形成完新的子序列,复制到原数组中,不断重复该操作。

解决办法:对此应当设置变量gap为归并每组数据个数,首先gap设为1,以二的幂次方增长。

int begin1=i,end1=i+gap-1;
int begin2=i+gap,end2=i+2*gap-1;
[begin1,end1][begin2,end2]归并

注意点:这里gap是二的幂次方增长,对于两个子序列匹配时,规定数量是固定的,可能会出现越界访问,所以需要注意end1,begin2和end2的值。

对于end2只需要将修改为临界值就行,而对于end1和begin2则不参与进程。完成一趟for循环,需要拷贝到原数组中,这样子的话不参与进程的序列,将被随机值替代.

if (end1 >= n || begin2 >= n)  break;
if (end2 >= n)  end2 = n - 1;
void MergeSortNonR(int* a, int n)
{
    int* tmp = (int*)malloc(n * sizeof(int));
    if (tmp == NULL)
    {
        perror("malloc fail!!!");
        return;
    }
    int gap = 1;
    while (gap < n)
    {
        printf("gap:%2d->", gap);
        for (int i = 0; i < n; i += 2 * gap)//调正部分是跳过两组子序列,去到新的两组子序列中
        {
            int begin1 = i, end1 = i + gap - 1;
            int begin2 = i + gap, end2 = i + 2 * gap - 1;
            //printf("[%2d,%2d][%2d, %2d] ", begin1, end1, begin2, end2);
            if (end1 >= n || begin2 >= n)
            {
                break;
            }
            if (end2 >= n)
            {
                end2 = n - 1;
            }
            printf("[%2d,%2d][%2d, %2d] ", begin1, end1, begin2, end2);
      
            //上面是传值
            int j = begin1;
      
            //下面是插入,这里j下标很有讲究
            while (begin1 <= end1 && begin2 <= end2)
            {
                if (a[begin1] < a[begin2])
                {
                    tmp[j++] = a[begin1++];
                }
                else
                {
                    tmp[j++] = a[begin2++];
                }
            }//还有部分数据没有放入新数组中
            while (begin1 <= end1)
            {
                tmp[j++] = a[begin1++];
            }
            while (begin2 <= end2)
            {
                tmp[j++] = a[begin2++];
            }
            memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));//可能是右边归并
        }
        printf("\n");
        gap *= 2;
    } 
    free(tmp);
}

以上就是本篇文章的所有内容,在此感谢大家的观看!这里是店小二初阶数据结构笔记,希望对你在学习初阶数据结构中有所帮助!


相关文章
|
5天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
14 1
|
1月前
|
算法 搜索推荐 Shell
数据结构与算法学习十二:希尔排序、快速排序(递归、好理解)、归并排序(递归、难理解)
这篇文章介绍了希尔排序、快速排序和归并排序三种排序算法的基本概念、实现思路、代码实现及其测试结果。
20 1
|
1月前
|
存储
【数据结构】二叉树链式结构——感受递归的暴力美学
【数据结构】二叉树链式结构——感受递归的暴力美学
|
1月前
|
算法
蓝桥杯宝藏排序 | 数据结构 | 快速排序 归并排序
蓝桥杯宝藏排序 | 数据结构 | 快速排序 归并排序
|
1月前
|
算法 定位技术
数据结构与算法学习九:学习递归。递归的经典实例:打印问题、阶乘问题、递归-迷宫问题、八皇后问题
本文详细介绍了递归的概念、重要规则、形式,并展示了递归在解决打印问题、阶乘问题、迷宫问题和八皇后问题等经典实例中的应用。
38 0
|
14天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
90 9
|
8天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
11天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
13天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
40 4
|
1月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
30 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器

热门文章

最新文章