剑指offer
链表题,遍历链表主要方法有递归和迭代,前两道均用到了,第三道不适用
递归有时候需要写一个新函数,似乎是在要求输出链表头节点时则写一个新函数
06. 从尾到头打印链表(简单)
解题方法:
- 递归:走到链表末端,回溯时将当前值依次加入数组中
- 辅助栈:由于从尾到头打印符合栈先进后出的特点,所以可以用一个辅助栈将遍历到的值依次压入栈中并倒序输出栈
24. 反转链表(简单)
重点:不管是迭代还是递归,都要用到上一个节点pre和当前节点cur
解题方法:
- 迭代——双指针:初始时pre指向null(即原链表最后一个节点的指向),cur指向第一个节点,用tmp暂存cur.next,然后把cur指向pre,最后pre向前移动一步(pre=cur),cur也向前移动一步(cur=tmp)
- 递归:
所有的递归问题都可以用递推公式来表示。递归代码:递推公式+终止条件
递归是一种关于某个重复动作(完成重复性的功能)的形式化描述。 如果一个问题 A 可以分解为若干子问题 B、C、D,假设子问题 B、C、D 已经解决,在此基础上思考如何解决问题 A。递归只能考虑当前层和下一层的关系,不能继续往下深入。
令F(node)为问题: 反转以node为头节点的单向链表;
考虑F(n)和F(n-1)的关系: 如果n代表以node为头节点的单向链表,那么n-1就代表以node.next为头节点的单向链表.
令F(node.next)为问题: 反转以node.next为头节点的单向链表;
F(node)和F(node.next)之间的关系:
假设反转3个节点的链表:1 -> 2 -> 3
那么,F(node=1)=F(node=2)+?(node=1用node.next表示,node=2用node.next.next表示,node是node=1的上一个节点)
假设子问题F(node=2)已经解决,如何解决F(node=1):
需要反转node=2和node=1,即node.next.next=node; 同时 node.next=null;
则F(node=1)=F(node=2)+反转node=2和node=1
递推代码:
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
# 反转单链表,返回值是原先单链表的尾结点(reverseList_head)
# 假设该递归函数实现一个功能:将头结点next所指结点的next指向头结点,将头结点的next指向空,返回反转后的头结点(原尾结点)
# base case
if not head or not head.next:
return head
# recursive case
reverseList_head = self.reverseList(head.next) # node:指除了head结点,将后面所有结点组成的链表看作一个node,作为head.next
# 假设递归已经解决,我们还需处理:
head.next.next = head # 反转node2:原先第二个结点的next指针指向head
head.next = None # 反转node1:原先head结点的next指针指向None
return reverseList_head
35. 复杂链表的复制(中等)
重点:解决random指向的问题
解法:
- 拼接+拆分:三次遍历链表,第一次复制新节点放在原节点之后,第二次把复制节点的random指向原节点的random的next,第三次把原链表和新链表拆分
- 利用哈希表,两次遍历链表,第一次构建键值对,键是原链表节点,值是新链表节点,第二次修改新节点next和random的指向,要注意赋值时用dic.get()
Leetcode75
都是字符串相关题目
205. 同构字符串(简单)
重点:两个数组中的字母是否满足一一对应,zip(s, t)的使用
解法:
- 两个哈希表判断s到t和t到s的映射关系
- return len(set(zip(s, t)))==len(set(s)):zip(s, t)形成键值对,set()去重
392. 判断子序列(简单)
解题方法:
- 双指针
- 动态规划(没有看太明白)