对快速排序进行尾递归优化

常规快速排序存在的问题

对于快速排序的实现,可以参考这篇博文:
三分钟学会快速排序(图示讲解,附代码,通俗易懂)

快排的核心函数如下:

void quickSort(int arr[], int low, int high)
{
    if (low < high)
    {
        int pi = partition(arr, low, high);
        quickSort(arr, low, pi - 1);
        quickSort(arr, pi + 1, high);
    }
}

在最坏情况下,递归是这样的:

quickSort(arr,1,n )
quickSort(arr,1,n-1)
quickSort(arr,1,n-2)
quickSort(arr,1,n-3)
.
.
.
quickSort(arr,1,1)

用下面的递归树可以更好的理解,树的深度是n

在这里插入图片描述
由上图可知,此时堆栈上需要 O ( n ) O(n) O(n)空间

用while进行尾递归优化后的快速排序

void quickSort(int arr[], int low, int high)
{
    while (low < high)
    {
        int pi = partition(arr, low, high);
        if (pi - low < high - pi)
        {
            quickSort(arr, low, pi - 1);
            low = pi + 1;
        }
        else
        {
            quickSort(arr, pi + 1, high);
            high = pi - 1;
        }
    }
}

在上面的代码中,如果左侧部分长度更小,则对左侧部分进行递归调用,否则对右侧部分进行递归调用。

此时使用 O ( L o g n ) O(Logn) O(Logn)额外空间。

尾递归

若函数在尾位置调用自身,则称这种情况为尾递归。尾递归是一种特殊的尾调用,即在尾部直接调用自身的递归函数。

函数自身调用次数过多会导致递归层数很深,导致爆栈。

举个例子:

def recsum(x):
  if x == 1:
    return x
  else:
    return x + recsum(x - 1)

当调用recsum(5)时,栈调用如下:

recsum(5)
5 + recsum(4)
5 + (4 + recsum(3))
5 + (4 + (3 + recsum(2)))
5 + (4 + (3 + (2 + recsum(1))))
5 + (4 + (3 + (2 + 1)))
5 + (4 + (3 + 3))
5 + (4 + 6)
5 + 10
15

可见递归深度是很深的

可以如下进行尾递归优化:

def foo(x, sum=0):
  if x == 0:
    return sum
  else:
    return foo(x - 1, sum + x)

则对应的调用为:

foo(5, 0)
foo(4, 5)
foo(3, 9)
foo(2, 12)
foo(1, 14)
foo(0, 15)
15

易知这是一个线性的调用

函数调用栈

在程序运行时,程序会被分配一定的内存空间,其中一部分被用于记录程序中正在调用的各个函数的运行情况,这就是函数的调用栈。

常规的函数调用总是会在调用栈最上层添加一个新的堆栈帧(stack frame,也翻译为“栈帧”或简称为“帧”),这个过程被称作“入栈”或“压栈”(意即把新的帧压在栈顶)。

当函数的调用层数非常多时,调用栈会消耗不少内存,甚至会撑爆内存空间(俗称爆栈)。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Python 中实现快速排序尾递归版本相对复杂,因为 Python 的垃圾回收机制并不支持原地尾递归优化(Tail Call Optimization, TCO),这会导致递归深度过深时栈溢出。尽管如此,我们可以使用循环结构来模拟尾递归,虽然不是严格意义上的尾递归,但可以避免不必要的栈空间消耗。 快速排序通常是一个分治算法,其基本思路是选择一个基准值,将数组分为两部分,一部分所有元素都小于基准,另一部分所有元素都大于基准,然后对这两部分分别递归进行排序。然而,为了展示这种概念,这里有一个不使用递归但具有相似逻辑的非尾递归快速排序的例子: ```python def quicksort_tail_recursive(arr, low=0, high=None): if high is None: high = len(arr) - 1 while low < high: pivot_index = partition(arr, low, high) if pivot_index - low < high - pivot_index: quicksort_tail_recursive(arr, low, pivot_index - 1) else: quicksort_tail_recursive(arr, pivot_index + 1, high) return arr def partition(arr, low, high): pivot = arr[high] i = low - 1 for j in range(low, high): if arr[j] <= pivot: i += 1 arr[i], arr[j] = arr[j], arr[i] arr[i+1], arr[high] = arr[high], arr[i+1] return i + 1 ``` 在这个版本中,`quicksort_tail_recursive` 函数使用了两个指针 `low` 和 `high` 来迭代处理数组,而不是传统的递归调用。这样可以避免栈的增长,但是Python并没有直接支持尾递归优化,所以这个实现并不能真正节省栈空间。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值