(一)相关原理

1.减治思想:

​ 在拆分子问题的时候,只将原问题转化成 一个 规模更小的子问题,因此子问题的结果就是上一层原问题的结果,每一步只需要解决一个规模更小的子问题,相比较于「分治思想」而言,它 没有「合并」的过程。

​ 「减治思想」思想的典型应用是「二分查找」「选择排序」「插入排序」「快速排序」算法。

2.递归与迭代

2.1「自顶向下」与「递归」

​ 「自顶向下」是直接面对我们要解决的问题,逐层拆分,直到不能拆分为止,再按照拆分的顺序的逆序逐层解决,直至原问题得到了解决,这是「递归」。

2.2「自底向上」与「迭代」

​ 如果我们非常清楚一个问题最开始的样子,并且也清楚一个问题是如何从它最开始的样子逐步演变成为我们想要求解的问题的样子,我们就可以通过「递推」的方式,从小规模的问题开始逐步「递推」得到最终要解决的大问题的解。

(很关键一点是,使用迭代必须要 能正确且全面地抽取出其实行逻辑,否则的话可能会出现逻辑不完善,边界条件不能完全考虑到等问题,可能会需要很多条件判断等步骤,但是想起来可能更加符合人思考的逻辑。 )

(二)关键例题

1.归并排序

​ 使用了减治的思想,通过递归地二分操作来进行处理,首先递归地将数组一分为二,然后没有其他操作直到触底,但是在回溯的过程中,需要进行一个逐个检验并合并数组的操作(这个过程实际是自底向上的),从数据形式上,传入的始终都是数组形式。

def mergeSort(self,alist):#触底条件if len(alist)<=1:return alist#递归主体 (基于下行过程中的二分操作,只是将它改成了递归式,从而可以在回溯的过程中进行后面写的那个操作)mid=len(alist)//2 #这里思考的时候一开始想到要分奇偶,而且每隔一个都会奇偶交替,但实际模拟一下发现并不需要分,毕竟就算不等分也没关系,等分并不是硬性要求,最后都能顺利触底就好left=self.mergeSort(alist[:alist])right=self.mergeSort(alist[alist:])#回溯过程中的操作merged=[]while left and right:if left[0]<=right[0]: #这里一定程度上可以认为是用了双指针(但它仅仅是用了两个指针而已)merged.append(left.pop(0)) #非常pythonic,所以不会真实地用到类似指针的东西else:merged.append(right.pop(0))merged.extend(left if left else right)return merged

2.快速排序

①自己想的常方法(要用到额外空间)

​ 也是使用了减治的思想,只是之前是按照序号进行二分,这次选择的是一个数字,然后按照数值大小来排,也是一种可实现的方法,递归实现。

def quickSort(self,alist):#触底条件(递归结束条件)if len(alist)<=1:return alist#写出每一次顺利二分的操作a=alist[0] #选择参考数值为最左侧数据l=[]l.append(a)cnt=0for i in alist[1:]:if i<a:l.insert(0,i)cnt+=1else:l.append(i)return l #这样可以得到一个成功左都更小右都更大的“进步了的”数组#写递归主体(基于上行过程中的合并操作)left=self.quickSort(alist[:cnt+1]) #这里必须要是cnt+1,之所以+1是因为一定要把参考数据划分到左边去作为最右元素,不然它要是在右侧,就又会变成右半部分的参考元素,但是事实上已经不需要了,我一开始只是觉得它重复作为参考元素没有意义,但我仔细一想,重复作为参考元素会导致右半边无法顺利递归下去,大问题,所以必须得注意。right=self.quickSort(alist[cnt+1:])return left.extend(right)
②双指针(对撞指针)(不需要额外空间)
def quickSort(self,alist):#触底条件(递归结束条件)if len(alist)<=1:return alist#写出每一次顺利二分的操作a=alist[0]lefthand=1righthand=len(alist)-1done=Falsewhile not done:while lefthand<=righthand and alist[lefthand]<=a:lefthand+=1while lefthand<=righthand and alist[righthand]>=a:righthand-=1if lefthand>righthand:done=Trueelse:temp=alist[lefthand]alist[lefthand]=alist[righthand]alist[righthand]=temptemp=alist[righthand]alist[righthand]=alist[0]alist[0]=tempreturn alist#写递归主体self.quickSort(alist[:righthand])self.quickSort(alist[righthand+1:])#这边要return吗

3.二分查找

3.1 二分查找

①自己写的递归:
class Solution(object):def search(self, nums, target):""":type nums: List[int]:type target: int:rtype: int"""#递归结束条件if len(nums)==0:return -1if len(nums)==1 and nums[0]!=target:return -1#内部实现逻辑+递归主体mid=len(nums)//2if nums[mid]==target:return midelif nums[mid]>target:return self.search(nums[:mid],target)else:res=self.search(nums[mid+1:],target)if res==-1:return reselse:return mid+1+res
②迭代(使用双指针)
class Solution(object):def search(self, nums, target):""":type nums: List[int]:type target: int:rtype: int"""if len(nums)==0:return -1left=0right=len(nums)-1while left<=right: #必须是带=,不然只剩两个并且是右边那个的时候会漏掉mid=(left+right)/2if nums[mid]==target:return midelif nums[mid]>target:right=mid-1else:left=mid+1return -1
3.2类似的题:x的平方根
①迭代的二分查找(用到双指针)
class Solution(object):def mySqrt(self, x):""":type x: int:rtype: int"""left=1right=xmid=(left+right)//2while left<=right:if mid**2==x:return midelif mid**2<x:left=mid+1else:right=mid-1return right

​ 因为它不是列表,所以不能直接切片之类的操作,所以即使是递归也是需要用到双指针的。但是我想了一下,好像因为这个返回值就是它的根,没办法用递归。

②牛顿迭代法

​ 有时间再说吧。

3.3 可以深入理解二分的题:搜索旋转排序数组

① 自己写的:分步法(使用了递归)

​ 整体思路大概是:1.找到两个有序的分界处(使用普通的迭代法);2.最后一个元素是分界元素,可以通过比较最后一个元素和target的大小来判断是在哪半个序列里; 3.在确定的序列中再进行常规的二分查找。

​ 整体将近50行,且在分界取切片和在其中二分查找的接口处容易出现很多忽略的边界情况(也直接导致提交了好几次都不对,调试了半个多小时才顺利通过。)

class Solution(object):#常规的二分查找def s(self,nums,target):if len(nums)==0:return -1if len(nums)==1 and nums[0]!=target:return -1mid=len(nums)//2if target==nums[mid]:return midelif target<nums[mid]:return self.s(nums[:mid],target)else:r=self.s(nums[mid+1:],target)if r==-1:return relse:return mid+1+r#主体函数def search(self, nums, target):""":type nums: List[int]:type target: int:rtype: int"""if nums==[]:return -1if len(nums)==1:return 0 if nums[0]==target else -1cur=0l=len(nums)while cur<l-1:if nums[cur]<=nums[cur+1]:cur+=1else:breakif cur==l-1:return self.s(nums, target)cur+=1a=nums[-1]if target>a:return self.s(nums[:cur], target)else:#return cur+self.s(nums[cur:],target)res=self.s(nums[cur:],target)if res==-1:return reselse:return cur+res
②官方题解:二分变体【迭代】

​ 这个一方面可以理解为把那个寻找断点的操作集成到了二分里面,不过这也证明了二分所要求的“有序”,其实也没有那么死板;另一方面可以理解为二分的变种进化,有一种递归的感觉,但是答案使用的是迭代,不一定非要有序,只要最后最底层的那个是有序的就行。

class Solution(object):def search(self, nums, target):""":type nums: List[int]:type target: int:rtype: int"""left=0right=len(nums)-1while left<=right:mid=(left+right)//2if nums[mid]==target:return midif nums[left]<=nums[mid]:if nums[left]<=target<=nums[mid]:right=mid-1else:left=mid+1else:if nums[mid]<=target<=nums[right]:left=mid+1else:right=mid-1return -1
③自己仿写的:二分变体【递归】
class Solution(object):def search(self, nums, target):""":type nums: List[int]:type target: int:rtype: int"""if not nums:return -1mid=len(nums)//2if nums[mid]==target:return midif len(nums)==1 and nums[0]!=target:return -1if nums[mid]>=nums[0]:if nums[0]<=target<nums[mid]:return self.search(nums[:mid],target)else:res=self.search(nums[mid+1:],target)if res==-1:return reselse:return mid+1+resif nums[mid]<=nums[-1]:if nums[mid]<target<=nums[-1]:res=self.search(nums[mid+1:],target)if res==-1:return reselse:return mid+1+res               else:return self.search(nums[:mid],target)
3.4 整理几个关键点!!
  1. 当迭代时,用的就是while left<=right,使用双指针这样循环;当递归时,就是以下面那个作为终止条件:

    if len(nums)==0:return -1
    if len(nums)==1 and nums[0]!=target:return -1
    
  2. 用到递归时,如果要返回下标,涉及右半部分的时候要注意判断并加上上一步的mid(即得到的是相对坐标,要加上参考系坐标),可以使用如下方式:

    res=self.search(nums[mid+1:],target)
    if res==-1:return res
    else:return mid+1+res
    

【leetcode记录02】递归相关推荐

  1. LeetCode记录总结

    LeetCode记录总结 本文章主要记录LeetCode刷题学到的知识 242.Valid Anagram 题目: Given two strings s and t , write a functi ...

  2. LeetCode——树:递归

    LeetCode--树:递归 目录 概述 树的高度(LeetCode104) 平衡树(LeetCode110) 两节点的最长路径(LeetCode543) 翻转树(LeetCode226) 归并两棵树 ...

  3. Unity3D游戏制作学习记录02——丛林战争

    Unity3D游戏制作学习记录02--丛林战争 Siki学院的视频教程指路牌:http://www.sikiedu.com/course/61. 一.服务器端--消息接收的异步处理 由于之前使用Rec ...

  4. Java学习记录02

    Java学习记录02 在学习中,博客的书写不可缺少,相应的markdown基本语法也要掌握,下面是自己在CSDN上查找并学习的链接:Markdown语法 2.6项目实训:基于控制台设计简易打折与累加计 ...

  5. LeetCode刷题记录02——新手村专辑(一)

    新手村专辑 题目 1480一维数组的动态和 我的思路 我的代码 提交结果 代码优化 总结 383赎金信 我的思路 我的代码 提交结果 代码优化 总结 题目 1480一维数组的动态和 1480一维数组的 ...

  6. LeetCode Additive Number(递归)

    问题:给出一个字符串,问组成它的数字是否可以形成累加序列.序列至少包含32上数,除了最开始的两个数外,字符串中的其他数等于前两个数之和.字符串只包含0-9,同时数字不能以0开头. 思路:先检测前两个数 ...

  7. leetcode记录汇总

    时隔几个月,发现之前做过的题很多都忘的一干二净,所以决定记录一下经典题目的做题思路,方便复习和复盘- . . . 开始日期:2021.Feb.24 Date Title 2.24 最长递增子序列(中等 ...

  8. 瑞萨开发记录02:定时器闪烁LED灯(R5F104FEA芯片)

    02.定时器闪烁lED灯 一.引脚配置 1.配置LED 2.配置定时器 二.编写代码 1.定时器定时 2.LED引脚定义 3.主函数 三.烧录程序   上一篇博客记录了点亮一颗LED灯以及工程文件的配 ...

  9. 5000并发的qps是多少_高并发初体验记录-02

    前言 这半个多月完成了这次活动的业务代码开发和测试,至于性能调优对我而言近乎玄学.总结记录,以备参考.如有错误,欢迎指正.不过其实大佬不用浪费时间看这篇低质量的流水账了. 实测过后,一个月前老板给我说 ...

最新文章

  1. i5 10400f相当于几代i7_十代酷睿i9/i7/i5差异惊人!功耗/温度放飞自我
  2. 浙大开源 | VID-Fusion: 用于精确外力估计的鲁棒视觉惯性动力学里程计
  3. zz SOA推荐书籍列表
  4. 全文搜索引擎 Elasticsearch 简介 及其与 Python 的对接实现
  5. JQuery中each()的使用方法说明
  6. 如何用极致业务基础平台做一个通用企业ERP系列之二一览表培训
  7. 对象与内存控制1---实例变量和类变量
  8. linux---基础02
  9. next和hasnext_使用Java中的next()和hasNext()方法遍历List元素
  10. Python 项目实践三(Web应用程序)第一篇
  11. 数据抽取工具——DMCTextFilter V4.2(纯文本抽出通用程序库)
  12. paip.验证码识别---除噪算法-中值滤波
  13. python怎么做成app_自己怎么做App软件 自己制作APP教程
  14. bin文件夹下的roslyn文件夹
  15. 数据库中的主键、超键、候选键、外键
  16. 计算机中解决不匹配,电脑显示屏显示不匹配.怎么办
  17. 【报错】进程已结束,退出代码-1073740791 (0xC0000409)
  18. Nginx架构四之七层负载均衡
  19. 软键盘遮挡edittext_Android软键盘遮挡的四种解决方案
  20. day20 网络编程(上)

热门文章

  1. 安装oracle后,电脑变卡变慢的解决办法
  2. UWP x:bind
  3. Hyper-V Server共享VHDX
  4. Eclipse文件夹导入Jar
  5. HTTP协议容易犯的误区
  6. IT英语职场之网管英语大全
  7. swagger2注解说明文档
  8. 【置顶】方立勋JavaWeb学习地址
  9. 转 Java中final、finally、finalize的区别与用法
  10. python 学习笔记(十二) 文件和序列化