剑指offer刷题碎碎念

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

返回数组中重复出现的任意数字。

对于 Python 可以使用 dict 或 set 进行解体, 因为 Python 的 dictset 都是使用 hash 表实现的所以查找操作都是 O(1)的时间复杂度。
但是, 如果想使用 list 进行解题, 则不应该使用 in 操作符, 因为 Python list 使用顺序表实现, 所以其 in 的时间复杂度为 O(n).

class Solution(object):
    def findRepeatNumber(self, nums):
        table = set()
        for i in xrange(len(nums)):
            if nums[i] in table: # set可用in O(1)
                return nums[i]
            else: 
                table.add(nums[i])
def findRepeatNumber(self, nums):
        array = [0] * len(nums)
        for n in nums:
            if array[n] > 0: # list只能用赋值并判断大小,不能 in
                return n
            else: 
                array[n] += 1

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

在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

标志数引入: 此类矩阵中左下角和右上角元素有特殊性,称为标志数。
左下角元素: 为所在列最大元素,所在行最小元素。
右上角元素: 为所在行最大元素,所在列最小元素。
将 matrix 中的左下角元素(标志数)记作 flag ,则有:

  • 若 flag > target ,则 target 一定在 flag 所在行的上方,即 flag 所在行可被消去。
  • 若 flag < target ,则 target 一定在 flag 所在列的右方,即 flag 所在列可被消去。
def findNumberIn2DArray(self, matrix: List[List[int]], target: int) -> bool:
        i, j = len(matrix) - 1, 0   # 从矩阵左下角元素开始遍历
        while i >= 0 and j < len(matrix[0]):
            if matrix[i][j] > target:   # target一定在上方
                i -= 1
            elif matrix[i][j] < target: # target一定在右边
                j += 1
            else:   # matrix[i][j] == target
                return True
        return False

剑指 Offer 05. 替换空格(简单)

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

# 44 ms, 13.6 MB
def replaceSpace(self, s: str) -> str:
        sList = s.split(" ")
        print(sList)
        s_new = sList[0]
        for i in range(1, len(sList)):
            s_new += ("%20")
            s_new += (sList[i])
        return s_new
# 36 ms, 13.4 MB
def replaceSpace(self, s: str) -> str:
        res = []
        for c in s:
            if c == ' ':
                res.append("%20")
            else:
                res.append(c)
        return "".join(res)

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

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

  1. 反转
def reversePrint(self, head: ListNode) -> List[int]:
        res = []
        while head:
            res.append(head.val)
            head = head.next
        return res[::-1]
  • [::-1] 用来反转list。
  1. 递归
def reversePrint(self, head: ListNode) -> List[int]:
        if not head:
            return []
        return self.reversePrint(head.next) + [head.val]
  1. 辅助栈
  • 入栈: 遍历链表,将各节点值 push 入栈。(Python​ 使用 append() 方法,​Java​借助 LinkedList 的addLast()方法)。
  • 出栈: 将各节点值 pop 出栈,存储于数组并返回。
def reversePrint(self, head: ListNode) -> List[int]:
        stack = []
        while head:
            stack.append(head.val)
            head = head.next
        res = []
        while stack:
            res.append(stack.pop())
        return res

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

输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

什么序遍历说针对根节点的:

  • 前序遍历顺序:根节点–左子树–右子树
  • 中序遍历顺序:左子树–根节点–右子树
  • 后序遍历顺序:左子树–右子树–根节点
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        self.dic, self.po = {}, preorder
        for i in range(len(inorder)):   # 用dic储存中序遍历的值和索引
            self.dic[inorder[i]] = i
        # dic:{9: 0, 3: 1, 15: 2, 20: 3, 7: 4}
        return self.recur(0, 0, len(inorder) - 1)

    # pre_root:前序遍历根节点index;in_left:中序遍历左边界index;in_right:中序遍历右边界index
    def recur(self, pre_root, in_left, in_right):   # 终止条件:中序遍历为空
        if in_left > in_right:
            return 
        root = TreeNode(self.po[pre_root])  # 前序遍历第一个node是根节点
        i = self.dic[self.po[pre_root]] # 获取根节点在中序遍历中的index,根节点在inorder中划分出左右节点
        root.left = self.recur(pre_root + 1, in_left, i - 1)
        root.right = self.recur(i - in_left + pre_root + 1, i + 1, in_right)
        return root

剑指 Offer 09. 用两个栈实现队列(简单)

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

栈无法实现队列删除栈底元素。
双栈进行列表倒序,然后删除队首元素:设有含三个元素的栈 A = [1,2,3]A=[1,2,3] 和空栈 B = []B=[]。若循环执行 AA 元素出栈并添加入栈 BB ,直到栈 AA 为空,则 A = []A=[] , B = [3,2,1]B=[3,2,1] ,即 栈 B 元素实现栈 AA 元素倒序 。倒序后,BB 执行出栈则相当于删除了 AA 的栈底元素,即对应队首元素。

class CQueue:
    def __init__(self):
        self.A, self.B = [], []

    def appendTail(self, value: int) -> None:
        self.A.append(value)

    def deleteHead(self) -> int:
        if self.B:  # 当栈 B 不为空:B中仍有已完成倒序的元素,因此直接返回 B 的栈顶元素。
            return self.B.pop()
        if not self.A:  # 当 A 为空: 即两个栈都为空,无元素,因此返回 -1。
            return -1
        while self.A:   # 将栈 A元素全部转移至栈 B 中,实现元素倒序,并返回栈 B 的栈顶元素。
            self.B.append(self.A.pop())
        return self.B.pop()

剑指 Offer 62. 圆圈中最后剩下的数字(简单)

0,1,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。

l = list(range(10))
print(l)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

def lastRemaining(self, n: int, m: int) -> int:
        i, a = 0, list(range(n))
        while len(a) > 1:
            i = (i+m-1) % len(a)
            a.pop(i)
        return a[0]

剑指 Offer 24. 反转链表(简单)

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

  1. 双指针:prev用作新生成链表的当前节点,curr用于链表的遍历。
def reverseList(self, head: ListNode) -> ListNode:
        prev, curr = None, head
        while curr:
            curr.next, prev, curr = prev, curr, curr.next
        return prev

deque
2. deque储存链表,再一次pop元素,重新构建反向链表。

from collections import deque
    def reverseList(self, head: ListNode) -> ListNode:
        if not head:
            return None
        # 储存链表
        deq = deque()
        while head.next:
            deq.append(head)
            head = head.next
            print(head)
        print(".....")
        temp = head     # 尾节点
        # 反向链表
        while deq:
            node = deq.pop()
            node.next = temp.next   # 新链表的头节点是旧链表的尾节点
            temp.next = node        # 向前推进
            temp = node
        return head

剑指 Offer 38. 字符串的排列(中等)

输入一个字符串,打印出该字符串中字符的所有排列。你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。

l = list(“abcde”)
print(l)
[‘a’, ‘b’, ‘c’, ‘d’, ‘e’]

深度优先搜索所有排列方案。即通过字符交换,先固定第 11 位字符( nn 种情况)、再固定第 22 位字符( n-1n−1 种情况)、… 、最后固定第 nn 位字符( 11 种情况)。

def permutation(self, s: str) -> List[str]:
        c, res = list(s), []

        def dfs(x): # 当前固定位:x
            if x == len(c) - 1: # 固定到最后一位,完成,直接append
                print(f"res{c}")
                res.append(''.join(c))
                return

            dic = set() # set排除重复结果
            for i in range(x, len(c)):  # 
                # 防止重复
                if c[i] in dic:
                    continue
                dic.add(c[i])   # 检测重复
                # 将第 x 位字符与 i∈[x,len(c)] 字符分别交换
                c[i], c[x] = c[x], c[i]
                dfs(x + 1)
                c[i], c[x] = c[x], c[i]
        dfs(0)
        return res

剑指 Offer 48. 最长不含重复字符的子字符串(中等) DP

题目
DP

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        dic = {}                    # 记录各字符最后一次出现的索引位置
        res = tmp = 0               # tmp = dp[j] 代表以字符 s[j] 为结尾的 “最长不重复子字符串” 的长度。
        for j in range(len(s)):     # j是子字符串的右边界
            i = dic.get(s[j], -1)   # 这个字符上一次出现的索引,之前没出现过 -> 返回-1
            dic[s[j]] = j           # 更新表,记录该字符出现的索引,成为最近一次出现的索引
            if tmp < j - i:         # dp[j−1]<j−i ,说明字符 s[i] 在子字符串 dp[j-1] 区间之外
                tmp = tmp + 1       # dp[j] = dp[j-1] + 1
            else:                   # dp[j−1]≥j−i ,说明字符 s[i] 在子字符串 dp[j-1] 区间之中 
                tmp = j - i 
            res = max(res, tmp)
        return res
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值