Python 递归与迭代时间复杂性
有谁能确切地解释一下,为了使递归方法在时间复杂度方面更快、更有效地处理下面的问题,到底发生了什么 问题是:编写一个程序,将整数数组作为输入,返回数组中排序的最大三个数字,而不排序原始(输入)数组 例如:Python 递归与迭代时间复杂性,python,arrays,algorithm,data-structures,Python,Arrays,Algorithm,Data Structures,有谁能确切地解释一下,为了使递归方法在时间复杂度方面更快、更有效地处理下面的问题,到底发生了什么 问题是:编写一个程序,将整数数组作为输入,返回数组中排序的最大三个数字,而不排序原始(输入)数组 例如: 输入:[22,5,3,1,8,2] 输出:[5,8,22] 尽管我们可以简单地对原始数组进行排序并返回最后三个元素,但这至少需要O(nlog(n))时间,因为最快的排序算法可以做到这一点。因此,挑战在于更好地执行任务,并在O(n)时间内完成任务 所以我想出了一个递归的解决方案: def
- 输入:
[22,5,3,1,8,2]
- 输出:
[5,8,22]
O(nlog(n))
时间,因为最快的排序算法可以做到这一点。因此,挑战在于更好地执行任务,并在O(n)
时间内完成任务
所以我想出了一个递归的解决方案:
def findThreeLargestNumbers(array, largest=[]):
if len(largest) == 3:
return largest
max = array[0]
for i in array:
if i > max:
max = i
array.remove(max)
largest.insert(0, max)
return findThreeLargestNumbers(array, largest)
def find_largest(array, limit=3):
if len(array) <= limit:
# Special logic not needed.
return sorted(array)
else:
# Initialize the answer to values that will be replaced.
min_val = min(array[0:limit])
answer = [min_val for _ in range(limit)]
# Now scan for smallest.
for i in array:
if answer[0] < i:
# Sift elements down until we find the right spot.
j = 1
while j < limit and answer[j] < i:
answer[j-1] = answer[j]
j = j+1
# Now insert.
answer[j-1] = i
return answer
我一直在寻找最大的数字,从原始数组中删除它,将其附加到空数组中,然后递归地再次调用该函数,直到数组中有三个元素
但是,当我查看建议的迭代方法时,我编写了以下代码:
def findThreeLargestNumbers(array):
sortedLargest = [None, None, None]
for num in array:
check(num, sortedLargest)
return sortedLargest
def check(num, sortedLargest):
for i in reversed(range(len(sortedLargest))):
if sortedLargest[i] is None:
sortedLargest[i] = num
return
if num > sortedLargest[i]:
shift(sortedLargest, i, num)
return
def shift(array, idx, element):
if idx == 0:
array[0] = element
return array
array[0] = array[1]
array[idx-1] = array[idx]
array[idx] = element
return array
这两个代码都成功地通过了所有测试,我确信迭代方法更快(尽管没有那么干净)。但是,我导入了时间模块,并通过提供一个包含一百万个随机整数的数组并计算每个解决方案返回最大三个数字的排序数组所需的时间来测试代码
递归方法比迭代方法快得多(大约快9倍)
为什么呢?即使递归方法遍历巨大数组三次,而且每次都会删除一个元素(这需要O(n)
时间,因为所有其他999
元素都需要在内存中移位),然而,迭代方法只遍历输入数组一次,而且每次迭代都会进行一些操作,但是使用一个非常小的数组,大小为3,根本不需要时间
我真的希望能够对任何给定的问题判断并选择最有效的算法,因此任何解释都会大有帮助。递归解决方案
递归函数遍历列表3次以查找最大的数字,并从列表中删除最大的数字3次
for i in array:
if i > max:
...
及
所以,你有3×N的比较,加上3倍的删除。我猜删除在C中是优化的,但是仍然有大约3×N/2的比较来找到要删除的项
因此,总共大约有4.5×N的比较
另一种解决方案
另一种解决方案只遍历列表一次,但每次都与sortedLargest
中的三个元素进行比较:
for i in reversed(range(len(sortedLargest))):
...
几乎每次它都用这三个赋值对sortedLargest
进行排序:
array[0] = array[1]
array[idx-1] = array[idx]
array[idx] = element
所以,你是N次:
- 呼叫
检查
- 创建和反转
范围(3)
- 访问
sortedLargest[i]
- 比较
num>sortedLargest[i]
- 呼叫
shift
- 比较
idx==0
array[0] = array[1]
array[idx-1] = array[idx]
array[idx] = element
N/3次数组[0]=元素
很难计算,但这远远超过了4.5×N的比较。优化建议
避免函数调用。避免创建临时垃圾。避免额外的比较。具有尽可能少地查看元素的逻辑。手工浏览你的代码是如何工作的,看看它需要多少步骤
递归代码只进行3次函数调用,正如其他地方指出的那样,每次调用平均进行1.5次比较。(1在查找最小值时,0.5在确定移除元素的位置时。)
您的迭代代码对每个元素进行大量比较,调用多余的函数,并调用像sorted
这样创建/销毁垃圾的东西
现在与此迭代解决方案进行比较:
def findThreeLargestNumbers(array, largest=[]):
if len(largest) == 3:
return largest
max = array[0]
for i in array:
if i > max:
max = i
array.remove(max)
largest.insert(0, max)
return findThreeLargestNumbers(array, largest)
def find_largest(array, limit=3):
if len(array) <= limit:
# Special logic not needed.
return sorted(array)
else:
# Initialize the answer to values that will be replaced.
min_val = min(array[0:limit])
answer = [min_val for _ in range(limit)]
# Now scan for smallest.
for i in array:
if answer[0] < i:
# Sift elements down until we find the right spot.
j = 1
while j < limit and answer[j] < i:
answer[j-1] = answer[j]
j = j+1
# Now insert.
answer[j-1] = i
return answer
def find_最大(数组,限制=3):
如果len(array)我不太适合python,但是我有一种不同的方法来解决这个问题。
据我所知,发布的所有解决方案都是O(NM)
,其中N是数组的长度,M是最大元素数组的长度。
由于您的具体情况是N>>M
,您可以说它是O(N)
,但输入越长,它的O(NM)
我同意@zvone的说法,在迭代解决方案中似乎有更多的步骤,这听起来似乎是对不同计算速度的有效解释。
回到我的建议,使用递归实现二进制搜索O(N*logM)
:
import math
def binarySearch(arr, target, origin = 0):
"""
Recursive binary search
Args:
arr (list): List of numbers to search in
target (int): Number to search with
Returns:
int: index + 1 from inmmediate lower element to target in arr or -1 if already present or lower than the lowest in arr
"""
half = math.floor((len(arr) - 1) / 2);
if target > arr[-1]:
return origin + len(arr)
if len(arr) == 1 or target < arr[0]:
return -1
if arr[half] < target and arr[half+1] > target:
return origin + half + 1
if arr[half] == target or arr[half+1] == target:
return -1
if arr[half] < target:
return binarySearch(arr[half:], target, origin + half)
if arr[half] > target:
return binarySearch(arr[:half + 1], target, origin)
def findLargestNumbers(array, limit = 3, result = []):
"""
Recursive linear search of the largest values in an array
Args:
array (list): Array of numbers to search in
limit (int): Length of array returned. Default: 3
Returns:
list: Array of max values with length as limit
"""
if len(result) == 0:
result = [float('-inf')] * limit
if len(array) < 1:
return result
val = array[-1]
foundIndex = binarySearch(result, val)
if foundIndex != -1:
result.insert(foundIndex, val)
return findLargestNumbers(array[:-1],limit, result[1:])
return findLargestNumbers(array[:-1], limit,result)
导入数学
def二进制搜索(arr、目标、原点=0):
"""
递归二进制搜索
Args:
arr(列表):要搜索的数字列表
目标(int):要搜索的编号
返回:
int:从inmmediate lower元素到arr中目标的索引+1,或-1(如果已经存在或低于arr中的最低值)
"""
一半=数学楼层((长度(arr)-1)/2);
如果目标>arr[-1]:
返回原点+长度(arr)
如果len(arr)=1或目标目标:
返回原点+一半+1
如果arr[half]==目标或arr[half+1]==目标:
返回-1
如果arr[一半]<目标:
返回二进制搜索(arr[half:],目标,原点+half)
如果arr[half]>目标:
返回二进制搜索(arr[:half+1],目标,原点)
def FindLargestNumber(数组,限制=3,结果=[]):
"""
数组中最大值的递归线性搜索
Args:
数组(列表):要搜索的数字数组
limit(int):返回的数组长度。默认值:3
返回:
列表:长度为限制的最大值数组
"""
如果len(