LC和牛客总结的字节跳动面试题记录

字节跳动面试题记录

  • 个人面经统计
    • 104. 二叉树的最大深度
    • 110. 平衡二叉树
    • 814. 二叉树剪枝
    • 23.合并K个排序链表
    • 1. 两数之和
    • 445. 两数相加 II
    • 2. 两数相加
    • 62. 不同路径
    • 63. 不同路径 II
    • 4. 寻找两个正序数组的中位数
    • 剑指 Offer 29. 顺时针打印矩阵
    • 509. 斐波那契数
    • 102. 二叉树的层序遍历
    • 146. LRU缓存机制
    • 415. 字符串相加
    • 199. 二叉树的右视图
    • 113. 路径总和 II
    • 3. 无重复字符的最长子串
    • 25. K 个一组翻转链表
    • 2. 两数相加
    • 958. 二叉树的完全性检验
    • 剑指 Offer 42. 连续子数组的最大和
    • 347. 前 K 个高频元素
    • 101. 对称二叉树
    • 1147. 段式回文
    • 20. 有效的括号
    • 283. 移动零
    • 876. 链表的中间结点
    • 460. LFU缓存
    • 206. 反转链表
    • 100. 相同的树
    • 69. x 的平方根
    • 124. 二叉树中的最大路径和
    • 剑指 Offer 11. 旋转数组的最小数字
    • 160. 相交链表
    • 15. 三数之和
    • 143. 重排链表
    • 8. 字符串转换整数 (atoi)
    • 842. 将数组拆分成斐波那契序列
    • 41.缺失的第一个正数
    • 704. 二分查找
    • 128. 最长连续序列
    • 814. 二叉树剪枝
    • 剑指 Offer 33. 二叉搜索树的后序遍历序列
    • 23. 合并K个排序链表
    • 445. 两数相加 II
    • 62. 不同路径
    • 63. 不同路径 II
    • 4. 寻找两个正序数组的中位数
    • 剑指 Offer 29. 顺时针打印矩阵
    • 509. 斐波那契数
    • 103. 二叉树的锯齿形层次遍历
    • 141. 环形链表
    • 剑指 Offer 52. 两个链表的第一个公共节点
    • 剑指 Offer 61. 扑克牌中的顺子
    • 105. 从前序与中序遍历序列构造二叉树
    • 215. 数组中的第K个最大元素
    • 111. 二叉树的最小深度
    • 994. 腐烂的橘子
    • 剑指 Offer 54. 二叉搜索树的第k大节点
    • 33. 搜索旋转排序数组
    • 977. 有序数组的平方
    • 344. 反转字符串
    • 234. 回文链表
  • 牛客网字节27真题
    • 字典序
    • 头条校招
    • 机器人跳跃问题
    • 找零
    • 毕业旅行问题
    • 特征提取

记录一下字节跳动的面试题及思路,来源可能是牛客网或者LeetCode,以及自己和身边人亲身经历。

个人面经统计

104. 二叉树的最大深度

递归做法是入门题

class Solution:
    def maxDepth(self, root):
        if root is None: 
            return 0 
        else: 
            left_height = self.maxDepth(root.left) 
            right_height = self.maxDepth(root.right) 
            return max(left_height, right_height) + 1 

迭代算是一个简单题

class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        stack = []
        if root is not None:
            stack.append((1, root))

        res = 0
        while stack:
            current_depth, root = stack.pop()
            if root:
                res = max(res, current_depth)
                stack.append((current_depth+1,root.left))
                stack.append((current_depth+1,root.right))
        return res

110. 平衡二叉树

把检索二叉树高度的过程加一个差距判断,并且可以用自底向上的方法优化达到提前返回的目的。

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        def helper(root):
            if not root:
                return 0
            l = helper(root.left)
            if l == -1:
                return -1
            r = helper(root.right)
            if r== -1:
                return -1
            return max(l,r) + 1 if abs(l-r) < 2 else -1
        return helper(root) != -1

814. 二叉树剪枝

运用辅助递归函数,根节点和左右字数都是只含有0时删除这个子树。

class Solution:
    def pruneTree(self, root: TreeNode) -> TreeNode:
        def helper(root):
            if not root:
                return False
            l = helper(root.left)
            r = helper(root.right)
            if not l:
                root.left = None
            if not r:
                root.right = None
            return root.val == 1 or l or r
            #删除操作放到树的上一层,用root.left删除。
        return root if helper(root) else None

23.合并K个排序链表

二分思想,递归到合并两个链表的子问题。

class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        if not lists:return 
        n = len(lists)
        return self.merge(lists, 0, n-1)
    #二分法
    def merge(self,lists, left, right):
        if left == right:
            return lists[left]
        mid = left + (right - left) // 2
        l1 = self.merge(lists, left, mid)
        l2 = self.merge(lists, mid+1, right)
        return self.mergeTwoLists(l1, l2)
	#合并2个链表
    def mergeTwoLists(self,l1, l2):
        if not l1:return l2
        if not l2:return l1
        ans = p = ListNode()
        while l1 and l2:
            if l1.val < l2.val:
                p.next = l1
                l1 = l1.next
            else:
                p.next = l2
                l2 = l2.next
            p = p.next
        if l1:
            p.next = l1
        if l2:
            p.next = l2
        return ans.next

1. 两数之和

没啥好说的,hash表解决

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        hash_num = {}
        for i in range(len(nums)):
            x = hash_num.get(target - nums[i],-1)
            if x != -1:
                return [x,i]
            hash_num[nums[i]] = i

445. 两数相加 II

既然不能反转,就用空间换时间,用栈把数据值反转,然后循环相加。

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        #空间换时间,省去2次遍历
        s1, s2 = [], []
        while l1:
            s1.append(l1.val)
            l1 = l1.next
        while l2:
            s2.append(l2.val)
            l2 = l2.next
        carry = 0
        ans = None
        #循环相加
        while s1 or s2 or carry:
            a = 0 if not s1 else s1.pop()
            b = 0 if not s2 else s2.pop()
            tmp = (a+b+carry)%10
            carry =  (a+b+carry)//10
            curnode = ListNode(tmp)
            curnode.next = ans
            ans = curnode
        return ans

2. 两数相加

相当于上一道题的简化,直接循环相加即可。

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        carry = 0
        head = cur = ListNode()
        while l1 or l2:
            x1 = l1.val if l1 else 0
            x2 = l2.val if l2 else 0
            cur.next = ListNode((x1+x2+carry)%10)
            carry = (x1+x2+carry)//10
            cur = cur.next
            if l1:
                l1 = l1.next
            if l2:
                l2 = l2.next
        if carry:
            cur.next = ListNode(carry)
        return head.next

62. 不同路径

dp问题,每个位置只能由右边和上边得到,可以轻松的写出转移方程。

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        dp = [1] * n
        #初始化
        #右和上
        for i in range(1, m):
            for j in range(1, n):
                dp[j] += dp[j-1]
        return dp[-1]

63. 不同路径 II

比上面一道题加了几个分析判断条件。

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        m = len(obstacleGrid)
        n = len(obstacleGrid[0])
        #一开始就堵了
        if obstacleGrid[0][0] == 1:
            return 0

        dp = [0] * n
        #第一行一直往右走,一旦堵了就全堵了
        for i in range(n):
            if obstacleGrid[0][i] == 0:
                dp[i] = 1
            else:
                break

        for i in range(1,m):
            #第一列只能往下走,要看上一行第一个走不走得通
            dp[0] = dp[0] if obstacleGrid[i][0] == 0 else 0
            for j in range(1,n):
                #本身堵了就是0,没有就看上和左的路径了
                dp[j] = dp[j-1] + dp[j]  if obstacleGrid[i][j] == 0 else 0 
        return dp[-1]

4. 寻找两个正序数组的中位数

相当于把两个数组都分为左右两部分,左右部分的元素个数需要相等。而边界值就是需要的中位数。

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        m = len(nums1)
        n = len(nums2)
        # m更小 不会减出0
        if m > n:
            m,n = n,m
            nums1,nums2 = nums2,nums1
        mid = (m+n+1)//2

        l,r = 0,m
        while l <= r:
            #i,j是nums1和nums2中左边元素的数量(不是下标)
            i = (l+r)//2
            j = mid - i
            #nums1大了
            if i > 0 and nums1[i-1] >nums2[j]:
                r = i - 1
            #num1小了
            elif i < m and nums1[i] < nums2[j-1]:
                l = i + 1
            #满足条件,开始寻找左右极值
            else:
                #左最大
                if i == 0:
                    l_max = nums2[j-1]
                elif j ==0:
                    l_max = nums1[i-1]
                else:
                    l_max = max(nums1[i-1],nums2[j-1])
                if (m+n)%2:
                    return l_max
                #右最小
                if i== m:
                    r_min= nums2[j]
                elif j ==n:
                    r_min = nums1[i]
                else:
                    r_min = min(nums1[i],nums2[j])
                return (l_max + r_min)/2.0

剑指 Offer 29. 顺时针打印矩阵

模拟仿真操作,我们直觉在顺时针打印时,是按照一行一列输出并逐渐往内延申的,我们也可以按照这种思路编写程序。

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        if not matrix: 
            return []
        #当前未遍历到的矩阵边界
        l, r, t, b, res = 0, len(matrix[0]) - 1, 0, len(matrix) - 1, []
        while True:
            #顶上一行
            for i in range(l, r + 1): 
                res.append(matrix[t][i]) # left to right
            t += 1
            if t > b: break
            #右边一列
            for i in range(t, b + 1): 
                res.append(matrix[i][r]) # top to bottom
            r -= 1
            if l > r: break
            #底下一行
            for i in range(r, l - 1, -1): 
                res.append(matrix[b][i]) # right to left
            b -= 1
            if t > b: break
            #左边一列
            for i in range(b, t - 1, -1): 
                res.append(matrix[i][l]) # bottom to top
            l += 1
            if l > r: break
        return res

509. 斐波那契数

每个数仅与前两个数相关,所以只需要2个变量储存历史信息就是完成dp。

class Solution:
    def fib(self, N: int) -> int:
        d0 = 0
        d1 = 1
        for i in range(N):
            d0,d1= d1,d0+d1
        return d0

102. 二叉树的层序遍历

利用队列,每次处理一层的节点数据。

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        out = []
        s = collections.deque()
        if not root:
            return out

        s.append(root)
        while s:
            n = len(s)
            temp = []
            #一层节点
            for i in range(n):
                x = s.popleft()
                temp.append(x.val)
                if x.left:
                    s.append(x.left)
                if x.right:
                    s.append(x.right)
            out.append(temp)
        return out

146. LRU缓存机制

O(1)的要求只能是hash表作为索引+链表存数据的组合了,利用虚拟头尾节点简化边界条件。

class DLinkedNode:
    def __init__(self, key=0, value=0):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None


class LRUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.size = 0
        self.hashmap = {}
        self.head = DLinkedNode()
        self.tail = DLinkedNode()
        self.head.next = self.tail
        self.tail.prev = self.head

    def get(self, key: int) -> int:
        if key not in self.hashmap:
            return -1
        #找到节点挪到开头
        node = self.hashmap[key]
        self.move2head(node)
        return node.value

    def put(self, key: int, value: int) -> None:
        if key not in self.hashmap:
            #插入新节点
            node = DLinkedNode(key, value)
            self.hashmap[key] = node
            #添加到开头
            node.prev = self.head
            node.next = self.head.next

            self.head.next.prev = node
            self.head.next = node
            #容量上限
            self.size += 1
            if self.size > self.capacity:
                removed = self.tail.prev
                removed.next.prev = removed.prev
                removed.prev.next = removed.next
                self.hashmap.pop(removed.key)
                self.size -= 1
        else:
            #更新
            node = self.hashmap[key]
            node.value = value
            self.move2head(node)

    #把节点移动到链表头
    def move2head(self,node):
        node.next.prev = node.prev
        node.prev.next = node.next

        node.prev = self.head
        node.next = self.head.next

        self.head.next.prev = node
        self.head.next = node

415. 字符串相加

类似用链表处理加法的方式。

class Solution:
    def addStrings(self, num1: str, num2: str) -> str:
        res = ""
        i, j, carry = len(num1) - 1, len(num2) - 1, 0
        while i >= 0 or j >= 0 or carry:
        	#加数1
            n1 = int(num1[i]) if i >= 0 else 0
            #加数2
            n2 = int(num2[j]) if j >= 0 else 0
            tmp = n1 + n2 + carry
            carry = tmp // 10
            res = str(tmp % 10) + res
            i, j = i - 1, j - 1
        return res

199. 二叉树的右视图

层次遍历,存储最后每层最后一个节点。

class Solution:
    def rightSideView(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        #队列结构方便遍历
        queue = collections.deque()
        queue.append(root)
        res = []
        while queue:
            n = len(queue)
            for i in range(n-1):
                node = queue.popleft()
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            #存储当前层最后的节点
            else:
                node = queue.popleft()
                res.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
        return res

113. 路径总和 II

利用dfs遍历树节点,记录路径利用回溯法求解。

class Solution:
    def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
        res = []
        if not root: return []
        def helper(root,sum,tmp):
            if not root:
                return 
            if not root.left and not root.right and sum - root.val == 0 :
                tmp += [root.val]
                res.append(tmp)
                return 
            helper(root.left,sum - root.val,tmp + [root.val])
            helper(root.right,sum - root.val,tmp + [root.val])

        helper(root, sum, [])
        return res

3. 无重复字符的最长子串

滑动窗口解决

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if not s:
            return 0
        l = r = out = 0
        #窗口元素
        pattern = set()
        while r < len(s):
            if s[r] not in pattern:
                #更新最大值
                pattern.add(s[r])
                out = max(out,r-l+1)
                r += 1
            else:
                pattern.remove(s[l])
                l += 1
        return out

25. K 个一组翻转链表

模拟题意得操作

class Solution:
    #反转一小段链表
    def reverse(self, head, tail):
        prev = tail.next
        p = head
        while prev != tail:
            p.next,prev,p = prev,p,p.next
        return tail, head

    def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
        #哨兵节点
        new_head = ListNode(0)
        new_head.next = head
        pre = new_head

        while head:
            tail = pre
            #定位到k长度
            for i in range(k):
                tail = tail.next
                if not tail:
                    return new_head.next
            nxt = tail.next
            head, tail = self.reverse(head, tail)
            #反转后的一小段连上总链表
            pre.next = head
            tail.next = nxt

            pre = tail
            head = tail.next
        
        return new_head.next

2. 两数相加

利用循环进位加法

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        carry = 0
        head = cur = ListNode()
        while l1 or l2 or carry:
            x1 = l1.val if l1 else 0
            x2 = l2.val if l2 else 0
            cur.next = ListNode((x1+x2+carry)%10)
            carry = (x1+x2+carry)//10
            cur = cur.next
            if l1:
                l1 = l1.next
            if l2:
                l2 = l2.next

        return head.next

958. 二叉树的完全性检验

根据定义,出现空值之后必须都是空值。

class Solution:
    def isCompleteTree(self, root: TreeNode) -> bool:
        if not root:
            return False
        q = collections.deque()
        q.append(root)
        #记录空节点是否出现过
        flag = False
        while q:
            n = len(q)
            for i in range(n):
                node = q.popleft()
                if node:
                    if flag:
                        #前面出现空值了,错误
                        return False
                    else:
                        q.append(node.left)
                        q.append(node.right)
                else:
                    flag = True
        return True

剑指 Offer 42. 连续子数组的最大和

贪心思想,当当前的部分和小于0时重新开始计数。

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        if not nums:
            return 0
        out = float('-inf')
        cur = 0
        for num in nums:
            if cur < 1:
                cur = num
            else:
                cur += num
            out = max(cur,out)
        return out

347. 前 K 个高频元素

除了用最大堆完成nlogn的方法之外,还可以利用hash表,存储每个元素出现次数再遍历次数。

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        n_f = {}
        f_n = {}
        for i in nums:
            n_f[i] = n_f.get(i,0) + 1
        for n,f in n_f.items():
            temp = f_n.get(f,[])
            temp.append(n)
            f_n[f] = temp
    
        arr = []
        #按照次数从小到大遍历
        for x in range(len(nums),0,-1):
            if x in f_n:
                for i in f_n[x]:
                    arr.append(i)
        return arr[:k]

101. 对称二叉树

按照定义,把要比较的节点成组放入,成组比较。

import queue
class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        q = queue.Queue()
        q.put((root, root))
        while q.qsize():
            left, right = q.get()
            if not left and not right:
                continue
            #一者为空,不符合
            if not left or not right:
                return False
            if left.val != right.val:
                return False
            q.put((left.left, right.right))
            q.put((left.right, right.left))
        return True

1147. 段式回文

当我们用双指针从头尾向中间遍历时,如果是段式回文,前后缀就会相等。

class Solution:
    def longestDecomposition(self, text: str) -> int:
        n = len(text)
        i, j = 0, n - 1
        str1, str2, ans = '', '', 0
        while i < j:
            str1 = str1 + text[i]
            str2 = text[j] + str2
            #分段回文
            if str1 == str2:
                ans += 2
                str1, str2 = '', ''
            i += 1
            j -= 1
        if n % 2 == 1 or str1 != '':
            ans += 1
        return ans

20. 有效的括号

通过栈结构,匹配括号。

class Solution:
    def isValid(self, s: str) -> bool:
        mapping = {')':'(',']':'[','}':'{'}
        stack = []
        for x in s:
            if x not in mapping.keys():
                stack.append(x)
            else:
                if not stack:
                    return False
                if stack.pop() != mapping[x]:
                    return False
        #多余括号
        if stack:
            return False
        return True

283. 移动零

一个指针遍历数组,一个存储插入位置。

        prvot = 0
        for i in range(len(nums)):
            if nums[i]!=0:
                nums[i],nums[prvot] = nums[prvot],nums[i]
                prvot += 1

876. 链表的中间结点

利用快慢指针遍历

class Solution:
    def middleNode(self, head: ListNode) -> ListNode:
        if not head:
            return
        p1 = head
        p2 = head.next

        while p2 and p2.next:
            p1 = p1.next
            p2 = p2.next.next
        if p2:
            return p1.next
        else:
            return p1

460. LFU缓存

使用双向链表+hash表模拟操作,工程实现比较复杂,推荐看官方题解。

206. 反转链表

遍历链表,每次处理一个点,利用python多元赋值

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        p1 = None
        p2 = head
        while p2:
            p2.next,p2,p1 = p1,p2.next,p2
        return p1

100. 相同的树

递归处理

class Solution:
    def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
        if not p and not q:
            return True
        if not p or not q:
            return False
        if p.val != q.val:
            return False
        return self.isSameTree(p.left,q.left) and self.isSameTree(p.right,q.right)

69. x 的平方根

可以用普通的二分法,也可以用牛顿法求解,令f(x) = 0一阶泰勒展开求出迭代公式。

class Solution:
    def mySqrt(self, x: int) -> int:
        if x == 0:
            return 0
        
        C, x0 = float(x), float(x)
        while True:
            xi = 0.5*(C/x0+x0)
            if abs(x0 - xi) < 1e-7:
                break
            x0 = xi
        return int(xi)

124. 二叉树中的最大路径和

树的问题一般都可以用递归方式解决,这道题可以拆分为过左右子树和不过的两种考虑。

class Solution:
    def __init__(self):
        #存储变量
        self.max_sum = float('-inf')
    def maxPathSum(self, root: TreeNode) -> int:
        self.helper(root)
        return self.max_sum
        
    def helper(self,root):
        if not root:
            return 0
        left_gain = max(self.helper(root.left), 0)
        right_gain = max(self.helper(root.right), 0)
        #过根节点左右子树的最大路径
        node_in_path = left_gain + right_gain + root.val
        self.max_sum = max(self.max_sum,node_in_path)
        #向上返回的话只能过一个子树
        return root.val + max(left_gain , right_gain)

剑指 Offer 11. 旋转数组的最小数字

常规的二分法搜索

class Solution:
    def minArray(self, numbers: List[int]) -> int:
        l,r = 0,len(numbers)-1
        while l < r:
            mid = (l+r)//2
            if numbers[mid] > numbers[r]:
                l = mid + 1
            elif numbers[mid] < numbers[r]:
                r = mid
            else:
                r -= 1
        return numbers[l]

160. 相交链表

两个链表长度可能不一致,但是A+B和B+A是等长的,所以连在一起遍历如果相交两个指针肯定会重合在入口节点。

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        p1 = headA
        p2 = headB
        while p1!=p2:
            p1 = p1.next if p1 else headB
            p2 = p2.next if p2 else headA
        return p1

15. 三数之和

先把数组排序,然后固定第一个元素再去寻找其他元素。

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        n=len(nums)
        res=[]
        if(not nums or n<3):
            return []
        nums.sort()
        for i in range(n):
            #排了序,第一个元素为整数组成不了3元组
            if nums[i] > 0:
                return res
            #重复元素只要第一个
            if(i>0 and nums[i]==nums[i-1]):
                continue
            l = i + 1
            r = n - 1
            while l<r:
                #双指针检索合适的元素
                if nums[i] + nums[l] +nums[r] == 0:
                    res.append( [nums[i] , nums[l] , nums[r]])
                    while(l<r and nums[l]==nums[l+1]):
                        l += 1
                    while(l<r and nums[r]==nums[r-1]):
                        r -= 1
                    l -= 1
                    r -= 1
                elif(nums[i]+nums[l]+nums[r]>0):
                    r-=1
                else:
                    l+=1
        return res

143. 重排链表

先把链表分成两块,后半反转之后把两个链表交替结合即可。

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

    def reorderList(self, head: ListNode) -> None:
        if not head:
            return head
        slow, fast = head, head
        while fast and fast.next:
            slow, fast = slow.next, fast.next.next
        #p为链表的中间位置
        p = slow.next


        slow.next = None
        p = self.reverseList(p)
        m = head

        while p:
            m.next, p.next, m, p = p, m.next, m.next, p.next

8. 字符串转换整数 (atoi)

详细考虑一下越界情况。

class Solution:
    def myAtoi(self, str: str) -> int:
        str = str.strip() # 删除首尾空格
        if not str: 
            return 0 # 字符串为空则直接返回
        int_max, int_min = 2 ** 31 - 1, -2 ** 31
        boudry = 2 ** 31 // 10
        res, i, sign = 0, 1, 1
        if str[0] == '-': sign = -1 # 保存负号
        elif str[0] != '+': i = 0 # 若无符号位,则需从 i = 0 开始数字拼接
        for c in str[i:]:
            if not '0' <= c <= '9' : 
                break
            if res > boudry or(res == boudry and c > '7'):
                #注意,超过边界的数末尾是大于7的,等于7的本身就是边界值,正常输出
                return int_max if sign == 1 else int_min
            res = 10 * res + ord(c) - ord('0')
        return sign*res

842. 将数组拆分成斐波那契序列

使用回溯法

class Solution:
    def splitIntoFibonacci(self, S: str) -> List[int]:
        n = len(S)

        res = []

        def backtrack(index, temp):
        #index开始,前面数字的分段结果为temp
            if index == n:
                if self._is_fibonacci(temp):
                    res.append(temp)
                return

            for i in range(index, n):
                new_value = S[index:i + 1]
                if int(new_value) > 2 ** 31 - 1:
                    break
                if not (new_value.startswith('0') and len(new_value) > 1):
                    if len(temp) >= 2 and int(new_value) == (temp[-1] + temp[-2]):
                        backtrack(i + 1, temp + [int(new_value)])
                    elif len(temp) < 2:
                        backtrack(i + 1, temp + [int(new_value)])

        backtrack(0, [])

        return res[0] if res else []
	#判断是否满足条件
    def _is_fibonacci(self, temp):
        if len(temp) < 3:
            return False
        for i in range(2, len(temp)):
            if temp[i] != (temp[i - 1] + temp[i - 2]):
                return False
        return True

41.缺失的第一个正数

利用索引位置把数字归位,第一个对不上的就是缺失的整数

class Solution:
    def firstMissingPositive(self, nums: List[int]) -> int:
        n = len(nums)
        for i in range(n):
            while 1 <= nums[i] <=n and nums[nums[i]-1]!= nums[i]:
                nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]
            
        for i in range(n):
            if nums[i] != i+1:
                return i+1
        return n+1

704. 二分查找

常规二分查找

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        l = 0
        r = len(nums)-1
        while l <= r:
            mid = (l+r)//2
            if nums[mid] == target:
                return mid
            elif nums[mid] > target:
                r = mid - 1
            else:
                l = mid + 1
        return -1 

128. 最长连续序列

利用hash表,从每个区间的开始元素开始判断长度。

class Solution:
    def longestConsecutive(self, nums: List[int]) -> int:
        longest_streak = 0
        num_set = set(nums)
        for num in num_set:
        #找到区间的开始元素
            if num - 1 not in num_set:
                current_num = num
                current_streak = 1
                while current_num + 1 in num_set:
                    current_num += 1
                    current_streak += 1

                longest_streak = max(longest_streak, current_streak)

        return longest_streak

814. 二叉树剪枝

递归判断子树是否满足条件,来进行删除(置空操作)。

class Solution:
    def pruneTree(self, root: TreeNode) -> TreeNode:
        def helper(root):
            if not root:
                return False
            l = helper(root.left)
            r = helper(root.right)
            if not l:
                root.left = None
            if not r:
                root.right = None
            return root.val == 1 or l or r
        return root if helper(root) else None

剑指 Offer 33. 二叉搜索树的后序遍历序列

使用单调栈,一直维护当前遍历的树结构的根节点,然后判断值是否符合二叉搜素树定义。

class Solution:
    def verifyPostorder(self, postorder: List[int]) -> bool:
        root = float('inf')
        stack = []
        for val in reversed(postorder):
            if val > root:
                return False
            while stack and val < stack[-1]:
                root = stack.pop()
            stack.append(val)
        return True

23. 合并K个排序链表

分治成多个合并两个链表的问题解决

class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        if not lists:return 
        n = len(lists)
        return self.merge(lists, 0, n-1)
    #拆分子问题分治
    def merge(self,lists, left, right):
        if left == right:
            return lists[left]
        mid = left + (right - left) // 2
        l1 = self.merge(lists, left, mid)
        l2 = self.merge(lists, mid+1, right)
        return self.mergeTwoLists(l1, l2)
	#两个链表合并
    def mergeTwoLists(self,l1, l2):
        if not l1:return l2
        if not l2:return l1
        ans = p = ListNode()
        while l1 and l2:
            if l1.val < l2.val:
                p.next = l1
                l1 = l1.next
            else:
                p.next = l2
                l2 = l2.next
            p = p.next
        if l1:
            p.next = l1
        if l2:
            p.next = l2
        return ans.next

445. 两数相加 II

模拟逐位加法进位操作,但是可以用空间换时间

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        #空间换时间,省去多次遍历单向链表
        s1, s2 = [], []
        while l1:
            s1.append(l1.val)
            l1 = l1.next
        while l2:
            s2.append(l2.val)
            l2 = l2.next
        carry = 0
        ans = None
        while s1 or s2 or carry:
            a = 0 if not s1 else s1.pop()
            b = 0 if not s2 else s2.pop()
            tmp = (a+b+carry)%10
            carry =  (a+b+carry)//10
            curnode = ListNode(tmp)
            curnode.next = ans
            ans = curnode
        return ans

62. 不同路径

利用滚动数组处理即可,常规的动态规划问题

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        dp = [1] * n
        #初始化
        #右和上
        for i in range(1, m):
            for j in range(1, n):
                dp[j] += dp[j-1]
        return dp[-1]

63. 不同路径 II

和上一道题一样利用滚动数组,要注意的点就是初始化条件不同。

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        m = len(obstacleGrid)
        n = len(obstacleGrid[0])
        #一开始就堵了
        if obstacleGrid[0][0] == 1:
            return 0

        dp = [0] * n
        #第一行一直往右走,一旦堵了就全堵了
        for i in range(n):
            if obstacleGrid[0][i] == 0:
                dp[i] = 1
            else:
                break

        for i in range(1,m):
            #第一列只能往下走,要看上一行第一个走不走得通
            dp[0] = dp[0] if obstacleGrid[i][0] == 0 else 0
            for j in range(1,n):
                #本身堵了就是0,没有就看上和左的路径了
                dp[j] = dp[j-1] + dp[j]  if obstacleGrid[i][j] == 0 else 0 
        return dp[-1]

4. 寻找两个正序数组的中位数

利用中位数的性质,两个数组左边部分之和应该是总元素个数的一半。找到满足这样条件的两个数组的切分位置即可。

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        m = len(nums1)
        n = len(nums2)
        # m更小 不会减出0
        if m > n:
            m,n = n,m
            nums1,nums2 = nums2,nums1
        mid = (m+n+1)//2

        l,r = 0,m
        while l <= r:
            #i,j是nums1和nums2中左边元素的数量(不是下标)
            i = (l+r)//2
            j = mid - i
            #nums1大了
            if i > 0 and nums1[i-1] >nums2[j]:
                r = i - 1
            #num1小了
            elif i < m and nums1[i] < nums2[j-1]:
                l = i + 1
            #满足条件,开始寻找左右极值
            else:
                #左最大
                if i == 0:
                    l_max = nums2[j-1]
                elif j ==0:
                    l_max = nums1[i-1]
                else:
                    l_max = max(nums1[i-1],nums2[j-1])
                if (m+n)%2:
                    return l_max
                #右最小
                if i== m:
                    r_min= nums2[j]
                elif j ==n:
                    r_min = nums1[i]
                else:
                    r_min = min(nums1[i],nums2[j])
                return (l_max + r_min)/2.0

剑指 Offer 29. 顺时针打印矩阵

仿真模拟操作

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        if not matrix: 
            return []
        #当前未遍历到的矩阵边界
        l, r, t, b, res = 0, len(matrix[0]) - 1, 0, len(matrix) - 1, []
        while True:
            #顶上一行
            for i in range(l, r + 1): 
                res.append(matrix[t][i]) # left to right
            t += 1
            if t > b: break
            #右边一列
            for i in range(t, b + 1): 
                res.append(matrix[i][r]) # top to bottom
            r -= 1
            if l > r: break
            #底下一行
            for i in range(r, l - 1, -1): 
                res.append(matrix[b][i]) # right to left
            b -= 1
            if t > b: break
            #左边一列
            for i in range(b, t - 1, -1): 
                res.append(matrix[i][l]) # bottom to top
            l += 1
            if l > r: break
        return res

509. 斐波那契数

常规的动态规划问题

class Solution:
    def fib(self, N: int) -> int:
        d0 = 0
        d1 = 1
        for i in range(N):
            d0,d1= d1,d0+d1
        return d0

103. 二叉树的锯齿形层次遍历

在正常的层次遍历基础上,根据层数做判断结果是从左到右还是从右到左保存。

class Solution:
    def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
        ans = list()
        if root:
        	#遍历用queue
            queue = collections.deque()
            queue.append(root)
            start_left = True
            level = collections.deque()
            while queue:
                length = len(queue)
                for i in range(length):
                    node = queue.popleft()
                    #当前层结果保存方向
                    if not start_left:
                        level.appendleft(node.val)
                    else:
                        level.append(node.val)
                    if node.left:
                        queue.append(node.left)
                    if node.right:
                        queue.append(node.right)
                start_left = not start_left
                ans.append(list(level))
                level.clear()
        return ans

141. 环形链表

利用快慢指针,查看其是否相遇

class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        p1 = p2 = head
        while p2 != None and p2.next != None:
            p1 = p1.next
            p2 = p2.next.next
            if p1 == p2:
                break
        else:
            return False
        return True

剑指 Offer 52. 两个链表的第一个公共节点

两个链表拼起来遍历,有公共节点则会重合。

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        p1 = headA
        p2 = headB
        while p1 != p2:
            p1 = p1.next if p1 else headB
            p2 = p2.next if p2 else headA
        return p1

剑指 Offer 61. 扑克牌中的顺子

排序后检查最大最小牌差值是多少即可。

class Solution:
    def isStraight(self, nums: List[int]) -> bool:
        joker = 0
        nums.sort() # 数组排序
        for i in range(4):
            if nums[i] == 0: joker += 1 # 统计大小王数量
            elif nums[i] == nums[i + 1]: return False # 若有重复,提前返回 false
        return nums[4] - nums[joker] < 5 # 最大牌 - 最小牌 < 5 则可构成顺子

105. 从前序与中序遍历序列构造二叉树

用前序找到树的根切分中序中左右子树节点,再递归构建。

class Solution:
    def buildTree(self, preorder, inorder):
        if len(inorder) == 0:
            return None
        # 前序遍历第一个值为根节点
        root = TreeNode(preorder[0])
        mid = inorder.index(preorder[0])
        root.left = self.buildTree(preorder[1:mid+1], inorder[:mid])
        root.right = self.buildTree(preorder[mid+1:], inorder[mid+1:])
        return root

215. 数组中的第K个最大元素

利用快排里的快速划分寻找

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        def partition(l,r):
            pivot = (l+r)//2
            p = nums[pivot]
            nums[r],nums[pivot] = nums[pivot],nums[r]
            store = l
            for i in range(l,r):
                if nums[i] < p:
                    nums[store],nums[i] = nums[i],nums[store]
                    store += 1
            nums[r],nums[store] = nums[store],nums[r]
            return store
        def select(l,r,k):
            #k为第k小
            if l == r:
                return nums[l]
            pivot = partition(l, r

            if k == pivot:
                return nums[k]
            elif k < pivot:
                return select(l, pivot - 1, k)
            else:
                return select(pivot + 1, r, k)

        return select(0, len(nums) - 1, len(nums) - k)

111. 二叉树的最小深度

递归解决,要注意的就是某些只有一个儿子的节点递归时会出现某一边深度为0需要判断。

class Solution:
    def minDepth(self, root):
        if not root:
            return 0
        l = self.minDepth(root.left)
        r = self.minDepth(root.right)
        #判断是不是只有一个儿子的节点
        if not all([l,r]) and any([l,r]):
            return max(l,r) + 1
        else:
            return min(l,r) + 1

994. 腐烂的橘子

利用BFS带一个时间标记完成。

class Solution(object):
    def orangesRotting(self, grid):
        R, C = len(grid), len(grid[0])

        #队列保存腐烂位置和时间
        queue = collections.deque()
        for r, row in enumerate(grid):
            for c, val in enumerate(row):
                if val == 2:
                    queue.append((r, c, 0))

        def neighbors(r, c):
            for nr, nc in ((r-1,c),(r,c-1),(r+1,c),(r,c+1)):
                if 0 <= nr < R and 0 <= nc < C:
                    yield nr, nc

        d = 0
        while queue:
            r, c, d = queue.popleft()
            for nr, nc in neighbors(r, c):
                if grid[nr][nc] == 1:
                    grid[nr][nc] = 2
                    #时间为源头腐烂时间+1
                    queue.append((nr, nc, d+1))

        if any(1 in row for row in grid):
            return -1
        return d

剑指 Offer 54. 二叉搜索树的第k大节点

利用二叉搜索树的性质,右子树节点都比他大,计数时减去,直到计数器为0返回根节点。

class Solution:
    def kthLargest(self, root: TreeNode, k: int) -> int:
        def dfs(root):
            if not root: 
                return
            dfs(root.right)
            if self.k == 0: 
                return
            self.k -= 1
            if self.k == 0: 
                self.res = root.val
            dfs(root.left)

            
        self.k = k
        dfs(root)
        return self.res

33. 搜索旋转排序数组

常规的二分搜索变形

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        if not nums:
            return -1
        l = 0
        r = len(nums) - 1
        while l <= r:
            mid = (l+r)//2
            if nums[mid] == target:
                return mid
            #前半有序
            if nums[l] <= nums[mid]:
                if nums[l] <= target < nums[mid]:
                    r = mid - 1
                else:
                    l = mid + 1
            else:
                if nums[mid] < target <= nums[r]:
                    l = mid + 1
                else:
                    r = mid - 1
        return -1

977. 有序数组的平方

因为是有序的数组,平方和大的数只会出现在两侧,所以需要从两侧向中间遍历。

class Solution:
    def sortedSquares(self, A: List[int]) -> List[int]:
        l = 0
        r = len(A) - 1
        res = []
        while l <= r:
            x1 = A[l] ** 2
            x2 = A[r] ** 2
            if x1 > x2:
                res.append(x1)
                l += 1
            else:
                res.append(x2)
                r -= 1
        return res[::-1]

344. 反转字符串

双指针从两头向中间遍历。

class Solution:
    def reverseString(self, s: List[str]) -> None:
        l = 0 
        r = len(s) - 1
        while l < r:
            s[l],s[r] = s[r],s[l]
            l += 1
            r -= 1

234. 回文链表

反转后半链表进行遍历对比

class Solution:

    def isPalindrome(self, head: ListNode) -> bool:
        if head is None:
            return True

        #寻找中间位置
        first_position = self.end_of_first_half(head)
        second_position = self.reverse_list(first_position.next)

        # 遍历搜索
        result = True
        #注意我们的分割方式下,多余的一个节点会在前半段,所以判断后半段是否遍历完
        while result and second_position is not None:
            if first_position.val != second_position.val:
                result = False
            first_position = first_position.next
            second_position = second_position.next
        return result    

    def end_of_first_half(self, head):
        fast = head
        slow = head
        while fast.next and fast.next.next:
            fast = fast.next.next
            slow = slow.next
        return slow

    def reverse_list(self, head):
        previous = None
        current = head
        while current:
            next_node = current.next
            current.next = previous
            previous = current
            current = next_node
        return previous

牛客网字节27真题

这里面的题比较偏门。。大家看看就行

字典序

LC和牛客总结的字节跳动面试题记录_第1张图片



def find_kth_number(n, k):
    cur = 1
    k-=1
    while k>0:
        step = 0
        #当前搜索边界
        first = cur
        last = cur + 1
        while first <=n:
            #cur开头的1位数,2位数……
            step += min(n + 1, last) - first
            first *= 10
            last *= 10
        if step<=k:
            #头位数字+1
            cur += 1
            k-=step
        else:
            #头位数字定下来了,寻找下一位数
            cur *= 10
            k-=1
    return cur

n,m = list(map(int,input().split()))
print(find_kth_number(n, m))

头条校招

排序后使用暴力搜索

n=int(input())
d=list(map(int,input().split()))
d.sort()

def increas_num(d,n):
    num = 0
    i = 0
    while i < n:
        #这一道题需要加2个
        if i == n - 1 or d[i + 1] - d[i] > 20:
            num += 2
            i += 1
        #这一道题后面可以做第三题
        elif d[i+1] - d[i] > 10:
            num += 1
            i += 2
        else:
            #i后面一道满足,再后面一道不满足
            if i + 1 == n - 1 or d[i+2] - d[i+1] > 10:
                num += 1
                i += 2
            else:
                #连续3道满足
                i += 3
    return num

print(increas_num(d,n))

机器人跳跃问题

根据题意推导出递推公式
在这里插入图片描述
即可写出代码

n = int(input())
h = list(map(int,input().split()))

e = 0
for i in reversed(range(n)):
    e =  (h[i] + e + 1)//2
print(e)

找零

因为硬币之间是可以替代的,所以直接用贪心思想就可以。

n = 1024 - int(input())
res = 0
while n:
    if n >= 64:
        res += n//64
        n = n%64
    elif n >= 16:
        res += n//16
        n = n %16
    elif n >= 4:
        res += n//4
        n = n %4
    else:
        res += n
        n = 0
print(res)

毕业旅行问题

link.
可以当作图论最小环问题求解。

n = int(input())
m = [[]*n for _ in range(n)]
for i in range(n):
    m[i] = list(map(int,input().split()))
    
V = 1 << (n-1)  #从左至右每一位二进制代表第i个城市是否被访问 如1000代表,第一个城市被访问,而其他城市没有
dp = [[float("inf")] * V for i in range(n)] # dp[i][j]:从节点i只经过集合j所有点再回到0点所需要的最小开销

for i in range(n):
    dp[i][0] = m[i][0]
    
for j in range(1,V):
    #为了顺利分解子问题,第一重循环从J开始,相当于每轮考虑一个点的集合,后续逐次增加点
    for i in range(n):
        for k in range(1,n):  #能不能先到k城市
            if (j >> (k-1) & 1) == 1: #可以途径k
                dp[i][j] = min(dp[i][j], m[i][k] + dp[k][j ^ (1 << (k-1))])
  
#从0出发,经过所有点,再回到0的费用
print(dp[0][(1 << (n-1)) - 1])

特征提取

link.
利用字典记录出现次数即可

n = int(input())

while n > 0:
    m = int(input())
    res = 1
    d = {}
    for i in range(m):
        l = list(input().split())
        k = int(l[0])
        tmp_d = {}
        for j in range(k):
            index = (l[2 * j + 1],l[2 * j + 2])
            if index in d:
                tmp_d[index] = d[index] + 1
                res = max(res, tmp_d[index])
            else:
                tmp_d[index] = 1
        d = tmp_d
    print(res)
    n -= 1

你可能感兴趣的:(LeetCode)