剑指offer思路整理(个人向)17-29

剑指17.打印从1到最大的n位数

输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。

示例 1:

输入: n = 1
输出: [1,2,3,4,5,6,7,8,9]

1.作为一个傻瓜,这道题可以说是过于简单了,一行代码搞定。

class Solution:
    def printNumbers(self, n: int) -> List[int]:
        return list(range(1,10**n))

2.果然傻瓜终究是傻瓜,这道题还有一个隐藏考点,那就是大数问题。考虑大数的话,就只能用字符串来做,n位字符,每隔字符取值0-9,通过dfs得到可能的取值。如何删除高位0是关键,其实就是设置一个起始位start,一开始是n-1,也就是只有最后一位,当低位全是9(n - self.start == self.nine)的时候,start前移,以此来解决高位0的问题。

class Solution:
    def printNumbers(self, n: int) -> [int]:
        result = []
        temp = []
        self.start = n-1
        self.nine = 0
        def recursion(nth):
            if nth == n:
                may = "".join(temp[self.start:])
                if may != '0':result.append(int(may))
                if n - self.start == self.nine: self.start -= 1
                return
            for num in range(10):
                if num == 9: self.nine += 1
                temp.append(str(num))
                nth += 1
                recursion(nth)
                nth -= 1
                temp.pop()
            self.nine -= 1

        recursion(0)
        return result

自我反思: 大数问题感觉真的很关键,在笔试和面试中,都必须考虑大数的情况。

剑指18.删除链表中的节点

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。
注意:此题对比原题有改动

示例 1:

输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.

1.思路灰常简单,就是遍历这个链表,找到该节点,把前一个节点连到下一个节点就好了。

class Solution:
    def deleteNode(self, head: ListNode, val: int) -> ListNode:
        start = head
        if head.val == val:
            return head.next
        while head.next:
            if head.next.val == val:
                head.next = head.next.next
            head = head.next
            if not head:
                break
        return start

2.(十分重要)但是,个人建议还是得养成使用双指针pre,cur的习惯,这样链表相关的题思路会更清晰。

class Solution:
    def deleteNode(self, head: ListNode, val: int) -> ListNode:
        if head.val == val: return head.next
        pre, cur = head, head.next
        while cur and cur.val != val:
            pre, cur = cur, cur.next
        pre.next = cur.next
        return head

**自我反思:**好的代码习惯和思路习惯,真的非常重要。

剑指19.正则表达式匹配

请实现一个函数用来匹配包含’. ‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但与"aa.a"和"ab*a"均不匹配。

示例 1:

输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。

1.动态规划
状态定义: 设动态规划矩阵 dp , dp[i][j] 代表字符串 s 的前 i 个字符和 p 的前 j 个字符能否匹配。

转移方程: 需要注意,由于 dp[0][0] 代表的是空字符的状态, 因此 dp[i][j] 对应的添加字符是 s[i - 1] 和 p[j - 1] 。

当 p[j - 1] = ‘*’ 时, dp[i][j] 在当以下任一情况为 true 时等于 true :
dp[i][j - 2]: 即将字符组合 p[j - 2] * 看作出现 0 次时,能否匹配;
dp[i - 1][j] 且 s[i - 1] = p[j - 2]: 即让字符 p[j - 2] 多出现 1 次时,能否匹配;
dp[i - 1][j] 且 p[j - 2] = ‘.’: 即让字符 ‘.’ 多出现 1 次时,能否匹配;

当 p[j - 1] != ‘*’ 时, dp[i][j] 在当以下任一情况为 true 时等于 true :
dp[i - 1][j - 1] 且 s[i - 1] = p[j - 1]: 即让字符 p[j - 1] 多出现一次时,能否匹配;
dp[i - 1][j - 1] 且 p[j - 1] = ‘.’: 即将字符 . 看作字符 s[i - 1] 时,能否匹配;

初始化: 需要先初始化 dp 矩阵首行,以避免状态转移时索引越界。
dp[0][0] = true: 代表两个空字符串能够匹配。
dp[0][j] = dp[0][j - 2] 且 p[j - 1] = ‘*’: 首行 s 为空字符串,因此当 p 的偶数位为 * 时才能够匹配(即让 p 的奇数位出现 0 次,保持 p 是空字符串)。因此,循环遍历字符串 p ,步长为 2(即只看偶数位)。
返回值: dp 矩阵右下角字符,代表字符串 s 和 p 能否匹配。

class Solution:
    def isMatch(self, s: str, p: str) -> bool:
        m = len(s)+1
        n = len(p)+1
        matrix = [[False] * n for _ in range(m)]
        matrix[0][0] = True
        for c in range(2,n,2):
            if p[c-1] == '*':
                matrix[0][c] = matrix[0][c-2]
        for r in range(1,m):
            for c in range(1,n):
                if p[c-1] == '*':
                    matrix[r][c] = matrix[r][c-2] or (matrix[r-1][c] and (s[r-1] == p[c-2] or p[c-2] == '.'))
                else:
                    matrix[r][c] = matrix[r-1][c-1] and (s[r-1]==p[c-1] or p[c-1]=='.')
        return matrix[-1][-1]

**自我反思:**二刷这道题,依然感觉十分吃力,要么多想,要么少想。K神的解法确实值得反复琢磨和回顾。

剑指20.表示数值的字符串

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100"、“5e2”、"-123"、“3.1416”、"-1E-16"、“0123"都表示数值,但"12e”、“1a3.14”、“1.2.3”、"±5"及"12e+5.4"都不是。
1.这道题的关键就是画出有限状态机,先写出一个可能的最复杂的数字-12.3e-16,然后根据这个数字来考虑状态转移。
在这里插入图片描述

class Solution:
    def isNumber(self, s: str) -> bool:
        states = [
            { ' ': 0, 's': 1, 'd': 2, '.': 4 }, # 0. start with 'blank'
            { 'd': 2, '.': 4 } ,                # 1. 'sign' before 'e'
            { 'd': 2, '.': 3, 'e': 5, ' ': 8 }, # 2. 'digit' before 'dot'
            { 'd': 3, 'e': 5, ' ': 8 },         # 3. 'digit' after 'dot'
            { 'd': 3 },                         # 4. 'digit' after 'dot' (‘blank’ before 'dot')
            { 's': 6, 'd': 7 },                 # 5. 'e'
            { 'd': 7 },                         # 6. 'sign' after 'e'
            { 'd': 7, ' ': 8 },                 # 7. 'digit' after 'e'
            { ' ': 8 }                          # 8. end with 'blank'
        ]
        p = 0                           # start with state 0
        for c in s:
            if '0' <= c <= '9': t = 'd' # digit
            elif c in "+-": t = 's'     # sign
            elif c in "eE": t = 'e'     # e or E
            elif c in ". ": t = c       # dot, blank
            else: t = '?'               # unknown
            if t not in states[p]: return False
            p = states[p][t]
        return p in (2, 3, 7, 8)

剑指21.调整数组顺序使奇数位于偶数前面

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。

示例:

输入:nums = [1,2,3,4]
输出:[1,3,2,4] 
注:[3,1,2,4] 也是正确的答案之一。

1.傻瓜办法,定义了两个空数组,然后遍历原数组,奇数和偶数分别放在两个数组里,然后返回两个数组的和

class Solution:
    def exchange(self, nums: List[int]) -> List[int]:
        odd = []
        even = []
        for number in nums:
            if number%2==1:
                odd.append(number)
            else:
                even.append(number)

        return odd+even

2.双指针双指针,总是忘了双指针!!!

class Solution:
    def exchange(self, nums: List[int]) -> List[int]:
        i, j = 0, len(nums)-1
        while i < j:
            while i < j and nums[i] & 1 == 1: i+=1
            while i < j and nums[j] & 1 == 0: j-=1
            nums[i], nums[j] = nums[j], nums[i]
        return nums

**自我反思:**感觉还是思维习惯不好,要尝试用机器的思维去解题。

剑指22.链表中倒数第k个节点

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

示例:

给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.

1.快慢指针,快指针提前k位出发,那么快指针到达链表尾的时候,慢指针的位置就是答案。

class Solution:
    def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
        temp1 = head
        temp2 = head
        while temp1:
            if k > 0:
                temp1 = temp1.next
                k = k - 1
            else:
                temp1 = temp1.next
                temp2 = temp2.next
        return temp2

**自我反思:**对于数据结构,养成良好的思维习惯和代码规范是很重要的。

剑指24.反转链表

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

1.依然是双指针,pre和cur的故事

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        pre, cur = None, head
        while cur:
            temp = cur.next
            cur.next = pre
            pre = cur
            cur = temp
        return pre

**自我反思:**这类简单的基础题已经要熟练掌握,做到又快又好

剑指25.合并两个排序的链表

输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。

示例1:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

1.捞中之捞的算法,建了一个新的链表,然后遍历两个原链表,把小的值往里放。

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        head = ListNode(0)
        start = head
        while l1 or l2:
            if l1 == None:
                head.next = l2
                break
            if l2 == None:
                head.next = l1
                break
            if l1.val <= l2.val:
                head.next = ListNode(l1.val)
                l1 = l1.next
                head = head.next
            else:
                head.next = ListNode(l2.val)
                l2 = l2.next
                head = head.next
        return start.next

2.代码简化

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        head = ListNode(0)
        start = head
        while l1 and l2:
            if l1.val <= l2.val:
                start.next = l1
                l1 = l1.next
            else:
                start.next = l2
                l2 = l2.next
            start = start.next
        start.next = l1 if l1 else l2
        return head.next

**自我反思:**注意优化代码细节,这道题递归应该也是可以的,但是运行效率可能低一点。

剑指26.数的字结构

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。

例如:
给定的树 A:

     3
    / \
   4   5
  / \
 1   2

给定的树 B:

   4 
  /
 1

返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。

示例 1:

输入:A = [1,2,3], B = [3,1]
输出:false

1.十分冗长的菜鸡解法,定义了两个递归函数,一个函数去递归A,找到入口。另一个函数递归A和B,B是不是A的子结构。

class Solution:
    def isSubStructure(self, A: TreeNode, B: TreeNode) -> bool:
        self.result = False
        if B == None:
            return self.result
        def ispartof(node1,node2):
            if node2 == None:
                return True
            if node1 == None:
                return False
            if node1.val == node2.val:
                return ispartof(node1.left,node2.left) and ispartof(node1.right,node2.right)
            else:
                return False
        def recursion(node1):
            if node1 == None:
                return 
            if node1.val == B.val:
                if ispartof(node1,B):
                    self.result = True
            recursion(node1.left)
            recursion(node1.right)
        recursion(A)
        return self.result

2.K神的简化版本,说实话,这代码我真写不出来,还是老老实实按上面的写吧,其实思想是一样的,都是双递归。

class Solution:
    def isSubStructure(self, A: TreeNode, B: TreeNode) -> bool:
        def recur(A, B):
            if not B: return True
            if not A or A.val != B.val: return False
            return recur(A.left, B.left) and recur(A.right, B.right)

        return bool(A and B) and (recur(A, B) or self.isSubStructure(A.left, B) or self.isSubStructure(A.right, B))

**自我反思:**大道至简,学会代码的优化很重要,但是现在还做不来。

剑指27.二叉树的镜像

请完成一个函数,输入一个二叉树,该函数输出它的镜像。

例如输入:

     4
   /   \
  2     7
 / \   / \
1   3 6   9
镜像输出:

     4
   /   \
  7     2
 / \   / \
9   6 3   1

示例 1:

输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]

1.就是递归,然后把左右子树互换就可以了

class Solution:
    def mirrorTree(self, root: TreeNode) -> TreeNode:
        def recursion(root):
            if root == None:
                return 
            temp = root.left
            root.left = root.right
            root.right = temp
            recursion(root.left)
            recursion(root.right)
        recursion(root)
        return root

2.用栈(但是入栈出栈,好像效率不高)

class Solution:
    def mirrorTree(self, root: TreeNode) -> TreeNode:
        if not root: return
        stack = [root]
        while stack:
            node = stack.pop()
            if node.left: stack.append(node.left)
            if node.right: stack.append(node.right)
            node.left, node.right = node.right, node.left
        return root

**自我反思:**其实递归和栈也就对应了dfs和bfs

剑指28.对称的二叉树

请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

    1
   / \
  2   2
 / \ / \
3  4 4  3

但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

    1
   / \
  2   2
   \   \
   3    3

示例 1:

输入:root = [1,2,2,3,4,4,3]
输出:true

1.对root进行镜面递归,判断是否相等

class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        def recursion(root1,root2):
            if root1 == None and root2 == None:
                return True
            if root1 == None or root2 == None:
                return False
            if root1.val == root2.val:
                return recursion(root1.left,root2.right) and recursion(root1.right,root2.left)
            else:
                return False
        return recursion(root,root)

**自我反思:**其实很简单,但我记得刚开始做的时候,没有想着用镜面递归,而是想把值取出来判断数组是否回文,十分傻叉。

剑指29.顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。

示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]

1.再一次被我的长度震惊了,大致思路就是顺时针打印矩阵边缘,然后不断缩小边界。就是代码有点过于冗长了。

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        result = []
        if matrix == []:
            return result
        start_c,start_r = 0, 0
        end_c,end_r = len(matrix[0])-1, len(matrix)-1
        num = 0
        sumof = (end_c+1)*(end_r+1)
        while start_c<=end_c and start_r<=end_r:
            for c in range(start_c,end_c):
                result.append(matrix[start_r][c])
                num += 1
            for r in range(start_r,end_r):
                result.append(matrix[r][end_c])
                num += 1
            for c in range(end_c,start_c,-1):
                result.append(matrix[end_r][c])
                num += 1
            for r in range(end_r,start_r,-1):
                result.append(matrix[r][start_c])
                num += 1
            start_c += 1
            start_r += 1
            end_c -= 1
            end_r -= 1
        if num == sumof - 1:
            result.append(matrix[start_r-1][start_c-1])
        return result[:sumof]

**自我反思:**代码还是不够清晰和精简,感觉面试或者笔试很容易卡bug

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值