当前位置:   article > 正文

递归&二分法_二分法递归

二分法递归

1. 用T函数表示法计算时间复杂度

例如二分法 T(n) = T(n/2) + O(1),将规模为n的问题降低为规模为n/2的问题

1)首先 T 代表的是 Time Complexity,n 代表的是问题规模(二分法里就是数组的大小)。
那么 T(n) 代表的就是:求处理问题规模为n的数据的时间复杂度是多少

2)然后 O 代表的是时间复杂度。O(1) 就意味着,你大概用一个 if 语句,或者简单的加加减减,就可以完成。O 在这里的意思是数量级约等于。在 O 的世界里,我们只考虑最高项是什么,不考虑系数和常数项。

2. 递归 (从后往前推导,降低所给问题的规模)

1)递归的三要素

  • 递归的定义:函数接受什么样的参数,返回什么样的值,代表什么样的意思
  • 递归的拆解
  • 递归的出口

2)Stack Overflow

摘要:我们通常所说的内存空间,包含了两个部分:栈空间 (stack space) 和 堆空间 (heap space)

栈空间 (Stack space):当一个程序在执行的时候,操作系统为了让进程可以使用一些固定的、不被其他进程侵占的空间用于函数调用、递归等操作,会开辟一个固定大小的空间 (比如8M) 给进程使用。这个空间通常不会太大,否则内存的利用率会很低,这个空间就是我们所说的 Stack space

栈空间里存储的内容,会在函数执行结束的时候被销毁。

栈空间主要包含:

  • 函数的参数与返回值
  • 函数的局部变量

堆空间 (Heap space):通常对象、new出来的东西都会存放在堆空间中。堆空间里存放的内容在函数执行结束后不会被销毁。

stack overflow: 我们通常所说的栈溢出,是指在函数调用或递归调用时,开辟了过多的内存,超出了操作系统分配的那个很小的固定空间导致的。

3)递归深度

定义:递归深度就是递归函数在内存中,同时存在的最大次数。

函数的调用,会在内存的栈空间中开辟新空间,用来存放子函数。递归函数会不断占用栈空间,因而太深的递归会导致栈空间溢出。

3. 二分法

1)基本原理:

每次取数组当前区间的中点,并与目标值target比较,根据比较结果缩减一半搜索范围,时间复杂度为O(logn)

2)基本条件:有序数组

3)递归实现:

  1. class Solution {
  2. public int search(int[] nums, int target) {
  3. return binarySearch(nums,0,nums.length-1,target);
  4. }
  5. // 通过缩减区间,来达到降低问题规模的目的
  6. private int binarySearch(int[] nums, int start, int end, int target){
  7. // 递归出口
  8. if(start > end){
  9. return -1;
  10. }
  11. int mid = start + (end - start)/2;
  12. if(nums[mid] == target){
  13. return mid;
  14. }
  15. if(nums[mid] < target){
  16. return binarySearch(nums,mid+1,end,target);
  17. }
  18. return binarySearch(nums,start,mid-1,target);
  19. }
  20. }

4)非递归-无死循环实现:

  1. public class Solution {
  2. /**
  3. * @param A an integer array sorted in ascending order
  4. * @param target an integer
  5. * @return an integer
  6. */
  7. public int findPosition(int[] nums, int target) {
  8. if (nums == null || nums.length == 0) {
  9. return -1;
  10. }
  11. int start = 0, end = nums.length - 1;
  12. // 要点1: start + 1 < end
  13. while (start + 1 < end) {
  14. // 要点2:start + (end - start) / 2
  15. int mid = start + (end - start) / 2;
  16. // 要点3:=, <, > 分开讨论,mid 不+1也不-1
  17. if (nums[mid] == target) {
  18. return mid;
  19. } else if (nums[mid] < target) {
  20. start = mid;
  21. } else {
  22. end = mid;
  23. }
  24. }
  25. // 要点4: 循环结束后,单独处理start和end
  26. if (nums[start] == target) {
  27. return start;
  28. }
  29. if (nums[end] == target) {
  30. return end;
  31. }
  32. return -1;
  33. }
  34. }

注: 为什么while条件要写成start + 1 < end ?

意味着当数组区间中剩余2个数时,退出循环。因为在某些情况下 --- 寻找最右数,会陷入死循环

例如[1,1] target = 1,取中点并与target比较后,区间范围依然是[1,1],没有缩小,导致代码超时。

4. 相关例题

leetcode: 509. 斐波那契数704. 二分查找34. 在排序数组中查找元素的第一个和最后一个位置剑指 Offer II 069. 山峰数组的顶部

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/916588
推荐阅读
相关标签
  

闽ICP备14008679号