• 剑指offer刷题合集


    参考大神https://blog.csdn.net/zjulyx1993/article/details/108327108

    1.剑指 Offer 03. 数组中重复的数字(数组)

     1 """ 找出数组中重复的数字。
     2 
     3 在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
     4 2 <= n <= 100000
     5 
     6 示例 1:
     7 
     8 输入:
     9 [2, 3, 1, 0, 2, 5, 3]
    10 输出:2 或 3 
    11 
    12  """
    13 
    14 #集合法,依次将列表中的数放入集合,若已在集合中重复,则输出,时间复杂度O(n),空间复杂度O(n)
    15 def findRepeatNumber1(nums):
    16     d = set()
    17     for i in nums:
    18         if i not in d:
    19             d.add(i)
    20         else:
    21             return i
    22 
    23 #注意数字范围在0~n-1之间, 这说明每个数都可以放到等同于其自身值的下标中,时间复杂度O(n),空间复杂度降到O(1)
    24 def findRepeatNumber2(nums):
    25     for i in range(len(nums)):
    26         
    27         # for循环中每遍历一次位置i,会一直交换至nums[i]==i
    28         while i != nums[i]:
    29             # 当前下标i和值nums[i]不相等, 进入/继续内层循环,如果相等则说明此位置符合要求
    30             j = nums[i]
    31             if nums[i] == nums[j]:
    32                 # 前提条件: i!=j, 所以nums[i]和nums[j]是数组的两个数字, 它们值相等, 即为重复数字
    33                 return nums[i]
    34             # 交换两个值, 使得nums[j] == j, 这样可以继续循环判断i和新的nums[i]
    35             nums[i], nums[j] = nums[j], nums[i]
    36 
    37     return -1   #如果给定的数组没有重复值的话,返回-1

    2.剑指 Offer 04. 二维数组中的查找(数组)

     1 """ 在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
     2 
     3 示例:
     4 
     5 现有矩阵 matrix 如下:
     6 [
     7   [1,   4,  7, 11, 15],
     8   [2,   5,  8, 12, 19],
     9   [3,   6,  9, 16, 22],
    10   [10, 13, 14, 17, 24],
    11   [18, 21, 23, 26, 30]
    12 ]
    13 给定 target = 5,返回 true。 给定 target = 20,返回 false。
    14 
    15 限制:
    16 0 <= n <= 1000
    17 0 <= m <= 1000 
    18 """
    19 #初始化坐标为右上角, 然后判断当前点与 target 的关系, 大于的话列号减 1, 小于的话行号+1, 直到找到等于 target 的点, 或者超出矩阵范围
    20 #时间复杂度O(R+C),空间复杂度O(1)
    21 def findNumberIn2DArray(matrix, target):
    22     if not matrix:
    23             return False
    24     row, col = len(matrix), len(matrix[0])
    25     r, c = 0, col-1
    26     while r<row and c>-1:
    27         if target < matrix[r][c]:
    28             c -= 1
    29         elif target > matrix[r][c]:
    30             r += 1
    31         else:
    32             return True
    33     return False

    3.剑指 Offer 05. 替换空格(字符串)

     1 """ 请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
     2 
     3 示例 1:
     4 
     5 输入:s = "We are happy."
     6 输出:"We%20are%20happy."
     7  
     8 限制: 0 <= s 的长度 <= 10000 
     9 """
    10 
    11 def replaceSpace(s):
    12     # return s.replace(' ', '%20')
    13     return '%20'.join(s.split())

    4.剑指 Offer 06. 从尾到头打印链表(链表)

     1 """ 输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
     2 
     3 示例 1:
     4 
     5 输入:head = [1,3,2]
     6 输出:[2,3,1]
     7  
     8 限制:0 <= 链表长度 <= 10000 
     9 class Solution:
    10     def reversePrint(self, head: ListNode) -> List[int]:
    11 """
    12 
    13 # Definition for singly-linked list.
    14 class ListNode:
    15     def __init__(self, x):
    16         self.val = x
    17         self.next = None
    18 
    19 #先正向存储到数组,再翻转返回
    20 def reversePrint1(head: ListNode):
    21     lst = []
    22     while head:
    23         lst.append(head.val)
    24         head = head.next
    25     return lst[::-1]
    26 
    27 #使用递归
    28 def reversePrint2(head: ListNode):
    29     res = []
    30     
    31     def add(x: ListNode):
    32         if not x:           #递归出口
    33             return 
    34         add(x.next)
    35         res.append(x.val)
    36     
    37     add(head)
    38     return res
    39 
    40 #不能翻转,不能使用递归,只能用栈迭代
    41 def reversePrint3(head: ListNode):
    42     stack = []
    43     while head:
    44         stack.append(head.val)     #正向压入栈中
    45         head = head.next
    46     
    47     res = []
    48     while stack:
    49         res.append(stack.pop().val)    #再一个个弹出
    50     return res

    5.剑指 Offer 07. 重建二叉树(树)

     1 """ 输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
     2 
     3 例如,给出
     4 前序遍历 preorder = [3,9,20,15,7]
     5 中序遍历 inorder = [9,3,15,20,7]
     6 返回如下的二叉树:
     7 
     8     3
     9    / 
    10   9  20
    11     /  
    12    15   7
    13 
    14 class Solution:
    15     def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
    16  """
    17 
    18 # Definition for a binary tree node.
    19 class TreeNode:
    20     def __init__(self, x):
    21         self.val = x
    22         self.left = None
    23         self.right = None
    24 
    25        
    26 #递归法
    27 def buildTree1(preorder, inorder):
    28     if not preorder:
    29         return None
    30     
    31     valueToInorderIndex = {}        #使用一个value=>inorder index的字典加速运算, 为了使得查找中序下标的操作变为O(1), 不需要扫描整个中序数组
    32     n = len(preorder)
    33 
    34     for i in range(n):
    35         valueToInorderIndex[inorder[i]] = i
    36 
    37     def build(pb, pe, ib, ie):      #(pb, pe)是当前前序遍历的起点和终点 (ib, ie)是当前中序遍历的起点和终点
    38         if pb > pe:                 
    39             return None             #递归出口
    40         
    41         root = TreeNode(preorder[pb])         #前序遍历的当前第一个元素即为当前的根节点
    42         if pb == pe:
    43             return root
    44         
    45         im = valueToInorderIndex[root.val]    #根节点对应的中序遍历的下标,以此作为分界点
    46         pm = pb + im - ib                     #根节点对应的前序遍历的下标(对应部分的前序和中序长度应该相等, 即im-ib=pm-pb,由此得出)
    47 
    48         #前序[pb    ]pm[     pe]
    49         #中序[ib  ]im[       ie]
    50         root.left = build(pb + 1, pm, ib, im-1)         #左子树部分,前序(pb+1, pm),中序(ib, im-1)
    51         root.right = build(pm + 1, pe, im + 1, ie)      #右子树部分,前序(pm+1, pe),后序(im+1, ie)
    52 
    53         return root
    54     return build(0, n-1, 0, n-1)
    55 
    56 #迭代法
    57 def buildTree2(preorder, inorder):
    58     if not preorder:
    59         return None
    60 
    61     root = TreeNode(preorder[0])
    62     stack = [root]
    63     
    64     ii = 0                  #ii表示当前的中序下标
    65  
    66     for pi in range(1, len(preorder)):
    67         prenode = stack[-1]        #记录上一个栈顶节点,一定不为空
    68         curnode = TreeNode(preorder[pi])     #当前节点curnode位于上一节点的左子树或者右子树
    69 
    70         if prenode.val != inorder[ii]:    #上一个节点不是当前中序节点, 意味着现在还没到上一个节点的右边部分, 所以当前节点位于左子树,
    71             prenode.left = curnode
    72         else:                              #上一节点就是当前中序节点,意味着当前节点在右子树上
    73             while stack and ii < len(inorder) and stack[-1].val == inorder[ii]:
    74                 prenode = stack.pop()      #找最上层一个(也即倒置前序序列最后一个)与当前中序节点相同的节点
    75                 ii += 1
    76             prenode.right = curnode        #那个节点的右儿子就是当前节点
    77             
    78         stack.append(curnode)              #将当前节点加入stack中, 作为下次循环的上一个节点
    79 
    80     return root        

    6.剑指 Offer 09. 用两个栈实现队列(栈/队列)

     1 """ 用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1
     2 
     3 示例 1:
     4 输入:
     5 ["CQueue","appendTail","deleteHead","deleteHead"]
     6 [[],[3],[],[]]
     7 输出:[null,null,3,-1]
     8 
     9 示例 2:
    10 输入:
    11 ["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]
    12 [[],[],[5],[2],[],[]]
    13 输出:[null,-1,null,null,5,2]
    14 
    15 提示:1 <= values <= 10000,最多会对 appendTail、deleteHead 进行 10000 次调用 
    16 """
    17 # 两个队列stack1和stack2,队列尾部插入数据,即数据压入stack1,队列头部删除整数,判断三种情况
    18 # 1.第二个栈不为空,则从第二个栈中弹出数据   2.第一个栈不为空,则从第一个栈中把元素依次弹出并压入第二个栈中   3.第一个栈为空,即删除失败
    19 class CQueue:
    20     def __init__(self):
    21         self.stack1 = []    
    22         self.stack2 = []
    23 
    24     def appendTail(self, value: int):       #在队列尾部插入整数
    25         self.stack1.append(value)
    26 
    27     def deleteHead(self):                   #在队列头部删除整数
    28         if self.stack2:             #对应情况1
    29             return self.stack2.pop()
    30         if not self.stack1:         #对应情况3
    31             return -1
    32        
    33         while self.stack1:          #对应情况2
    34             x = self.stack1.pop()
    35             self.stack2.append(x)
    36         return self.stack2.pop()
    37 
    38 
    39 # Your CQueue object will be instantiated and called as such:
    40 # obj = CQueue()
    41 # obj.appendTail(value)
    42 # param_2 = obj.deleteHead()

    7.剑指 Offer 10- I. 斐波那契数列(动态规划)

     1 """ 写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
     2 F(0) = 0,   F(1) = 1
     3 F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
     4 斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
     5 
     6 答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
     7  """
     8 
     9 def fib(n):
    10     if n < 2:
    11         return n
    12 
    13     x, y, z = 0, 1, 1
    14     while n-2:
    15         x, y = y, z
    16         z = x + y
    17         n = n - 1
    18     return z % 1000000007
    19 
    20 #用数组更清晰
    21 def fib2(n):
    22     if n < 2:
    23         return n
    24 
    25     dp=[0]*(n+1)
    26     dp[1]=1
    27     for k in range(2, n+1):
    28         dp[k] = dp[k-1] + dp[k-2]
    29     return dp[-1]%1000000007

    8.剑指 Offer 10- II. 青蛙跳台阶问题(动态规划)

    本质和上一题一样

    9.剑指 Offer 11. 旋转数组的最小数字(二分查找)

     1 """ 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
     2 输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。
     3 例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。  
     4 
     5 输入:[3,4,5,1,2]
     6 输出:1
     7 
     8 输入:[2,2,2,0,1]
     9 输出:0 
    10 """
    11 #题目的意思是数组有两段递增序列,找分段点,如果只有一段递增序列,那就是开头
    12 #二分查找
    13 def minArray(numbers):
    14     n = len(numbers)
    15     i, j = 0, n-1
    16     while i < j:
    17         mid = (i + j) // 2
    18         if numbers[mid] < numbers[j]:    #如果中间值小于末尾, 那么一定说明该数字之后(后半段)有序.
    19             j = mid
    20         elif numbers[mid] > numbers[j]:  #如果中间值大于末尾, 那么毫无疑问后半段无序.
    21             i = mid + 1
    22         else:                          #如果中间值等于末尾, 那就不好判断是前半段无序还是后半段无序,退化为逐个遍历
    23             j -= 1
    24     return numbers[i]

    类似的还有面试题

    面试题 10.03. 搜索旋转数组

     1 """ 搜索旋转数组。给定一个排序后的数组,包含n个整数,但这个数组已被旋转过很多次了,次数不详。
     2 请编写代码找出数组中的某个元素,假设数组元素原先是按升序排列的。若有多个相同元素,返回索引值最小的一个
     3 
     4 输入: arr = [15, 16, 19, 20, 25, 1, 3, 4, 5, 7, 10, 14], target = 5
     5 输出: 8(元素5在该数组中的索引) 
     6 """
     7 
     8 #理解一下:旋转一次和旋转多次没有任何区别, 最终还是只有一个旋转点, 以及不多于 2 个的有序区间
     9 def search(arr, target):
    10     left, right = 0, len(arr) - 1
    11     while left <= right:
    12 
    13         mid = (left + right) // 2
    14         if arr[left] == target:
    15             return left
    16         elif arr[mid] == target:         #mid和right值等于target时,未必是索引值最小的,还要继续遍历
    17             right = mid
    18         elif arr[right] == target:
    19             left = mid + 1
    20         elif arr[mid] < arr[right]:             #后半段有序
    21             if arr[mid] < target < arr[right]:    #target在后半段里
    22                 left = mid + 1
    23             else:                                 #target在前半段里
    24                 right = mid - 1
    25         elif arr[mid] > arr[right]:             #前半段有序
    26             if arr[left] < target < arr[mid]:     #target在前半段里
    27                 right = mid - 1
    28             else:                                 #target在后半段里
    29                 left = mid + 1
    30         else:                           #前后段谁有序不清楚,逐一遍历
    31             right -= 1
    32     return -1 

    10.剑指 Offer 12. 矩阵中的路径(回溯法)

     1 """ 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
     2 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
     3 
     4 输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
     5 输出:true
     6 
     7 输入:board = [["a","b"],["c","d"]], word = "abcd"
     8 输出:false
     9 
    10 """
    11 
    12 #直接修改board记录每个点是否被访问过
    13 def exist(board, word):               #题干强调了同一个单元格内的字母不允许被重复使用
    14   
    15     def dfs(i, j, k):
    16         if not 0<= i < len(board) or not 0<= j < len(board[0]) or board[i][j] != word[k]:
    17             return False
    18         if k == len(word) - 1:
    19             return True
    20         board[i][j] = ' '
    21         res = dfs(i+1, j, k+1) or dfs(i-1, j, k+1) or dfs(i, j+1, k+1) or dfs(i, j-1, k+1)
    22         board[i][j] = word[i][j]
    23         return res
    24     
    25     for i in range(len(board)):
    26         for j in range(len(board[0])):
    27             if dfs(i, j, 0):
    28                 return True
    29     return False       
    30 
    31 #使用集合visited(C++中使用矩阵)记录每个点是否被访问过
    32 def exist2(board, word):   
    33          
    34     def dfs(i, j, k, visited):
    35         if not 0<= i < len(board) or not 0<= j < len(board[0]) or board[i][j] != word[k] or (i,j) in visited:
    36             return False
    37         if k == len(word) - 1:
    38             return True
    39         visited.add((i,j))
    40         res = dfs(i-1, j, k+1, visited) or dfs(i+1, j, k+1, visited) or dfs(i, j-1, k+1, visited) or dfs(i, j+1, k+1, visited)
    41         visited.remove((i, j))
    42         return res
    43 
    44     for i in range(len(board)):
    45         for j in range(len(board[0])):
    46             visited = set()
    47             if dfs(i, j, 0, visited):
    48                 return True
    49     return False
  • 相关阅读:
    WIF基本原理(4)联合身份验证实例
    Open XML应用安全(4)文档校验
    WIF基本原理(5)WIF的功能简介
    Open XML应用安全(3)隐藏数据
    WIF基本原理(3)安全令牌服务
    Open XML应用安全(5)数字签名
    Open XML应用安全(1)宏安全
    WIF基本原理(2)基于声明的标识模型
    搭建基于Android和PhoneGap的开发环境
    定位flash上传出现IO Error #2038的错误
  • 原文地址:https://www.cnblogs.com/cxq1126/p/14488205.html
Copyright © 2020-2023  润新知