文章目录

骚题目

1420. 生成数组

蓄水池抽样

博客园 - 蓄水池采样算法

O ( N ) O(N) O(N)的时间复杂度从 N N N个数中无放回等可能抽样 K K K个数

用于不知道数据规模的情况,保证每个样本被抽中的概率是等可能的

  • 算法步骤

假设数据序列的规模为 n n n,需要采样的数量的为 k k k

首先构建一个可容纳 k k k个元素的数组,将序列的前 k k k个元素放入数组中。

然后从第 k + 1 k+1 k+1个元素开始,以 k / n k/n k/n n n n表示动态增长的数据规模)的概率来决定该元素是否被替换到数组中(数组中的元素被替换的概率是相同的)。 当遍历完所有元素之后,数组中剩下的元素即为所需采取的样本。
数据结构与算法重难点100道_链表

数据结构与算法重难点100道_时间复杂度_02

class Solution:

    def __init__(self, head: ListNode):
        """
        @param head The linked list's head.
        Note that the head is guaranteed to be not null, so it contains at least one node.
        """
        self.res=[]
        self.K=1
        self.head=head



    def getRandom(self) -> int:
        """
        Returns a random node's value.
        """
        k=self.K
        p=self.head
        while k and p:
            self.res.append(p.val)
            k-=1
            p=p.next
        p=self.head
        i=0
        while p:
            ix=random.randint(0, i)
            if ix < self.K:
                self.res[ix]=p.val
            i+=1
            p=p.next
        return self.res[0]

字节跳动推荐算法岗面经,三次技术面通过

这题也是蓄水池抽样
数据结构与算法重难点100道_二叉树_03

shuffle算法本质上也是蓄水池抽样,就是动作换成swap

全Shuffle和抽m个Shuffle:

from random import randint


def shuffle(nums):
    n = len(nums)
    for i in range(n):
        ri = randint(0, i)
        nums[i], nums[ri] = nums[ri], nums[i]
    return nums


def shuffle_m(nums, m):
    n = len(nums)
    for i in range(n):
        ri = randint(0, i)
        if ri < m:
            nums[i], nums[ri] = nums[ri], nums[i]
    return nums[:m]


print(shuffle(list(range(10))))
print(shuffle_m(list(range(10)),3))
Alias Sampling

Alias Method:时间复杂度O(1)的离散采样方法

python3 Alias Sample

class Solution:

    def __init__(self, w: List[int]):
        N = len(w)
        sum_ = sum(w)
        prob = [p / sum_ for p in w]
        alias = [0] * N
        alias_prob = [p * N for p in prob]
        small_q = []
        large_q = []
        for i, p in enumerate(alias_prob):
            if p < 1:
                small_q.append(i)
            else:
                large_q.append(i)
        while small_q and large_q:
            small = small_q.pop(0)
            large = large_q.pop(0)
            alias[small] = large
            alias_prob[large] -= (1 - alias_prob[small])
            if alias_prob[large] < 1:
                small_q.append(large)
            else:
                large_q.append(large)
        self.alias = alias
        self.N = N
        self.alias_prob = alias_prob

    def pickIndex(self) -> int:
        ix = random.randint(0, self.N - 1)
        p = random.random()
        if p < self.alias_prob[ix]:
            return ix
        else:
            return self.alias[ix]

补充其他与随机数相关的算法:

  • 线性同余法

linear congruential generator (LCG)

R a n d S e e d = ( A ∗ R a n d S e e d + B ) % M RandSeed = (A * RandSeed + B) \% M RandSeed=(ARandSeed+B)%M

LCG的周期最大为 M,但大部分情况都会少于M。要令LCG达到最大周期,应符合以下条件:

数据结构与算法重难点100道_链表_04

  • 中心极限生成高斯分布

反函数法

一般,一种概率分布,如果其分布函数为 y = F ( x ) y=F(x) y=F(x),那么,y的范围是0~1,求其反函数 G G G,然后产生0到1之间的随机数作为输入,那么输出的就是符合该分布的随机数了:

y = G ( x ) y= G(x) y=G(x)

中心极限定理

分大量的批次,每个批次生成12个 [ 0 , 1 ] [0,1] [0,1]间的随机数,然后求和。求和后的随机变量方差为 1 / 12 × 12 = 1 1/12\times 12=1 1/12×12=1,均值为 1 / 2 × 12 = 6 1/2 \times 12=6 1/2×12=6。然后-6。只要产生的批次足够多,就能生成总体服从 N ( 0 , 1 ) \mathcal{N}(0,1) N(0,1)的分布。

import numpy as np
import pylab as plt

n = 12
N = 5000
x = np.zeros([N])
for j in range(N):
    a = np.random.rand(n)
    u = sum(a)
    x[j] = u - n * 0.5
plt.hist(x)
plt.show()

Box Muller

import numpy as np
import pylab as plt

N = 1000
x1 = np.random.rand(1, N)
x2 = np.random.rand(1, N)
y1 = np.sqrt(-2 * np.log(x1)) * np.cos(2 * np.pi * x2)
y2 = np.sqrt(-2 * np.log(x1)) * np.sin(2 * np.pi * x2)
y = np.hstack([y1, y2])
plt.hist(y)
plt.show()	
  • 接受拒绝采样生成高斯分布

逆采样(Inverse Sampling)和拒绝采样(Reject Sampling)原理详解

  • 接受拒绝采样求 π \pi π
from math import sqrt
from random import random

def sample_pi(max_cnt=100000):
    acc_cnt=0
    for i in  range(max_cnt):
        x=random()
        y=random()
        if sqrt(x**2+y**2)<1:
            acc_cnt+=1
    print("pi =",acc_cnt/max_cnt*4)

sample_pi()
  • 高斯分布生成均匀分布

正态分布可以生成均匀分布吗?

a r c t a n ( z 0 z 1 ) + 0.5 arctan(\frac{z0}{z1})+0.5 arctan(z1z0)+0.5

生成二维标准正态分布的方法就是取两个独立的标准正态分布变量X和Y放在一起(X, Y)就行了

然后二维标准正态分布在直角坐标系里有各向同性,也就是(X, Y)这个点所指的方向和X轴(或者任何一个给定方向)的夹角是均匀分布的

很好理解, t a n ( θ ) = y x tan(\theta )=\frac{y}{x} tan(θ)=xy

反着用Box-Muller

数据结构与算法重难点100道_数组_05

AUC曲线计算

数据结构与算法重难点100道_数组_06
按照prob对【标签-样本】pairs进行排序,如果模型具有良好的排序能力,结果应该是 [ 0 , 0 , 1 , 1 , 1 ] [0,0,1,1,1] [0,0,1,1,1],正样本数 M = 3 M=3 M=3,负样本数 N = 2 N=2 N=2

只考虑正样本获取对应的排序值rankList,为 [ 3 , 4 , 5 ] [3,4,5] [3,4,5],求和为12 。右边公式算出来是6,分母6,结果1,符合定义。

def calAUC(prob,labels):
  f = list(zip(prob,labels))
  rank = [values2 for values1,values2 in sorted(f,key=lambda x:x[0])]
  rankList = [i+1 for i in range(len(rank)) if rank[i]==1]
  posNum = 0
  negNum = 0
  for i in range(len(labels)):
    if(labels[i]==1):
      posNum+=1
    else:
      negNum+=1
  auc = 0
  auc = (sum(rankList)- (posNum*(posNum+1))/2)/(posNum*negNum)
  print(auc)
  return auc
1363. 形成三的最大倍数

1363. 形成三的最大倍数

class Solution:
    def largestMultipleOfThree(self, digits: List[int]) -> str:
        n=len(digits)
        count=[0]*10
        modulo=[0]*3
        s=0
        for digit in digits:
            s+=digit
            count[digit]+=1
            modulo[digit%3]+=1
        if s%3==1:
            if modulo[1]>=1:
                rcnt=1
                rmod=1
            else:
                rcnt=2
                rmod=2
        elif s%3==2:
            if modulo[2]>=1:
                rcnt=1
                rmod=2
            else:
                rcnt=2
                rmod=1
        else:
            rcnt=0
            rmod=0
        ans=""
        for i in range(10):
            for j in range(count[i]):
                if rcnt>0 and i%3==rmod:
                    rcnt-=1
                else:
                    ans+=str(i)
                
        if len(ans)>0 and ans[-1]=='0':
            return '0'
        return ans[::-1]
403. 青蛙过河

403. 青蛙过河

时间复杂度: O ( N 2 ) O(N^2) O(N2)

class Solution:
    def canCross(self, stones: List[int]) -> bool:
        mapper={}
        for stone in stones:
            mapper[stone]=set()
        mapper[0].add(0)
        for stone in stones:
            for k in mapper[stone]:
                for step in range(k-1,k+2):
                    if step>0 and stone+step in mapper:
                        mapper[stone+step].add(step)
        return len(mapper[stones[-1]])>0
不同路径(考虑障碍物的DP)

62. 不同路径

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        dp=[[0]*(n+1) for _ in range(m+1)]
        dp[1][1]=1
        for i in range(1, m+1):
            for j in range(1, n+1):
                if i==1 and j==1:
                    continue
                dp[i][j]=dp[i-1][j]+dp[i][j-1]
        return dp[m][n]

63. 不同路径 II

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        m=len(obstacleGrid)
        n=len(obstacleGrid[0])
        dp=[[0]*(n+1) for _ in range(m+1)]
        # dp[1][1]=1
        for i in range(1, m+1):
            for j in range(1, n+1):
                if obstacleGrid[i-1][j-1]==1:
                    continue
                if i==1 and j==1:
                    dp[1][1]=1
                    continue
                dp[i][j]=dp[i-1][j]+dp[i][j-1]

        return dp[m][n]
股票难题

188. 买卖股票的最佳时机 IV

class Solution:
    def maxProfit(self, k: int, prices: List[int]) -> int:
        N=len(prices)
        K=k
        dp=[[[0]*2 for _ in range(K+1)] for _ in range(N+1)]
        for k in range(K+1):
            dp[0][k][1]=-inf
        for i in range(N+1):
            dp[i][0][1]=-inf
        for i in range(1, N+1):
            # for k in range(K,0,-1):
            for k in range(1, K+1):
                # 卖
                dp[i][k][0]=max(dp[i-1][k][0], dp[i-1][k][1]+prices[i-1])
                # 买
                dp[i][k][1]=max(dp[i-1][k][1], dp[i-1][k-1][0]-prices[i-1])
        return dp[N][K][0]

309. 最佳买卖股票时机含冷冻期

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        N=len(prices)
        dp=[[0]*2 for _ in range(N+1)]
        dp[0][1]=-inf
        for i in range(1, N+1):
            # 卖
            dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i-1])
            # 买
            dp[i][1]=max(dp[i-1][1],dp[i-2][0]-prices[i-1])
        return dp[N][0]

714. 买卖股票的最佳时机含手续费

class Solution:
    def maxProfit(self, prices: List[int], fee: int) -> int:
        if not prices:
            return 0
        N = len(prices)
        dp_i_0 = 0
        dp_i_1 = -inf
        for i in range(N):
            dp_i_0 = max(dp_i_0, dp_i_1 + prices[i] - fee)
            dp_i_1 = max(dp_i_1, dp_i_0 - prices[i])
        return dp_i_0
315. 计算右侧小于当前元素的个数

315. 计算右侧小于当前元素的个数

其实和逆序对差不多,就是改为对index排序(要记录下标)

和逆序对一样,在归并的统计区做数据统计,其他基本不变

class Solution:
    def countSmaller(self, nums: List[int]) -> List[int]:

        counts = [0] * len(nums)
        index = [i for i in range(len(nums))]

        def merge_sort(l, r):
            if l >= r:
                return
            mid = (r + l) // 2
            merge_sort(l, mid)
            merge_sort(mid + 1, r)
            # 统计
            b = mid + 1
            for a in range(l, mid + 1):
                while b <= r and nums[index[b]] < nums[index[a]]:
                    b += 1
                counts[index[a]] += b - mid - 1
            # 归并
            a = l
            b = mid + 1
            merged = []
            while a <= mid and b <= r:
                if nums[index[a]] <= nums[index[b]]:
                    merged.append(index[a])
                    a += 1
                else:
                    merged.append(index[b])
                    b += 1
            while a <= mid:
                merged.append(index[a])
                a += 1
            while b <= r:
                merged.append(index[b])
                b += 1
            index[l:r + 1] = merged

        merge_sort(0, len(nums) - 1)
        # print(index)
        return counts
255. 验证前序遍历序列二叉搜索树

255. 验证前序遍历序列二叉搜索树

入栈的时候递减,出栈的时候递增

Python3 图解,栈

class Solution:
    def verifyPreorder(self, preorder: List[int]) -> bool:
        root=-inf
        stack=[]
        # 局部递减,全局递增
        for val in preorder:
            if val<root:
                return False
            while stack and stack[-1]<val:
                root=stack.pop()
            stack.append(val)
        return True

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

面试题33. 二叉搜索树的后序遍历序列(递归分治 / 单调栈,清晰图解)

class Solution:
    def verifyPostorder(self, postorder: List[int]) -> bool:
        root=inf
        stack=[]
        # 局部递增,全局递减
        for val in reversed(postorder):
            if val>root:
                return False
            while stack and stack[-1]>val:
                root=stack.pop()
            stack.append(val)
        return True
376. 摆动序列

376. 摆动序列

class Solution:
    def wiggleMaxLength(self, nums: List[int]) -> int:
        n=len(nums)
        if n<=1:
            return n
        pre_delta=nums[1]-nums[0]
        ans=1 if pre_delta==0 else 2
        for i in range(2,n):
            delta=nums[i]-nums[i-1]
            if (delta<0 and pre_delta>=0) or (delta>0 and pre_delta<=0):
                ans+=1
                pre_delta=delta
        return ans
582. 杀掉进程

582. 杀掉进程

  • 并查集

考虑一种极端情况,数的结构类似链表,那么时间复杂度直接飙到 N 2 N^2 N2,就TLE

hashmap并查集的话,如果某个结点不存在,如x not in parent,可以直接让parent[x]=x,然后返回x,这样连初始化都免了

class Solution:
    def killProcess(self, pid: List[int], ppid: List[int], kill: int) -> List[int]:
        result = [kill]
        parent = dict(zip(pid, ppid))
        parent[0] = 0
        for i in parent.keys():
            x = self.find(i, parent, kill)
            if x == kill:
                result.append(i)
        return result

    def find(self, x, parent, kill):
        # 在kill处早停
        if parent[x] == kill:
            return kill
        # 路径压缩
        p = parent[x]
        if parent[x] != x:
            parent[x] = self.find(parent[x], parent, kill)
        return parent[x]
  • 模拟树

的确是种好方法

反正都差不多,这样就 O ( N ) O(N) O(N)

# 模拟树(哈希表)
class Node:
    def __init__(self,val,children):
        self.val = val
        self.children = children 

class Solution:
    def killProcess(self, pid: List[int], ppid: List[int], kill: int) -> List[int]:
        def getAllChildren(p, l):
            """递归搜索子进程"""
            for n in p.children:
                l.append(n.val)
                getAllChildren(n,l)
                
        mapping = {}
        for _, val in enumerate(pid):
            mapping[val] = Node(val,[])
        for i in range(len(ppid)): # 父子关系连接
            if ppid[i] >0:
                cur = mapping[ppid[i]]
                cur.children.append(mapping[pid[i]])

        l  = []
        l.append(kill)
        getAllChildren(mapping[kill],l)
        return l 
718. 最长重复子数组

数据结构与算法重难点100道_字符串_07

718. 最长重复子数组

  • 动态规划

O ( N M ) O(NM) O(NM)

class Solution:
    def findLength(self, A: List[int], B: List[int]) -> int:
        n, m = len(A), len(B)
        dp = [[0] * (m + 1) for _ in range(n + 1)]
        ans = 0
        for i in range(1,n+1):
            for j in range(1,m+1):
                dp[i][j] = dp[i - 1][j - 1] + 1 if A[i-1] == B[j-1] else 0
                ans = max(ans, dp[i][j])
        return ans

  • 滑动窗口
class Solution:
    def findLength(self, A: List[int], B: List[int]) -> int:
        def maxLength(addA: int, addB: int, length: int) -> int:
            ret = k = 0
            for i in range(length):
                if A[addA + i] == B[addB + i]:
                    k += 1
                    ret = max(ret, k)
                else:
                    k = 0
            return ret

        n, m = len(A), len(B)
        ret = 0
        for i in range(n):
            length = min(m, n - i)
            ret = max(ret, maxLength(i, 0, length))
        for i in range(m):
            length = min(n, m - i)
            ret = max(ret, maxLength(0, i, length))
        return ret
class Solution {
public:
    int findLength(vector<int> &A, vector<int> &B) {
        int m = A.size(), n = B.size(), res = 0;
        // 枚举对应关系
        for (int diff = -(m - 1); diff <= n - 1; ++diff) {
            // 遍历公共部分
            for (int i = max(0, -diff), l = 0; i < min(m, n - diff); ++i) {
                l = (A[i] == B[i + diff]) ? (l + 1) : 0;
                res = max(res, l);
            }
        }
        return res;
    }
};
剑指 Offer 41. 数据流中的中位数
class MedianFinder {
public:
    priority_queue<int> lo;
    priority_queue<int, vector<int>, greater<int> > hi;

    void addNum(int num) {
        lo.push(num);
        hi.push(lo.top());
        lo.pop();
        if (lo.size() < hi.size()) {
            lo.push(hi.top());
            hi.pop();
        }
    }

    double findMedian() {
        if (lo.size() == hi.size()) {
            return (lo.top() + hi.top()) / 2.0;
        } else {
            return lo.top();
        }
    }
};
516. 最长回文子序列

516. 最长回文子序列

class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        n = len(s)
        dp = [[0] * n for _ in range(n)]
        # 初始化1
        for i in range(n):
            dp[i][i] = 1
        # 初始化2 
        for i in range(n - 1):
            dp[i][i + 1] = 2 if s[i] == s[i + 1] else 1
        # 遍历
        # 间隔
        for k in range(2,n):
            # 起点
            for i in range(n - k):
                # 终点
                j = i + k
                if s[i] == s[j]:
                    dp[i][j] = dp[i + 1][j - 1] + 2
                else:
                    dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
        return dp[0][n - 1]
97. 交错字符串
class Solution:
    def isInterleave(self, s1: str, s2: str, s3: str) -> bool:
        l1=len(s1)
        l2=len(s2)
        l3=len(s3)
        if l1+l2!=l3:
            return False
        dp=[[False]*(l2+1) for _ in range(l1+1)]
        dp[0][0]=True
        for i in range(1,l1+1):
            dp[i][0]=(dp[i-1][0] and s1[i-1]==s3[i-1])
        for i in range(1,l2+1):
            dp[0][i]=(dp[0][i-1] and s2[i-1]==s3[i-1])
        for i in range(1,l1+1):
            for j in range(1,l2+1):
                if s3[i+j-1]==s1[i-1] and dp[i-1][j]:
                    dp[i][j]=True
                elif s3[i+j-1]==s2[j-1] and dp[i][j-1]:
                    dp[i][j]=True
        return dp[l1][l2]
416. 分割等和子集

todo: 条件可以改成尽量分成两个子集,求选中了哪些物品

其实这题就是转化过的01背包

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        L = len(nums)
        sum_ = sum(nums)
        if sum_ % 2:
            return False
        target = sum_ // 2
        # L 行 target + 1 列
        dp = [[0] * (target + 1) for _ in range(L + 1)]
        # 容量为0的时候,绝逼恰好能装满
        dp[0][0] = True
        # 处理数据,让nums从1索引
        nums.insert(0,0)
        # 开始DP
        for i in range(1, L+1):
            for j in range(target + 1):
                if j >= nums[i]:
                    dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i]]
                else:
                    dp[i][j] = dp[i - 1][j]
            # 只装i个物品就装满了
            if dp[i][target]:
                return True
        return False
312. 戳气球

312. 戳气球

经典动态规划:戳气球问题

有空再研究

数据结构与算法重难点100道_二叉树_08

class Solution:
    def maxCoins(self, nums: List[int]) -> int:
        n = len(nums)
        points = [1] * (n + 2)
        for i in range(n):
            points[i + 1] = nums[i]
        dp = [[0] * (n + 2) for _ in range(n + 2)]
        for itv in range(2, n+2):  # [2,n+1]
            for i in range(0, n - itv + 2):
                j = i + itv
                for k in range(i + 1, j):
                    dp[i][j] = max(
                        dp[i][j],
                        dp[i][k] + dp[k][j] + points[i] * points[j] * points[k],
                    )
        return dp[0][n + 1]
583. 两个字符串的删除操作

583. 两个字符串的删除操作

def longestCommonSubsequence(text1: str, text2: str) -> int:
    l1=len(text1)
    l2=len(text2)
    dp=[[0]*(l2+1) for _ in range(l1+1)]
    for i in range(1, l1+1):
        for j in range(1, l2+1):
            if text1[i-1]==text2[j-1]:
                dp[i][j]=dp[i-1][j-1]+1
            else:
                dp[i][j]=max(dp[i][j-1],dp[i-1][j])
    return dp[l1][l2]

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        return len(word1)+len(word2)-2*longestCommonSubsequence(word1,word2)

712. 两个字符串的最小ASCII删除和

详解最长公共子序列问题,秒杀三道动态规划题目

234. 回文链表

234. 回文链表

  • 递归
class Solution:

    def recur(self,left):
        if left is None:
            return True
        ans=self.recur(left.next)
        ans &= (left.val==self.right.val)
        self.right=self.right.next
        return ans

    def isPalindrome(self, head: ListNode) -> bool:
        self.right=head
        return self.recur(head)
  • 快慢+翻转
def reverse(head):
    if head is None or head.next is None:
        return head
    last=reverse(head.next)
    head.next.next=head
    head.next=None
    return last

class Solution:

    def isPalindrome(self, head: ListNode) -> bool:
        if head is None or head.next is None:
            return True
        slow=head
        fast=head.next
        while fast and fast.next:
            slow=slow.next
            fast=fast.next.next
        p2=slow.next
        slow.next=None
        p2=reverse(p2)
        p1=head
        while p1 and p2:
            if p1.val !=p2.val:
                return False
            p1=p1.next
            p2=p2.next
        return True
726. 原子的数量

726. 原子的数量


class Solution:
    def countOfAtoms(self, formula: str):
        N = len(formula)
        stack = [Counter()]
        i = 0
        while i < N:
            if formula[i] == "(":
                stack.append(Counter())
                i += 1
            elif formula[i] == ")":
                top = stack.pop()
                i += 1
                i_start = i
                while i < N and formula[i].isdigit():
                    i += 1
                multi = int(formula[i_start:i] or 1)
                for name, cnt in top.items():
                    stack[-1][name] += cnt * multi
            else:
                i_start = i
                i += 1
                while i < N and formula[i].islower():
                    i += 1
                name = formula[i_start:i]
                i_start = i
                while i < N and formula[i].isdigit():
                    i += 1
                multi = int(formula[i_start:i] or 1)
                stack[-1][name] += multi
        return "".join(name + (str(stack[-1][name]) if stack[-1][name] > 1 else '')
                       for name in sorted(stack[-1]))
405. 数字转换为十六进制数

405. 数字转换为十六进制数

class Solution {
public:
    string get_hex(int num) {
        char chr[2] = {0};
        if (num <= 9) chr[0] = char('0' + num);
        else chr[0] = char('a' + num - 10);
        return string(chr);
    }

    string toHex(int num) {
        if(num==0)
            return "0";        
        unsigned int u = num;
        string ans;
        while (u) {
            ans += get_hex(u & 15);
            u >>= 4;
        }
        reverse(ans.begin(), ans.end());
        return ans;
    }
};
1. 两数之和
class Solution {
public:
    vector<int> twoSum(vector<int> &nums, int target) {
        map<int, int> sub2ix;
        for (int i = 0; i < nums.size(); ++i) {
            int sub = target - nums[i];
            if (sub2ix.count(sub))
                return {i, sub2ix[sub]};
            else
                sub2ix[nums[i]] = i;
        }
        return {0, 0};
    }
};
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        num2ix = {}
        for i, num in enumerate(nums):
            other = target - num
            if other in num2ix:
                return [i, num2ix[other]]
            num2ix[num] = i
        raise ValueError

如果遇到N数之和的问题,马上想到东哥的NSum函数,关键知识点:

  • 去重
  • 遍历前排序
  • 双指针
  • 递归
class Solution {
public:
    vector<vector<int>> nSum(vector<int> &nums, int n, int start, int target) {
        int sz = nums.size();
        vector<vector<int>> ans;
        if (n < 2 || sz < n)
            return ans;
        if (n == 2) {
            int lo = start, hi = sz - 1;
            while (lo < hi) {
                int left = nums[lo];
                int right = nums[hi]; //忘了使用 nums[]
                if (nums[lo] + nums[hi] < target)
                    while (lo < hi && nums[lo] == left) lo++;
                else if (nums[lo] + nums[hi] > target)
                    while (lo < hi && nums[hi] == right) hi--;
                else {
                    ans.push_back({nums[lo], nums[hi]});
                    // 等号判断条件写错
                    while (lo < hi && nums[lo] == left) lo++;
                    while (lo < hi && nums[hi] == right) hi--;
                }
            }
        } else {
            int i = start;
            while (i < sz) { // 与3数和不同,这题的递归函数中考虑了 sz<n 的边界条件, 所以这里的边界条件可以简化
                int cur_num = nums[i];
                vector<vector<int>> cur_ans = nSum(nums, n - 1, i + 1, target - nums[i]);
                if (cur_ans.size() > 0) {
                    for (auto vec:cur_ans) {
                        vec.push_back(nums[i]);
                        ans.push_back(vec);
                    }
                }
                while (i < sz and nums[i] == cur_num) i++;
            }
        }
        return ans;
    }

    vector<vector<int>> fourSum(vector<int> &nums, int target) {
        sort(nums.begin(),nums.end());
        return nSum(nums, 4, 0, target);
    }
};
3. 无重复字符的最长子串

滑动窗口

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        set<char> window;
        // ans 应该初始化为0而不是负数,以适应s为空的情况
        int l = 0, ans = 0; 
        for (int r = 0; r < s.size(); ++r) {
            char c = s[r];
            while (window.count(c)) {
                window.erase(s[l++]);
            }
            window.insert(c);
            ans = max(ans, (int) window.size());
        }
        return ans;
    }
};
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        window = set()
        l = 0
        max_len = 0
        for r in range(len(s)):
            # 右端点元素进窗口之前,需要通过不断右移左端点保证当前窗口所有元素唯一
            while s[r] in window:
                window.remove(s[l])
                l += 1
            window.add(s[r])
            max_len = max(max_len, r - l + 1)  # 可以用 len(windows)代替
        return max_len
  1. 至多包含 K 个不同字符的最长子串

数据结构与算法重难点100道_字符串_09

class Solution:
    def lengthOfLongestSubstringKDistinct(self, s: str, k: int) -> int:
        window = {}
        l = 0
        max_len = 0
        for r in range(len(s)):
            window[s[r]] = window.get(s[r], 0) + 1
            # 注意是 <= ,考虑 k=0 的边界
            while l <= r and len(window) > k:
                window[s[l]] -= 1
                if window[s[l]] == 0:
                    window.pop(s[l])
                l += 1
            max_len = max(max_len, r - l + 1)
        return max_len
  • 992.K 个不同整数的子数组

挂起

992. K 个不同整数的子数组

2. 两数相加

链表

2. 两数相加

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        dummy = ListNode()
        p = dummy
        p1 = l1
        p2 = l2
        carry = 0
        while p1 or p2:
            v1 = p1.val if p1 else 0
            v2 = p2.val if p2 else 0
            val = v1 + v2 + carry
            p.next = ListNode(val % 10)
            p = p.next
            carry = val // 10
            p1 = p1.next if p1 else None
            p2 = p2.next if p2 else None
        if carry:
            p.next = ListNode(carry)
        return dummy.next
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* p1=l1, *p2=l2;
        int carry=0;
        ListNode *dummy=new ListNode(0);
        ListNode *dp=dummy;
        while(p1!=nullptr || p2!=nullptr || carry){
            int a=p1==nullptr?0:p1->val;
            int b=p2==nullptr?0:p2->val;
            int s=a+b+carry;
            dp->next=new ListNode(s%10);
            dp=dp->next;
            carry=(int)s/10;
            p1=p1==nullptr?nullptr:p1->next;
            p2=p2==nullptr?nullptr:p2->next;
        }
        return dummy->next;
    }
};

445. 两数相加 II

相比于原版倒了,可以用【栈】或者【翻转链表】

371. 两整数之和

位运算

67. 二进制求和

模拟二进制加法

989. 数组形式的整数加法

数据结构与算法重难点100道_链表_10


两个大数的题

415. 字符串相加

43. 字符串相乘

5. 最长回文子串
  • 中心扩散法
  • DP(待办)
class Solution:
    def longestPalindrome(self, s: str) -> str:

        def func(s, i, j):
            while 0 <= i and j < len(s) and s[i] == s[j]:
                i -= 1
                j += 1
            i += 1
            j -= 1
            return i, j

        sa, sb = 0, 0
        for i in range(len(s)):
            a, b = func(s, i, i)
            if b - a > sb - sa:
                sa, sb = a, b
            if i < len(s) - 1:
                a, b = func(s, i, i + 1)
                if b - a > sb - sa:
                    sa, sb = a, b
        return s[sa:(sb + 1)]

动态规划——你超容易理解的船新版本

数据结构与算法重难点100道_字符串_11

214. 最短回文串

哈希,KMP,马拉车。。我先选择放弃

数据结构与算法重难点100道_链表_12

数据结构与算法重难点100道_字符串_13
奇数key不超过1

return sum(1 for val, cnt in collections.Counter(s).items() if cnt%2)<=1

336. 回文对

字典树+马拉车,放弃

516. 最长回文子序列

子序列问题通用思路|最长回文子序列

class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        n = len(s)
        dp = [[0] * n for _ in range(n)]
        for i in range(n):
            dp[i][i] = 1
        for i in range(n - 1):
            dp[i][i + 1] = 2 if s[i] == s[i + 1] else 1
        for k in range(2,n):
            for i in range(n - k):
                j = i + k
                if s[i] == s[j]:
                    dp[i][j] = dp[i + 1][j - 1] + 2
                else:
                    dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
        return dp[0][n - 1]

647. 回文子串

“马拉车”算法:基于中心扩散+剪枝原理

有空看,头痛

应该就是特化版的

但是马拉车很快,ON

数据结构与算法重难点100道_数组_14

class Solution:
    #中心扩散+马拉车优化 时间复杂度o(n),空间复杂度 o(n)
    def countSubstrings(self, s: str) -> int:
        s = "#" + "#".join(s) + "#"
        right = -1
        maxlen = [0]*len(s)
        res = 0
        for i in range(len(s)):
            if i<=right:
                im = 2*j-i
                minlen = min(maxlen[im],right-i)
                tmp = self.expand(s,i-minlen,i+minlen)
            else:
                tmp = self.expand(s,i,i)
            maxlen[i] = tmp
            if i+tmp>right:
                right = i+tmp
                j = i
            if i%2==1:
                res += maxlen[i]//2 + 1
            else:
                res += (maxlen[i]+1)//2
        return res


    def expand(self,s,l,r):
        while 0<=l and r<len(s) and s[l] == s[r]:
            l -= 1
            r += 1
        return (r-l)//2 - 1
class Solution:
    def countSubstrings(self, s: str) -> int:
        res = 0
        n = len(s)
        dp = [[0] * n for _ in range(n)]
        for i in range(n):
            for j in range(i + 1):
                if s[i] == s[j] and (i - j < 2 or dp[j + 1][i - 1]):
                    dp[j][i] = 1
                    res += 1
        return res
class Solution:
    def countSubstrings(self, s: str) -> int:
        L = len(s)
        cnt = 0
        # 以某一个元素为中心的奇数长度的回文串的情况
        for center in range(L):
            left = right = center
            while left >= 0 and right < L and s[left] == s[right]:
                cnt += 1
                left -= 1
                right += 1
        # 以某对元素为中心的偶数长度的回文串的情况
        for left in range(L - 1):
            right = left + 1
            while left >= 0 and right < L and s[left] == s[right]:
                cnt += 1
                left -= 1
                right += 1

        return cnt
42. 接雨水

42. 接雨水

  • DP
class Solution:
    def trap(self, height: List[int]) -> int:
        sum = 0
        n = len(height)
        max_left = [0] * n
        max_right = [0] * n
        for i in range(1, n - 1):
            max_left[i] = max(max_left[i - 1], height[i - 1])
        for i in reversed(range(1, n - 1)):  # range(n - 2, 0, -1):
            max_right[i] = max(max_right[i + 1], height[i + 1])
        for i in range(1, n - 1):
            min_height = min(max_left[i], max_right[i])
            sum += max(0, min_height - height[i])
        return sum
class Solution {
public:
    int trap(vector<int>& height) {
        int n=height.size();
        vector<int> left(n,0);
        vector<int> right(n,0);
        int ans=0;
        for(int i=1;i<n;++i){
            left[i]=max(left[i-1],height[i-1]);
        }
        for(int i=n-2;i>=0;--i){
            right[i]=max(right[i+1],height[i+1]);
        }
        for(int i=1;i<n-1;++i){
            ans+=max(0,
               min(left[i],right[i])-height[i]
            );
        }
        return ans;
    }
};
  • 单调栈
class Solution:
    def trap(self, height: List[int]) -> int:
        n = len(height)
        stack = []
        ans = 0
        #         □
        # □       □
        # □ □   □ □
        # 2 1 0 1 3
        #       ↑ ↑ 触发出栈情况
        # h:    1 1
        # l:    1 3
        # 出站后的pre变量代表了前一个高度
        for i in range(n):
            while stack and height[stack[-1]] < height[i]:
                pre = stack.pop()
                # 如果stack为空,表示 [2, 3, 4] 这种情况,围不起来
                if not stack:
                    break
                t = stack[-1]
                h = min(height[t], height[i]) - height[pre]  # 当前高度 - 上一个高度
                l = i - t - 1
                ans += h * l
            stack.append(i)
        return ans

O ( N log ⁡ N ) O(N\log N) O(NlogN)

class Solution:
    def trap(self, height: List[int]) -> int:
        if not height:
            return 0
        n = len(height)
        vis=[0]*n
        pq=[]
        for i in (0, n-1):
            vis[i]=True
            heapq.heappush(pq, (height[i], i))
        ans=0
        while pq:
            wh,x=heapq.heappop(pq)
            for dx in [-1, 1]:
                cx=x+dx
                if not (0<=cx<n):
                    continue
                if vis[cx]:
                    continue
                ch=height[cx]
                ans+=max(0, wh-ch)
                vis[cx]=True
                heapq.heappush(pq,(max(ch,wh),cx))
        return ans

407. 接雨水 II

O ( N M log ⁡ ( N + M ) ) O(NM\log (N+M)) O(NMlog(N+M))

class Solution:
    def trapRainWater(self, heightMap: List[List[int]]) -> int:
        m, n = len(heightMap), len(heightMap[0])
        pq = []
        vis = [[0] * n for _ in range(m)]
        for i in range(m):
            for j in range(n):
                if i in (0, m - 1) or j in (0, n - 1):
                    heapq.heappush(pq, (heightMap[i][j], i, j))
                    vis[i][j] = 1
        dirs = [-1, 0, 1, 0, -1]
        ans = 0
        while pq:
            wh, x, y = heapq.heappop(pq)
            for k in range(4):
                nx, ny = x + dirs[k], y + dirs[k + 1]
                if 0 <= nx < m and 0 <= ny < n and vis[nx][ny] == 0:
                    ch = heightMap[nx][ny]
                    ans += max(0, wh - ch)
                    vis[nx][ny] = 1
                    heapq.heappush(pq, (max(wh, ch), nx, ny))
        return ans
206. 反转链表

我的天,写这种题都会卡壳

  • 迭代
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        p = head
        pre = None
        while p:
            aft = p.next
            p.next = pre
            pre = p
            p = aft
        return pre
  • 递归

递归法就记一张图

1->2->3->^
1->2<-3
   |->^
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        if head is None or head.next is None:
            return head
        last=self.reverseList(head.next)
        head.next.next=head
        head.next=None
        return last
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head==nullptr || head->next==nullptr)
            return head;
        ListNode* last=reverseList(head->next);
        head->next->next=head;
        head->next=nullptr;
        return last;
    }
};

递归反转链表的一部分

92. 反转链表 II

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

令人智熄的题

智商不够,记忆力来凑(背下来)

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        l1 = len(nums1)
        l2 = len(nums2)
        tot = l1 + l2

        def findKthElement(k):
            ix1 = ix2 = 0
            while True:
                # 注意顺序
                if ix1 == l1:
                    return nums2[ix2 + k - 1]
                if ix2 == l2:
                    return nums1[ix1 + k - 1]
                if k == 1:
                    return min(nums1[ix1], nums2[ix2])
                nix1 = min(ix1 + k // 2 - 1, l1 - 1)
                nix2 = min(ix2 + k // 2 - 1, l2 - 1)
                if nums1[nix1] < nums2[nix2]:
                    k -= (nix1 - ix1 + 1)  # -= 写成了 =
                    ix1 = nix1 + 1
                else:
                    k -= (nix2 - ix2 + 1)
                    ix2 = nix2 + 1

        return findKthElement(tot // 2 + 1) if tot % 2 else (findKthElement(tot // 2) + findKthElement(
            tot // 2 + 1)) / 2
21. 合并两个有序链表
class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        dummy=ListNode()
        p=dummy
        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
        p.next=l1 if l1 else l2
        return dummy.next

TODO: 合并K个有序链表

15. 三数之和
class Solution:
    def twoSum(self, nums, start, target):
        l = start
        r = len(nums) - 1
        ans = []
        while l < r:
            lo = nums[l]
            hi = nums[r]
            if lo + hi == target:
                ans.append([lo, hi])
                #         判断条件写成了 !=
                while l < r and nums[r] == hi: r -= 1 # 写成了 += 1
                while l < r and nums[l] == lo: l += 1
            elif lo + hi < target:
                while l < r and nums[l] == lo: l += 1
            else:
                while l < r and nums[r] == hi: r -= 1
        return ans

    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        n = len(nums)
        ans = []
        i = 0
        while (i < n - 2): # 错误地使用了for循环
            num = nums[i]
            cur_ans = self.twoSum(nums, i + 1, 0 - num)
            if cur_ans:
                ans += [item + [num] for item in cur_ans]
            while i < n - 2 and nums[i] == num: i += 1
        return ans
146. LRU 缓存机制

146. LRU 缓存机制

牛客 - [编程题]LRU缓存

看了一下以前的代码,迷迷糊糊默写的,居然过了,离谱。。

双向链表

  • addLast : 队尾入队
  • remove:删除一个结点
  • removeFirst:队首出队

LRUCache

  • makeRecently:将元素拿到队尾
  • removeLeastRecently:删除对少最近,其实就是删队头
  • addRecently: 入队
  • removeKey: 删除任意一个结点
class Node():
    def __init__(self, k, v):
        self.k = k
        self.v = v
        self.prev = None
        self.next = None


class DoubleList():
    def __init__(self):
        self.head = Node(0, 0)
        self.tail = Node(0, 0)
        self.head.next = self.tail
        self.tail.prev = self.head
        self.size = 0

    def addLast(self, x: Node):
        x.next = self.tail
        x.prev = self.tail.prev
        self.tail.prev.next = x
        self.tail.prev = x
        self.size += 1

    def remove(self, x):
        x.prev.next = x.next
        x.next.prev = x.prev
        self.size -= 1

    def removeFirst(self):
        if self.head.next == self.tail:
            return None
        first = self.head.next
        self.remove(first)
        return first


class LRUCache:

    def __init__(self, capacity: int):
        self.capacity = capacity
        self.cache = DoubleList()
        self.map: Dict[int, Node] = {}

    def get(self, key: int) -> int:
        if key in self.map:
            self.makeRecently(key)
            return self.map[key].v
        return -1

    def put(self, key: int, value: int) -> None:
        if key not in self.map:
            if self.cache.size == self.capacity:
                self.removeLeastRecently()
            self.addRecently(key, value)
        self.removeKey(key)
        self.addRecently(key, value)

    def makeRecently(self, key):
        node = self.map[key]
        self.cache.remove(node)
        self.cache.addLast(node)

    def removeLeastRecently(self):
        first = self.cache.removeFirst()
        self.map.pop(first.k)

    def addRecently(self, key, value):
        node = Node(key, value)
        self.map[key] = node
        self.cache.addLast(node)

如果用Java的LinkedHashSet来刷的话,会很方便,不过需要记住删队头结点的代码是取KeySet然后迭代得到第一个key最后remove掉

import java.util.LinkedHashMap;
import java.util.Scanner;

class LRU {
    int capacity;
    LinkedHashMap<Integer, Integer> cache;

    LRU(int capacity) {
        this.capacity = capacity;
        cache = new LinkedHashMap<>();
    }

    int makeRecently(int k) {
        int v = cache.get(k);
        cache.remove(k);
        cache.put(k, v);
        return v;
    }

    void addRecently(int k, int v) {
        cache.put(k, v);
    }

    void removeKey(int k) {
        cache.remove(k);
    }

    void removeLeastRecently() {
        int oldestKey = cache.keySet().iterator().next();
        cache.remove(oldestKey);
    }

    void put(int k, int v) {
        if (cache.containsKey(k)) {
            removeKey(k);
            addRecently(k, v);
        } else {
            if (cache.size() >= capacity) {
                removeLeastRecently();
            }
            addRecently(k, v);
        }
    }

    int get(int k) {
        if (cache.containsKey(k))
            return makeRecently(k);
        return -1;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        int capacity = input.nextInt();
        input.nextLine();
        LRU lru = new LRU(capacity);
        for (int i = 0; i < n; i++) {
            String[] line = input.nextLine().split(" ");
            if (line[0].equals("GET")) System.out.println(lru.get(Integer.valueOf(line[1])));
            if (line[0].equals("PUT")) lru.put(Integer.valueOf(line[1]), Integer.valueOf(line[2]));
        }
    }
}

import java.util.*;


class LRU{
    int capacity;
    LinkedHashMap<Integer,Integer> cache;
    LRU(int capacity){
        this.capacity=capacity;
        cache=new LinkedHashMap<Integer,Integer>();
    }
    void put(int k, int v){
        if(cache.containsKey(k)){
            cache.remove(k);
            cache.put(k,v);
        }else{
            if(cache.size()>=capacity){
                int oldK=cache.keySet().iterator().next();
                cache.remove(oldK);
            }
            cache.put(k, v);
        }
    }
    int get(int k){
        if(cache.containsKey(k)){
            int v=cache.get(k);
            cache.remove(k);
            cache.put(k,v);
            return v;
        }
        return -1;
    }
}


public class Main{
    public static void main(String[] args){
        Scanner input = new Scanner(System.in);
        int n=input.nextInt();
        int capacity=input.nextInt();
        input.nextLine();
        LRU lru=new LRU(capacity);
        while(n-->0){
            String [] line=input.nextLine().split(" ");
            if(line.length==2){
                System.out.println(lru.get(Integer.parseInt(line[1])));
            }else{
                lru.put(Integer.parseInt(line[1]), Integer.parseInt(line[2]));
            }
        }
    }
}
53. 最大子序和
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        dp=0
        ans=-inf
        for num in nums:
            if dp<0: # 要写在前面
                dp=0
            dp+=num
            ans=max(ans,dp)
        return ans
25. K 个一组翻转链表

25. K 个一组翻转链表

class Solution:
    def reverse(self, a, b):
        # 相当于左开右闭,例如k=2,
        #  1->2->3
        # a↑    b↑
        # 最后返回是的 2->1->∅
        pre = None
        p = a
        while p != b:
            aft = p.next
            p.next = pre
            pre = p
            p = aft
        return pre

    def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
        a = b = head
        # 循环体写错 (老错误)
        for _ in range(k):
            if b is None:
                return head
            b = b.next
        new_head = self.reverse(a, b)
        # 把 a 写成了 new_head
        a.next = self.reverseKGroup(b, k) # b 写错 (老错误)
        return new_head

TODO: 默写

11. 盛最多水的容器
class Solution:
    def maxArea(self, height: List[int]) -> int:
        l=0
        n=len(height)
        r=n-1
        ans=0
        while l<r:
            ans=max((r-l)*min(height[l], height[r]),ans)
            if height[l]<height[r]:
                
                l+=1
            else:
                r-=1
        return ans

TODO: 单调栈

121. 买卖股票的最佳时机
class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        min_=inf
        ans=-inf
        for price in prices:
            min_=min(min_,price)
            ans=max(ans,price-min_)
        return ans

TODO: 刷其他5题相似的

33. 搜索旋转排序数组
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        n=len(nums)
        l=0
        r=n-1
        while l<=r:
            mid=(l+r)//2
            if nums[mid]==target:
                return mid
            # 左边有序
            if nums[l]<nums[mid] or l==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

81. 搜索旋转排序数组 II

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        n=len(nums)
        l=0
        r=n-1
        while l<=r:
            mid=(l+r)//2
            if nums[mid]==target:
                return True
            if nums[l]==nums[mid] and l!=mid:
                l+=1
                continue
            # 左边有序
            if nums[l]<nums[mid] or l==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 False

153. 寻找旋转排序数组中的最小值

class Solution:
    def findMin(self, numbers: List[int]) -> int:
        l = 0
        r = len(numbers) - 1
        while l < r:
            mid = (l + r) // 2
            if numbers[l] >= numbers[r]:
                if numbers[mid] < numbers[r]:
                    r = mid
                else:
                    l = mid + 1
            else:
                return numbers[l]
        return numbers[r]
46. 全排列

TODO:

60. 排列序列


46. 全排列

复习了一下笔记。粘贴我的笔记:

剑指 Offer 38. 字符串的排列

面试题38. 字符串的排列(回溯法,清晰图解)

我采用中规中矩的排列树做的,最后加个去重,并对两个参数的取值进行了思考

class Solution:
    def permutation(self, s: str) -> List[str]:
        path = list(s)
        ans = []
        L = len(path)

        def backtrace(t):
            # t == L 和 t == L - 1 一样
            # 因为如果 t == L 的话,在上一个状态 t=L-1时,
            # 是在做原地交换,相当于没做任何事
            # 所以 t == L - 1 即可
            if t == L - 1:  
                ans.append("".join(path))
                return
            # 初始条件必须是 t 而不是 t+1
            # t 看似是在原地交换,但是要考虑当前状态不做任何事的情况进入下个状态
            for i in range(t, L):  # i 最大为 L-1
                path[t], path[i] = path[i], path[t]
                backtrace(t + 1)
                path[t], path[i] = path[i], path[t]

        backtrace(0)
        return list(set(ans))

看了题解后,我做了修改

虽然时间复杂度还是 O ( N ! ) O(N!) O(N!) 但是实际的运行时间还是会减少的

class Solution:
    def permutation(self, s: str) -> List[str]:
        path = list(s)
        ans = []
        L = len(path)

        def backtrace(t):
            if t == L :  
                ans.append("".join(path))
                return
            visit = set()
            for i in range(t, L): 
                if path[i] in visit:
                    continue
                visit.add(path[i])
                path[t], path[i] = path[i], path[t]
                backtrace(t + 1)
                path[t], path[i] = path[i], path[t]

        backtrace(0)
        return list(set(ans))
20. 有效的括号

20. 有效的括号

class Solution:
    def isValid(self, s: str) -> bool:
        stack=collections.deque()
        mapper={
            ')':'(',
            '}':'{',
            ']':'[',
        }
        for c in s:
            if c in mapper:
                if not stack:
                    return False
                if mapper[c]!=stack.pop():
                    return False
            else:
                stack.append(c)
        return not stack # 这里写错
31. 下一个排列

31. 下一个排列

next-permutation的题,记3个图,3个步骤就好了

  1. 单调下降的序列无论怎么交换元素,无无法让字典序更大
  2. 只能找到单调下降前的那个元素,让那个元素稍稍变大一点
  3. 搞完之后把单调下降的序列变成单调增序列,因为除了要让字典序变大,还要保证变大后的字典序是尽可能小的。

数据结构与算法重难点100道_字符串_15数据结构与算法重难点100道_时间复杂度_16
数据结构与算法重难点100道_链表_17

编码时多想想case,1 5 1, 1 1 5, 5 1 1之类的 ,弄清楚等号

class Solution:
    def nextPermutation(self, nums: List[int]) -> None:
        n=len(nums)
        # 找到第一个比后面小的元素
        i=n-2
        while i>=0 and nums[i]>=nums[i+1]:
            i-=1
        if i>=0:
            j=n-1          # [1,5,1] 的 case
            while j>=0 and nums[i]>=nums[j]: # 就差一个等于号就全盘写对了,牢记!!!!!!!!
                j-=1
            nums[i], nums[j]=nums[j], nums[i]
        i=i+1
        j=n-1
        while i<j:
            nums[i],nums[j]=nums[j],nums[i]
            i+=1
            j-=1

60. 排列序列

python3 超详细多解法

23. 合并K个升序链表

23. 合并K个升序链表

方法 暴力 分治
时间复杂度 O ( k 2 n ) O(k^2n) O(k2n) O ( k log ⁡ k n ) O(k\log kn) O(klogkn) O ( k log ⁡ k n ) O(k\log kn) O(klogkn)
空间复杂度 O ( 1 ) O(1) O(1) O ( log ⁡ k ) O(\log k) O(logk) O ( n ) O(n) O(n)

数据结构与算法重难点100道_链表_18

class MyNode:
    def __init__(self, node: ListNode):
        self.node = node

    def __lt__(self, other):
        return self.node.val < other.node.val


class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        my_lists=[MyNode(node) for node in lists if node]  # 记得把阴间结点去掉
        heapq.heapify(my_lists)
        dummy = ListNode(0)
        p = dummy
        while my_lists:
            node = heapq.heappop(my_lists).node
            p.next = node
            p = p.next
            if node.next:
                heapq.heappush(my_lists, MyNode(node.next))
        p.next = None
        return dummy.next
def merge(a: ListNode, b: ListNode):
    dummy = ListNode(0)
    dp = dummy
    while a and b:
        if a.val < b.val:
            dp.next = a
            a = a.next
        else:
            dp.next = b
            b = b.next
        dp = dp.next
    dp.next = a if a else b
    return dummy.next


def partition(lists):
    n = len(lists)
    if n == 0:
        return None
    elif n == 1:
        return lists[0]
    else:
        mid = len(lists) // 2
        return merge(partition(lists[:mid]), partition(lists[mid:]))


class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        return partition(lists)

56. 合并区间

56. 合并区间

  • 二刷 默都默不出
class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        intervals.sort(key=lambda x:x[0])
        res=[]
        for itv in intervals:
            if res and res[-1][1]>=itv[0]: # >=
                res[-1][1]=max(res[-1][1], itv[1])
            else:
                res.append(itv)

        return res

57. 插入区间

数据结构与算法重难点100道_时间复杂度_19

直接在上题基础上append一个newInterval,然后用上题的代码做

但是这么做会导致时间复杂度为 O ( N log ⁡ N ) O(N\log N) O(NlogN)

但没有关系,插入的时候用类似插入排序的做法就OK了

986. 区间列表的交集

数据结构与算法重难点100道_字符串_20
用类似归并排序的方法做

class Solution:
    def intervalIntersection(self, A: List[List[int]], B: List[List[int]]) -> List[List[int]]:
        i = j = 0
        res = []
        while i < len(A) and j < len(B):
            a1, a2 = A[i][0], A[i][1]
            b1, b2 = B[j][0], B[j][1]
            if a1 <= b2 and a2 >= b1:
                res.append([max(a1, b1), min(a2, b2)])
            if b2 > a2:
                i += 1
            else:
                j += 1
        return res

435. 无重叠区间

数据结构与算法重难点100道_二叉树_21

class Solution:
    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
        if not intervals:
            return 0
        
        intervals.sort(key=lambda x: x[1])
        n = len(intervals)
        right = intervals[0][1]
        ans = 1

        for i in range(1, n):
            if intervals[i][0] >= right:
                ans += 1
                right = intervals[i][1]
        
        return n - ans
70. 爬楼梯

70. 爬楼梯

class Solution:
    def climbStairs(self, n: int) -> int:
        dp=[0]*(n+1)
        dp[0]=dp[1]=1
        for i in range(2, n+1):
            dp[i]=dp[i-1]+dp[i-2]
        return dp[n]

TODO: 常数时间复杂度

变态跳台阶

剑指Offer(九):变态跳台阶

f ( n ) = 2 f ( n − 1 ) f(n)=2f(n-1) f(n)=2f(n1)

# -*- coding:utf-8 -*-
class Solution:
    def jumpFloorII(self, number):
        # write code here
        if number <= 2:
            return number
        total = 1
        for _ in range(1, number):
            total *= 2
        return total

跳台阶,不能连续跳2阶

跳台阶基础版、改进版(不能连续跳2阶)

f f f: 跳到n阶的总方法数
g g g: 最后一步跳一阶跳到n阶的总方法数

初始值:
f = [ 1 , 2 , 3 ] f=[1,2,3] f=[1,2,3]
g = [ 1 , 1 , 2 ] g=[1,1,2] g=[1,1,2]

初始值很容易求得, f f f不用说, g g g可以用 g ( n ) = f ( n − 1 ) g(n)=f(n-1) g(n)=f(n1)求得

f ( n ) = f ( n − 1 ) + g ( n − 2 ) f(n)=f(n-1)+g(n-2) f(n)=f(n1)+g(n2)

解释:跳一步+跳两步。跳两步是最后一步跳一阶的情况

g ( n ) = f ( n − 1 ) g(n)=f(n-1) g(n)=f(n1)

解释:最后一步跳一阶

有趣的是,这两个递推公式可以合并

数据结构与算法重难点100道_字符串_22

public int modifiedJumpStairs(int n) {
    if (n <= 3)
        return n;

    int[] dp = new int[n];

    for (int i = 0; i < n; i++) {
        if (i <= 2) {
            dp[i] = i+1;
        } else {
            dp[i] = dp[i-1] + dp[i-3];
        }
    }
    return dp[dp.length-1];
}

7. 整数反转

7. 整数反转

class Solution {
public:
    int reverse(int x) {
        int ret=0;
        while(x!=0){
            int pop=x%10;
            x/=10;
            if(ret>INT_MAX/10 ||(ret==INT_MAX && pop>7)) return 0;
            if(ret<INT_MIN/10 ||(ret==INT_MIN && pop<-7)) return 0;
            ret=ret*10+pop;
        }
        return ret;
    }
};
215. 数组中的第K个最大元素

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

  • 调用系统的堆
// 除了用greater<int> , 还可以用以下的 cmp
struct cmp {
    bool operator()(int &a, int &b) {
        return a > b;  // 注意是 > 
    }
};

class Solution {
public:
    int findKthLargest(vector<int> &nums, int k) {
        priority_queue<int,vector<int>,greater<int> > heap;
        for (int num:nums) {
            if (heap.size() < k) {
                heap.push(num);
            } else if (num > heap.top()) {
                heap.pop();
                heap.push(num);
            }
        }
        return heap.top();
    }
};
  • 自己写堆的解法
void sink(vector<int> &nums, int n, int k) {
    while (true) {
        int i = k * 2 + 1, j = k * 2 + 2;
        if (i >= n) break;
        int ix = i;
        if (j < n && nums[j] < nums[i]) ix = j;
        if (nums[k] < nums[ix]) break;
        swap(nums[k], nums[ix]);
        k = ix;
    }
}

void swim(vector<int> &nums, int k) {
    for (int p = (k - 1) / 2; p >= 0 && nums[p] > nums[k]; k = p, p = (k - 1) / 2) {
        swap(nums[p], nums[k]);
    }
}


class Solution {
public:
    int findKthLargest(vector<int> &nums, int k) {
        vector<int> heap;
        for (int num:nums) {
            if (heap.size() < k) {
                heap.push_back(num);
                swim(heap, heap.size() - 1);
            } else if (num > heap[0]) {
                heap[0] = num;
                sink(heap, heap.size(), 0);
            }
        }
        return heap[0];
    }
};
  • cpp写的快排
int partition(vector<int> &nums, int l, int r) {
    int rix = rand() % (r - l + 1) + l;
    swap(nums[rix], nums[l]);
    int j = l, pivot = nums[l];
    for (int i = l + 1; i <= r; ++i) {
        if (nums[i] < pivot) {
            ++j;
            swap(nums[j], nums[i]);
        }
    }
    swap(nums[j], nums[l]);
    return j;
}


class Solution {
public:
    int findKthLargest(vector<int> &nums, int k) {
        int n = nums.size();
        int l = 0, r = n - 1;
        int target = n - k;
        while (true) {
            int j = partition(nums, l, r);
            if (j == target) return nums[j];
            else if (j < target) l = j + 1;
            else r = j - 1;
        }
    }
};

TODO: 用cpp刷, 快排

通过 partition 减治 + 优先队列(Java、C++、Python)

发现了一种更好写的Partition方法。

方法 时间复杂度 空间复杂度
partition分治 O ( N ) O(N) O(N) O ( 1 ) O(1) O(1)
堆方法 O ( N log ⁡ K ) O(N\log K) O(NlogK) O ( K ) O(K) O(K)
def partition(nums, l, r):
    rix = random.randint(l, r)
    nums[rix], nums[l] = nums[l], nums[rix]

    j = l
    pivot = nums[l]
    for i in range(l + 1, r + 1):
        if nums[i] < pivot:
            j += 1
            nums[j], nums[i] = nums[i], nums[j]
    nums[l], nums[j] = nums[j], nums[l]
    return j


def partition2(nums, l, r):
    rix = random.randint(l, r)
    nums[rix], nums[l] = nums[l], nums[rix]

    pivot = nums[l]
    while l < r:
        while l < r and nums[r] > pivot:
            r -= 1
        if l < r:
            nums[l], nums[r] = nums[r], nums[l]
            l += 1
        while l < r and nums[l] < pivot:
            l += 1
        if l < r:
            nums[l], nums[r] = nums[r], nums[l]
            r -= 1
    nums[l] = pivot
    return l


class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        n = len(nums)
        l, r = 0, n - 1
        target = n - k
        while True:
            index = partition2(nums, l, r)
            if index == target:
                return nums[index]
            elif index < target:
                l = index + 1
            else:
                r = index - 1
199. 二叉树的右视图

199. 二叉树的右视图

class Solution:
    def rightSideView(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        queue=[root]
        ans=[]
        while queue:
            sz=len(queue)
            cur_level=[]
            for _ in range(sz):
                top=queue.pop(0)
                cur_level.append(top.val)
                if top.left:
                    queue.append(top.left)
                if top.right:
                    queue.append(top.right)
            ans.append(cur_level[-1])
        return ans
124. 二叉树中的最大路径和

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

class Solution(object):
    def maxPathSum(self, root: TreeNode) -> int:
        res = -inf # 写成了0 错了

        def dfs(node):
            nonlocal res
            if node is None:
                return 0
            left = max(0, dfs(node.left))
            right = max(0, dfs(node.right))
            res = max(res, (left + right + node.val))
            return max(left, right) + node.val

        dfs(root)
        return res
200. 岛屿数量

200. 岛屿数量

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        n=len(grid)
        if not n:
            return 0
        m=len(grid[0])
        vis=[[0]*m for _ in range(n)]
        def dfs(x, y):
            if not (0<=x<n and 0<=y<m):
                return 
            if vis[x][y] or grid[x][y]=='0':
                return
            vis[x][y]=1
            dfs(x+1,y)
            dfs(x-1,y)
            dfs(x,y-1)
            dfs(x,y+1)
        ans=0
        for x in range(n):
            for y in range(m):
                if vis[x][y] or grid[x][y]=='0':
                    continue
                dfs(x, y)
                ans+=1
        return ans
92. 反转链表 II

92. 反转链表 II

class Solution:
    def reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode:
        # 以case 1, 2, 3, 4, 5 为例,left = 2, right = 4 
        p = head
        L = right - left + 1 # 翻转的部分是 2 3 4, 长度为3
        dummy=ListNode(0)
        dp=dummy
        for _ in range(left - 1): # 把 翻转部分 2 之前的添加到dummy中(dummy用来返回)
            dp.next=p
            dp=dp.next
            p = p.next
        # 迭代完之后 p=2
        aft = p.next
        pre = None
        l2_tail = p  # 第二段(l2) 的开头,但翻转后会变成末尾
        for _ in range(L):
            aft = p.next
            p.next = pre
            pre = p
            p = aft
        dp.next = pre # 接上翻转的列表, 即 1 4 3 2
        l2_tail.next = aft # l2 段 接上剩下没迭代的
        return dummy.next
class Solution:
    successor=None

    def reverseN(self,head, n):
        if n==1:
            self.successor=head.next
            return head
        last=self.reverseN(head.next,n-1)
        head.next.next=head
        head.next=self.successor
        return last

    def reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode:
        if left==1:
            return self.reverseN(head, right)
        head.next=self.reverseBetween(head.next,left-1, right-1)
        return head
93. 复原IP地址

93. 复原IP地址

  • DFS
class Solution:
    def restoreIpAddresses(self, s: str) -> List[str]:
        ans = []

        def dfs(s, ix, path):
            if s == "" and len(path) == 4:
                ans.append(".".join(path))
                return
            # 不选
            if ix <= len(s) - 1:
                dfs(s, ix + 1, path)
            # 选
            if len(path) >= 4 or ix == 0:
                return
            chose, rest = s[:ix], s[ix:]
            if chose=="" or int(chose) > 255 or (chose.startswith('0') and chose != '0'):
                return
            cur_path = path + [chose]
            if len(cur_path) == 4 and rest != "":
                return
            dfs(rest, 0, cur_path)

        dfs(s, 1, [])
        return ans
22. 括号生成

22. 括号生成

class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        ans = []

        def dfs(s, left, right):
            if left > n or right > n:
                return
            if left == right == n:
                ans.append(s)
            dfs(s + "(", left + 1, right)
            if left > right:
                dfs(s + ")", left, right + 1)

        dfs("", 0, 0)
        return ans

TODO: 用递归+cache做,理解时间复杂度

数据结构与算法重难点100道_数组_23

72. 编辑距离

72. 编辑距离

olution:
    def minDistance(self, word1: str, word2: str) -> int:
        l1 = len(word1)
        l2 = len(word2)
        dp = [[0] * (l2 + 1) for _ in range(l1 + 1)]
        # 错在没+1
        for i in range(1, l1 + 1):
            dp[i][0] = i
        for i in range(1, l2 + 1):
            dp[0][i] = i
        for i in range(1, l1 + 1):
            for j in range(1, l2 + 1):
                # 错在没+1
                left = dp[i][j - 1] + 1
                down = dp[i - 1][j] + 1
                ld = dp[i - 1][j - 1]
                if word1[i - 1] != word2[j - 1]:
                    ld += 1
                dp[i][j] = min(left, down, ld)
        return dp[l1][l2]

插入删除替换的惩罚不一样的编辑距离,面经也有提到

编辑距离(levenshtein)算法

102. 二叉树的层序遍历

102. 二叉树的层序遍历

不浪费时间了

148. 排序链表

148. 排序链表

  • 插入排序
class Solution:
    def insertionSortList(self, head: ListNode) -> ListNode:
        if not head: return head
        dummy = ListNode(-inf)
        dummy.next = head # 忘了
        tail = head
        p = head.next
        tail.next = None
        while p:
            aft = p.next
            if p.val >= tail.val:
                tail.next = p
                tail = p
                tail.next = None
            else:
                dp = dummy
                # 结束循环后,dp.next.val > p.val
                while dp.next and dp.next.val <= p.val:
                    dp = dp.next
                p.next = dp.next # 写成了dp
                dp.next = p
            p = aft
        return dummy.next
  • 归并排序

需要牢记这题不同的快慢指正初始化条件

class Solution:
    def sortList(self, head: ListNode) -> ListNode:
        # 递归终点
        if not head or not head.next:
            return head
        # 中点寻找
        slow = head
        fast = head.next  # 否则爆栈
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        mid = slow.next
        slow.next = None
        # 处理左链表与右链表
        left = self.sortList(head)
        right = self.sortList(mid)
        # 合并两个排序连败哦
        dummy = dp = ListNode(0)
        while left and right:
            if left.val < right.val:
                dp.next = left
                left = left.next
            else:
                dp.next = right
                right = right.next
            dp = dp.next
        dp.next = left if left else right
        return dummy.next
※ 440. 字典序的第K小数字

440. 字典序的第K小数字

真的很头大,有空默写一遍

[字节跳动最常考题之一]本题史上最完整具体的手摸手解答,时间效率超越100%用户

class Solution:
    def findKthNumber(self, n: int, k: int) -> int:
        def get_count(prefix):
            nxt = prefix + 1
            count = 0
            while prefix <= n:
                count += min(nxt, n + 1) - prefix
                prefix *= 10
                nxt *= 10
            return count

        p = 1
        prefix = 1
        while p < k:
            count = get_count(prefix)
            if k < p + count:
                p += 1
                prefix *= 10
            else:
                p += count
                prefix += 1
        return prefix

19. 删除链表的倒数第 N 个结点

19. 删除链表的倒数第 N 个结点

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        if not head:
            return None
        fast=head
        while fast and n:
            n-=1
            fast=fast.next
        dummy=ListNode(0)
        dummy.next=head
        p=dummy
        while fast:
            fast=fast.next
            p=p.next
        if p and p.next:
            p.next=p.next.next
        return dummy.next
54. 螺旋矩阵

54. 螺旋矩阵

注意break的判断条件

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        m, n = len(matrix), len(matrix[0])
        left = top = 0
        right, bottom = n - 1, m - 1
        ans = []
        while True:
            for i in range(left, right + 1):
                ans.append(matrix[top][i])
            top += 1
            if top > bottom or left > right: break

            for i in range(top, bottom + 1):
                ans.append(matrix[i][right])
            right -= 1
            if top > bottom or left > right: break

            for i in range(right, left - 1, -1):
                ans.append(matrix[bottom][i])
            bottom -= 1
            if top > bottom or left > right: break

            for i in range(bottom, top - 1, -1):
                ans.append(matrix[i][left])
            left += 1
            if top > bottom or left > right: break
        return ans
105. 从前序与中序遍历序列构造二叉树

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

32. 最长有效括号

32. 最长有效括号

class Solution:
    def longestValidParentheses(self, s: str) -> int:
        n = len(s)
        dp = [0] * n
        ans = 0
        for i in range(1, n):
            if s[i] == ")":
                if s[i - 1] == "(":
                    dp[i] = 2 + (
                        dp[i - 2] if i - 2 >= 0 else 0
                    )
                else:
                    pre = dp[i - 1]
                    pre_ix = i - 1 - pre
                    if pre_ix >= 0 and s[pre_ix] == "(":
                        dp[i] = dp[i - 1] + 2
                        # 没把条件缩进来
                        if pre_ix - 1 > 0:  # ()(())
                            dp[i] += dp[pre_ix - 1]
                ans = max(ans, dp[i])
        return ans

TODO: 栈

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

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

143. 重排链表

143. 重排链表

这道题反过来就是字节的一个题了

def reverse(head: ListNode):
    p=head
    pre=None
    while p:
        aft=p.next
        p.next=pre
        pre=p
        p=aft
    return pre

class Solution:
    def reorderList(self, head: ListNode) -> None:
        if head is None or head.next is None: # 边界
            return head
        slow=head
        fast=head.next
        while fast and fast.next:
            fast=fast.next.next
            slow=slow.next
        p2=slow.next
        if p2 is None: # 边界
            return head
        slow.next=None
        p2=reverse(p2)
        a=head
        b=p2
        while a and b:
            an=a.next
            bn=b.next
            a.next=b
            b.next=an
            a=an
            b=bn
        return head
10. 正则表达式匹配

10. 正则表达式匹配

class Solution:
    def isMatch(self, s: str, p: str) -> bool:
        ls=len(s)
        lp=len(p)
        def match(i,j):
            if i<=0 or j<=0:
                return False
            if p[j-1]=='.':
                return True
            if s[i-1]==p[j-1]:
                return True
            return False
        dp=[[False]*(lp+1) for _ in range(ls+1)]
        dp[0][0]=True
        for i in range(ls+1):
            for j in range(1, lp+1):
                if p[j-1]=='*':
                    if match(i,j-1):
                        dp[i][j] = dp[i][j-2] | dp[i-1][j]
                    else:
                        dp[i][j] = dp[i][j-2]
                else:
                    if match(i,j):
                        dp[i][j] |= dp[i-1][j-1]
                    else:
                        dp[i][j]=False
        return dp[ls][lp]

数据结构与算法重难点100道_数组_24

class Solution:
    def isMatch(self, s: str, p: str) -> bool:
        m, n = len(s), len(p)

        dp = [[False] * (n + 1) for _ in range(m + 1)]
        dp[0][0] = True
        for i in range(1, n + 1):
            if p[i - 1] == '*':
                dp[0][i] = True
            else:
                break
        
        for i in range(1, m + 1):
            for j in range(1, n + 1):
                if p[j - 1] == '*':
                    dp[i][j] = dp[i][j - 1] | dp[i - 1][j]
                elif p[j - 1] == '?' or s[i - 1] == p[j - 1]:
                    dp[i][j] = dp[i - 1][j - 1]

        return dp[m][n]
300. 最长递增子序列

300. 最长递增子序列

  • n 2 n^2 n2
class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        n = len(nums)
        dp = [1] * n
        ans = 0
        for i in range(1, n):
            for j in range(i):
                if nums[i] > nums[j]:
                    dp[i] = max(dp[i], dp[j] + 1)  # 错在没取max
                    ans = max(ans, dp[i])
        return max(dp)
  • n log ⁡ n n\log n nlogn

lower_bound

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        dp = [nums[0]]
        for num in nums[1:]:
            if num > dp[-1]:
                dp.append(num)
            else:
                l, r = 0, len(dp)
                while l < r:
                    mid = (l + r) // 2
                    if dp[mid] == num:
                        # l = mid + 1
                        r = mid
                    elif dp[mid] < num:
                        l = mid + 1
                    else:
                        r = mid
                dp[l] = num
        print(dp)
        return len(dp)

673. 最长递增子序列的个数

class Solution:
    def findNumberOfLIS(self, nums: List[int]) -> int:
        n=len(nums)
        length=[0]*n
        count=[1]*n
        for j in range(1, n):
            for i in range(j):
                if nums[j]>nums[i]:
                    if length[i]+1==length[j]:
                        length[j]=length[i]+1
                        count[j]+=count[i]
                    elif length[i]+1>length[j]:
                        length[j]=length[i]+1
                        count[j]=count[i]
        max_len=max(length)
        ans_cnt=sum([count[i] for i,l in enumerate(length) if l==max_len])
        return ans_cnt

334. 递增的三元子序列

class Solution:
    def increasingTriplet(self, nums: List[int]) -> bool:
        first=inf
        mid=inf
        for num in nums:
            if num<=first:
                first=num
            elif num<=mid:
                mid=num
            else:
                return True
        return False

354. 俄罗斯套娃信封问题

def lower_bound(nums, target):
    l=0
    r=len(nums)
    while l<r:
        mid=(l+r)//2
        if nums[mid]==target:
            r=mid
        elif nums[mid]>target:
            r=mid
        else:
            l=mid+1
    return l

class Solution:
    def maxEnvelopes(self, envelopes: List[List[int]]) -> int:
        # 后面这个操作是为了处理 第一个值相等的情况
        envelopes.sort(key=lambda x:(x[0], -x[1]))
        nums=[envelope[1] for envelope in envelopes]
        dp=[nums[0]]
        for num in nums[1:]:
            if num>dp[-1]:
                dp.append(num)
            else:
                dp[lower_bound(dp, num)]=num
        return len(dp)

还有个三维的题目,在【面试题】里面

24. 两两交换链表中的节点

24. 两两交换链表中的节点

数据结构与算法重难点100道_字符串_25

class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
        dummy = ListNode(0)
        dummy.next = head
        dp = dummy
        while dp.next and dp.next.next:
            p1 = dp.next
            p2 = dp.next.next
            dp.next = p2
            p1.next = p2.next
            p2.next = p1
            dp = p1
        return dummy.next
9. 回文数

9. 回文数

class Solution:
    def isPalindrome(self, x: int) -> bool:
        return False if x<0 else str(x)==str(x)[::-1]

你能不将整数转为字符串来解决这个问题吗?

class Solution:
    def isPalindrome(self, x: int) -> bool:
        # 排除 10 和 负数 的特殊情况
        if x < 0 or (x % 10 == 0 and x != 0):
            return False
        rx = 0
        while x > rx:
            rx = rx * 10 + x % 10
            x //= 10
        return rx == x or rx // 10 == x
※ 41. 缺失的第一个正数

41. 缺失的第一个正数

缺失的第一个正数

这题的hash法我看懂了,就是没时间写注释

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

448. 找到所有数组中消失的数字

复用上题的hash法

class Solution:
    def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
        n=len(nums)
        for i in range(n):
            num=abs(nums[i])
            if num<=n:
                nums[num-1]=-abs(nums[num-1])
        ans=[]
        for i in range(n):
            if nums[i]>0:
                ans.append(i+1)
        return ans

268. 丢失的数字

缺失数字

  • 数学tricky

用求和公式求出 [ 0 ⋯ n ] [0\cdots n] [0n] 的和,减去数组中所有数的和,就得到了缺失数字

求和,题解用的高斯求和,有玩家认为会溢出,提出了以下方法:

    public int missingNumber(int[] nums) {
        int sum = 0;
        for (int i = 1; i <= nums.length; i++) {
            sum += i;
            sum -= nums[i - 1];
        }
        return sum;
    }
  • 位运算

先和 [ 0 ⋯ n ] [0\cdots n] [0n]的数都xor一遍,根据a ^ a = 0的定理,a ^ a ^ b = b ,就找到了

class Solution:
    def missingNumber(self, nums):
        missing = len(nums)
        for i, num in enumerate(nums):
            missing ^= i ^ num
        return missing

287. 寻找重复数

复用剑指的代码

class Solution:
    def findDuplicate(self, nums: List[int]) -> int:
        n = len(nums)
        for i in range(n):
            nums[i] -= 1
        for i in range(n):
            while nums[i] != i:
                if nums[i] == nums[nums[i]]:
                    return nums[i] + 1
                t = nums[i]
                nums[i] = nums[t]
                nums[t] = t
135. 分发糖果

135. 分发糖果

class Solution:
    def candy(self, ratings: List[int]) -> int:
        n = len(ratings)
        left = [1] * n
        for i in range(1, n):
            if ratings[i] > ratings[i - 1]:
                left[i] = left[i - 1] + 1
        right = 1
        ans = left[n - 1]
        for i in range(n - 2, -1, -1):
            if ratings[i] > ratings[i + 1]:
                right += 1
            else: # 错在这
                right = 1
            ans += max(right, left[i])
        return ans
88. 合并两个有序数组

88. 合并两个有序数组

322. 零钱兑换

322. 零钱兑换

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # 语义:凑出dp_i 的金额需要的硬币数
        dp=[inf]*(amount+1)
        dp[0]=0
        for i in range(1, amount+1):
            cur=inf
            for coin in coins:
                if i-coin>=0:
                    cur=min(cur, dp[i-coin])
            dp[i]=cur+1
        return -1 if dp[amount]==inf else dp[amount]

518. 零钱兑换 II

class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        dp=[0]*(amount+1)
        dp[0]=1
        for coin in coins:
            for x in range(coin, amount+1):
                dp[x]+=dp[x-coin]
        return dp[amount]
78. 子集

78. 子集

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        n=len(nums)
        ans=[]
        def dfs(path, begin):
            ans.append(path)
            for i in range(begin, n):
                dfs(path+[nums[i]], i+1)
        dfs([],0)
        return ans

90. 子集 II

class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        # 注意
        nums.sort()
        n=len(nums)
        path = []
        ans = []
        def dfs(begin):
            ans.append(path[:]) # 注意
            for i in range(begin, n):
                # 注意
                if i>begin and nums[i]==nums[i-1]:
                    continue
                path.append(nums[i])
                dfs(i+1) # 注意
                path.pop()
        
        dfs(0)
        return ans
            
14. 最长公共前缀

14. 最长公共前缀

class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        l1 = len(strs)
        if l1 == 0:
            return ""
        l2 = min(map(len, strs))
        for i in range(l2):
            for j in range(1, l1):
                if strs[j][i] != strs[j - 1][i]:
                    return strs[0][:i]
        return strs[0][:l2] # 错在这
79. 单词搜索

79. 单词搜索

好像在剑指刷过

class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
        m,n=len(board),len(board[0])
        L=len(word)
        def dfs(x, y, t):
            if t==L:
                return True
            if not (0<=x<m and 0<=y<n):
                return False
            if board[x][y]!=word[t]:
                return False
            board[x][y]='-'
            ans=dfs(x+1,y,t+1) or dfs(x,y+1,t+1) or dfs(x-1,y,t+1) or dfs(x,y-1,t+1)
            board[x][y]=word[t]
            return ans
        for x in range(m):
            for y in range(n):
                if dfs(x, y, 0):
                    return True
        return False

我是傻逼

TODO: 写一个更优雅的答案

94. 二叉树的中序遍历

94. 二叉树的中序遍历

class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        stack = []
        ans = []
        while root or stack:
            while root:
                stack.append(root)
                root = root.left
            root = stack.pop()
            ans.append(root.val)
            root = root.right
        return ans
112. 路径总和

112. 路径总和

这样的简单题也能卡住,一定要多思考多练习

class Solution:
    def hasPathSum(self, root: TreeNode, targetSum: int) -> bool:
        self.ans=False
        def dfs(root, x):
            if self.ans:return
            if not root:
                return
            
            if root.left is None and root.right is None and x+root.val==targetSum:
                self.ans=True
                return
            dfs(root.left, x+root.val)
            dfs(root.right, x+root.val)
        dfs(root, 0)
        return self.ans
175. 组合两个表

175. 组合两个表

select FirstName, LastName, City, State 
from Person left join Address 
on Person.PersonId = Address.PersonId;
543. 二叉树的直径

543. 二叉树的直径

class Solution:
    def diameterOfBinaryTree(self, root: TreeNode) -> int:
        ans = 1 # 注意
        def rec(node:TreeNode):
            nonlocal ans
            if node is None:
                return 0
            rl, rr = rec(node.left), rec(node.right)
            ans = max(ans, 1 + rl + rr)
            return max(rl, rr) + 1
        
        rec(root)
        return ans - 1 # 注意
198. 打家劫舍

198. 打家劫舍

乱写的

class Solution:
    def rob(self, nums: List[int]) -> int:
        if not nums:
            return 0
        n=len(nums)
        dp=[0]*(n+1) # 负索引
        dp[0]=nums[0]
        for i in range(1,n):
            dp[i]=max(dp[i-1],dp[i-2]+nums[i]) # 负索引
        return max(dp)
剑指 Offer 03. 数组中重复的数字

数据结构与算法重难点100道_数组_26

和前面的一道题可以合起来看看

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

class Solution:
    def findRepeatNumber(self, nums: List[int]) -> int:
        n = len(nums)
        for i in range(n):
            # 有一个顾虑,就是如果不存在数字i会出现死循环,
            # 但不用担心,如 [1, 1, 1] ,会直接返回掉
            while nums[i] != i:
                if nums[i] == nums[nums[i]]:
                    return nums[i]
                # ↓ 这么干就凉了
                # nums[i], nums[nums[i]] = nums[nums[i]], nums[i]
                tmp = nums[nums[i]]
                nums[nums[i]] = nums[i]
                nums[i] = tmp
        return -1
141. 环形链表
class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        slow=head
        fast=head
        while fast and fast.next:
            slow=slow.next
            fast=fast.next.next
            if fast==slow:
                return True
        return False

这题还是不熟练

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        slow=head
        fast=head
        while fast and fast.next:
            fast=fast.next.next
            slow=slow.next
            if fast==slow:
                break
        if fast and fast.next:
            slow=head
            while slow!=fast: # 判断条件瞎搞导致出错
                slow=slow.next
                fast=fast.next
            return slow
        return None
176. 第二高的薪水

176. 第二高的薪水

select IFNULL(
    (select 
    distinct Salary 
    from Employee 
    order by Salary DESC
    limit 1 offset 1),   -- offset 要放后面
    null
) as SecondHighestSalary ;
101. 对称二叉树
def recur(n1, n2):
    if n1 is None and n2 is None:
        return True
    if n1 is None or n2 is None:
        return False
    if n1.val!=n2.val:
        return False
    return recur(n1.left, n2.right) and recur(n1.right, n2.left)


class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        if root is None:
            return True
        return recur(root.left, root.right)
160. 相交链表

160. 相交链表

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

221. 最大正方形

class Solution:
    def maximalSquare(self, matrix: List[List[str]]) -> int:
        # 处理matrix使其方便访问
        m,n=len(matrix),len(matrix[0])
        dp=[[0]*(n+1) for _ in range(m+1)]
        ans=0
        for i in range(1,m+1):
            for j in range(1,n+1):
                if matrix[i-1][j-1]=='1':
                    dp[i][j]=min(dp[i-1][j] , dp[i][j-1] , dp[i-1][j-1]) + 1
                    ans=max(ans,dp[i][j])
        return ans**2

1277. 统计全为 1 的正方形子矩阵

class Solution:
    def countSquares(self, matrix: List[List[int]]) -> int:
        # 处理matrix使其方便访问
        m,n=len(matrix),len(matrix[0])
        dp=[[0]*(n+1) for _ in range(m+1)]
        ans=0
        for i in range(1,m+1):
            for j in range(1,n+1):
                if matrix[i-1][j-1]==1:
                    dp[i][j]=min(dp[i-1][j] , dp[i][j-1] , dp[i-1][j-1])+1
                    ans+=dp[i][j]
        return ans
104. 二叉树的最大深度

104. 二叉树的最大深度

class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        if not root:
            return 0
        return max(
            self.maxDepth(root.left),
            self.maxDepth(root.right),
        )+1
76. 最小覆盖子串

76. 最小覆盖子串

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        need=collections.Counter(t)
        window=collections.defaultdict(int)
        valid=0
        n=len(s)
        l=0
        a,b=-1,n
        for r in range(n):
            c=s[r]
            if c in need:
                window[c]+=1
                if window[c]==need[c]:
                    valid+=1
            # 注意 l<=r 的判断条件
            while l<=r and valid==len(need): # 条件写错
                if r-l<b-a:
                    a,b=l,r
                c=s[l]
                if c in need:
                    if window[c]==need[c]:
                        valid-=1
                    window[c]-=1
                l+=1
        return "" if a==-1 else s[a:b+1] 
236. 二叉树的最近公共祖先

236. 二叉树的最近公共祖先

因为判断条件顺序的bug导致卡了很久

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if root is None:
            return None
        if root==p or root==q:
            return root
        left=self.lowestCommonAncestor(root.left,p,q)
        right=self.lowestCommonAncestor(root.right,p,q)
        # bug: 判断条件的顺序
        if left and right:
            return root
        if left or right:
            return left if left else right
        return None
347. 前 K 个高频元素

347. 前 K 个高频元素

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        counter = collections.Counter(nums)
        heap=[]
        for num,cnt in counter.items():
            heapq.heappush(heap, (cnt, num))
            if len(heap)>k:
                heapq.heappop(heap)
        return [num for _,num in heap]
8. 字符串转换整数 (atoi)

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

  • DFA
  • if-else
    • long
    • int, × 10 \times10 ×10前判断
  • Python, 正则表达式

写得头痛。。

int my_atoi(string &s) {
    int num = 0;
    int i = 0;
    int len = s.size();
    int flag = 1;
    while (s[i] == ' ') {
        i++;
    }
    if (s[i] == '+') {
        i++;
    } else if (s[i] == '-') {
        flag = -1;
        i++;
    }
    while (i < len) {
        if (s[i] < '0' || s[i] > '9') break;
        int cur = (int) (s[i] - '0');
        if (num > INT_MAX / 10) { return flag == 1 ? INT_MAX : INT_MIN; }
        if (num == INT_MAX / 10) {
            if (flag == 1 && cur >= 7) return INT_MAX;
            if (flag == -1 && cur >= 8) return INT_MIN;
        }
        num = num * 10 + cur;
        i++;
    }
    return flag * num;
}


class Solution {
public:
    int myAtoi(string s) {
        return my_atoi(s);
    }
};
122. 买卖股票的最佳时机 II

122. 买卖股票的最佳时机 II

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n=len(prices)
        ans=0
        for i in range(1, n):
            ans+=max(0, prices[i]-prices[i-1])
        return ans
45. 跳跃游戏 II

要求能背这两题

  • 基本题是贪心

55. 跳跃游戏

class Solution:
    def canJump(self, nums: List[int]) -> bool:
        k=0
        n=len(nums)
        for i in range(n):
            if i>k:
                return False
            k=max(k, i+nums[i])
        return True
  • 升级题还是贪心。。

45. 跳跃游戏 II

def jump(nums):
    ans = 0
    start = 0
    end = 1
    n = len(nums)
    while end < n:
        max_pos = 0
        for i in range(start, end):
            max_pos = max(max_pos, i + nums[i])
        start = end
        end = max_pos + 1
        ans += 1
    return ans
98. 验证二叉搜索树

98. 验证二叉搜索树

class Solution:
    def isValidBST(self, root: TreeNode, min_node=None, max_node=None) -> bool:
        if root is None:
            return True
        if min_node and min_node.val>=root.val: return False
        if max_node and max_node.val<=root.val: return False
        return self.isValidBST(root.left, min_node, root) and self.isValidBST(root.right, root, max_node)
128. 最长连续序列

128. 最长连续序列

class Solution:
    def longestConsecutive(self, nums: List[int]) -> int:
        num_set=set(nums)
        max_len=0
        for num in num_set:
            if num-1 not in num_set:
                cur_len=1
                cur_num=num+1
                while cur_num in num_set:
                    cur_len+=1
                    cur_num+=1
                max_len=max(max_len, cur_len)
        return max_len
84. 柱状图中最大的矩形

84. 柱状图中最大的矩形

注意是个单调递增栈

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        stack=[]
        n=len(heights)
        left=[-1]*n
        right=[n]*n
        for i, height in enumerate(heights):
            # 单调性写错
            # 单调递增
            while stack and heights[i]<heights[stack[-1]]: 
                top=stack.pop()
                right[top]=i
            if stack:
                left[i]=stack[-1]
            stack.append(i)
        ans=0
        for i in range(n):
            l=left[i]
            r=right[i]
            ans=max(ans, (r-l-1)*heights[i])
        return ans
64. 最小路径和

64. 最小路径和

刚开始用回溯写的,我是傻逼

class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:
        m,n=len(grid),len(grid[0])
        visit=[[0]*n for _ in range(m)]
        # path=[]
        ans=inf
        def dfs(i,j,cur_sum):
            nonlocal ans
            if i==m-1 and j==n-1:
                # sum_=sum(path)
                ans=min(ans, cur_sum)
            for dx,dy in [[1,0],[-1,0],[0,1],[0,-1]]:
                x,y=i+dx,j+dy
                # val=
                if  (0<=x<m and 0<=y<n) and visit[x][y]==0 and cur_sum+grid[x][y]<ans:
                    visit[x][y]=1
                    # path.append(val))
                    dfs(x,y,cur_sum+grid[x][y])
                    # path.pop()
                    visit[x][y]=0
        visit[0][0]=1
        # path.append(grid[0][0])
        dfs(0,0, grid[0][0])
        return ans

做的我有点想吐了。。

class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        dp = [[0] * (n) for _ in range(m)]
        dp[0][0] = grid[0][0]
        for i in range(1, m):
            dp[i][0] = dp[i - 1][0] + grid[i][0]
        for i in range(1, n):
            dp[0][i] = dp[0][i - 1] + grid[0][i]
        for i in range(1, m):
            for j in range(1, n):
                dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]
        return dp[m - 1][n - 1]
445. 两数相加 II

445. 两数相加 II

def reverse(p):
    pre=None
    while p:
        aft=p.next
        p.next=pre
        pre=p
        p=aft
    return pre

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        l1=reverse(l1)
        l2=reverse(l2)
        dummy=ListNode(0)
        dp=dummy
        carry=0
        while l1 or l2:
            v1=l1.val if l1 else 0
            v2=l2.val if l2 else 0
            l1=l1.next if l1 else None
            l2=l2.next if l2 else None
            v=v1+v2+carry
            carry=v//10
            dp.next=ListNode(v%10)
            dp=dp.next
        if carry:
            dp.next=ListNode(carry)
        return reverse(dummy.next)
26. 删除排序数组中的重复项

26. 删除排序数组中的重复项

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        slow=fast=0
        n=len(nums)
        while fast<n:
            if nums[slow]!=nums[fast]:
                slow+=1
                nums[slow]=nums[fast]
            fast+=1
        return slow+1
6. Z 字形变换

6. Z 字形变换

class Solution:
    def convert(self, s: str, numRows: int) -> str:
        if numRows==1:
            return s
        rows=[""]*numRows
        i=0
        flag=-1
        for c in s:
            rows[i]+=c
            if i in (0, numRows-1):
                flag*=-1
            i+=flag
        return "".join(rows)
96. 不同的二叉搜索树

96. 不同的二叉搜索树

def C(a, b):
    ans = 1
    for _ in range(b):
        ans *= a
        a -= 1
    while b >= 1:
        ans //= b
        b -= 1
    return ans

def catalan(n):
    return C(2*n, n) // (n + 1)


class Solution:
    def numTrees(self, n: int) -> int:
        return (catalan(n))
739. 每日温度

739. 每日温度

class Solution:
    def dailyTemperatures(self, T: List[int]) -> List[int]:
        stack=[]
        # 从右到左,第一个更小的
        n=len(T)
        right=[0]*n
        for i in range(n-1, -1, -1):
            # 单调递减
            while stack and T[stack[-1]]<=T[i]: # 要求更高的气温
                stack.pop()
            # 这里卡住了
            right[i]=stack[-1]-i if stack else 0
            stack.append(i)
        return right
240. 搜索二维矩阵 II

74. 搜索二维矩阵

从右上角开始遍历,时间复杂度 O ( N + M ) O(N+M) O(N+M)

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        m,n=len(matrix),len(matrix[0])
        i,j=0,n-1
        while True:
            if not (0<=i<m and 0<=j<n):
                return False
            val=matrix[i][j]
            if val<target:
                i+=1
            elif val>target:
                j-=1
            else:
                return True

240. 搜索二维矩阵 II

def binary_search(nums, target):
    l,r=0,len(nums)-1
    while l<=r:
        mid=(l+r)//2
        if nums[mid]==target:
            return True
        elif nums[mid]>target:
            r=mid-1
        else:
            l=mid+1
    return False

def upper_bound(nums, target):
    l,r=0,len(nums)
    while l<r:
        mid=(l+r)//2
        if nums[mid]==target:
            l=mid+1
        elif nums[mid]>target:
            r=mid
        else:
            l=mid+1
    return l


class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        m, n=len(matrix), len(matrix[0])
        first_line=[matrix[0][i] for i in range(n)]
        ub=upper_bound(first_line, target)
        for col_ix in range(ub):
            col=[matrix[i][col_ix] for i in range(m)]
            if binary_search(col, target):
                return True
        return False
51. N 皇后

51. N 皇后

主要涉及:

  1. col
  2. 主对角线 dj1
  3. 斜对角线 dj2
class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        board=[["."]*n for _ in range(n)]
        dj1=[0]*(2*n)
        dj2=[0]*(2*n)
        col=[0]*n
        ans=[]
        # j 表示行
        def dfs(j):
            # i 表示列
            if j==n:
                ans.append(["".join(line) for line in board])
            for i in range(n):
                ix1=i+j
                ix2=i-j+n
                if col[i]==1 or dj1[ix1]==1 or dj2[ix2]==1:
                    continue
                col[i]=1;dj1[ix1]=1;dj2[ix2]=1
                board[i][j]='Q'
                dfs(j+1)
                board[i][j]='.'
                col[i]=0;dj1[ix1]=0;dj2[ix2]=0
        dfs(0)
        return ans
39. 组合总和

39. 组合总和

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        ans=[]
        n=len(candidates)
        if not n:
            return []
        def dfs(begin ,path, target):
            if target==0:
                ans.append(path)
                return
            for i in range(begin, n):
                if target-candidates[i]<0:
                    break
                dfs(i, path+[candidates[i]], target-candidates[i])
        
        candidates.sort()
        dfs(0, [],target)
        return ans

40. 组合总和 II

class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        candidates.sort()
        n = len(candidates)
        ans = []

        def dfs(begin, target, path):
            if target == 0:
                ans.append(path)
                return
            for i in range(begin, n):
                if target - candidates[i] < 0:
                    break
                if i > begin and candidates[i] == candidates[i - 1]:
                    continue
                # 不是begin而是i
                dfs(i + 1, target - candidates[i], path + [candidates[i]])

        dfs(0, target, [])
        return ans

78. 子集

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        n=len(nums)
        ans=[]
        def dfs(path, begin):
            ans.append(path)
            for i in range(begin, n):
                dfs(path+[nums[i]], i+1)
        dfs([],0)
        return ans

90. 子集 II

class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        # 注意
        nums.sort()
        n=len(nums)
        path = []
        ans = []
        def dfs(begin):
            ans.append(path[:]) # 注意
            for i in range(begin, n):
                # 注意
                if i>begin and nums[i]==nums[i-1]:
                    continue
                path.append(nums[i])
                dfs(i+1) # 注意
                path.pop()
        
        dfs(0)
        return ans
            
85. 最大矩形

85. 最大矩形

def max_hist_rect(heights):
    n=len(heights)
    left=[0]*n
    right=[n]*n
    stack=[]
    for i in range(n):
        # 单调递增栈      栈顶比当前元素大
        while stack and heights[stack[-1]]>heights[i]:
            top = stack.pop()
            right[top]=i
        left[i]=stack[-1] if stack else -1
        stack.append(i)
    ans=0
    for i in range(n):
        ans=max(ans, heights[i]*(right[i]-left[i]-1))
    return ans

class Solution:
    def maximalRectangle(self, matrix: List[List[str]]) -> int:
        if len(matrix)==0:
            return 0
        m, n = len(matrix), len(matrix[0])
        hist=[0]*n
        ans=0
        for i in range(m):
            for j in range(n):
                if matrix[i][j]=='0':
                    hist[j]=0
                else:
                    hist[j]+=1
            ans=max(ans, max_hist_rect(hist))
        return ans
82. 删除排序链表中的重复元素 II

背下来

82. 删除排序链表中的重复元素 II

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        # 节点数 <= 1  直接返回
        if not (head and head.next):
            return head
        dummy=ListNode(-inf)
        dummy.next=head
        a=dummy
        b=head
        while b and b.next:
            if a.next.val != b.next.val:
                a=a.next
                b=b.next
            else:
                while b and b.next and a.next.val == b.next.val:
                    b=b.next
                a.next=b.next
                b=b.next
        return dummy.next
38. 外观数列

38. 外观数列

class Solution:
    def countAndSay(self, n: int) -> str:
        pre = ''
        cur = '1'
        for _ in range(1, n):
            pre = cur
            cur = ''
            start = 0
            end = 0
            while end < len(pre):
                while end<len(pre) and pre[start] == pre[end]:
                    end += 1
                cur += str(end - start) + pre[start]
                start = end
        return cur
120. 三角形最小路径和

120. 三角形最小路径和

class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        n=len(triangle)
        dp=copy.deepcopy(triangle)
        for i in range(1, n):
            for j in range(i+1):
                min_=inf
                for k in range(max(0, j-1),min(i,j+1)):
                    min_=min(min_, dp[i-1][k])
                min_+=triangle[i][j]
                dp[i][j]=min_
        return min(dp[n-1])
力扣找不到的字节面试题——36进制加法

力扣找不到的字节面试题——36进制加法

415. 字符串相加

class Solution:
    def addStrings(self, num1: str, num2: str) -> str:
        l1 = len(num1)
        l2 = len(num2)
        i1 = l1 - 1
        i2 = l2 - 1
        carry = 0
        base = 10
        ans = ''
        while i1 >= 0 or i2 >= 0 or carry:
            a = int(num1[i1]) if i1 >= 0 else 0
            b = int(num2[i2]) if i2 >= 0 else 0
            sum_ = a + b + carry
            ans = ans + str(sum_ % base)
            carry = sum_ // base
            i1 -= 1
            i2 -= 1
        return ans[::-1]

43. 字符串相乘

O ( ( n + m ) × n ) O((n+m)\times n) O((n+m)×n)

class Solution:
    def multiply(self, num1: str, num2: str) -> str:
        if num1 == "0" or num2 == "0":
            return "0"
        
        ans = "0"
        m, n = len(num1), len(num2)
        for i in range(n - 1, -1, -1):
            add = 0
            y = int(num2[i])
            curr = ["0"] * (n - i - 1)
            for j in range(m - 1, -1, -1):
                product = int(num1[j]) * y + add
                curr.append(str(product % 10))
                add = product // 10
            if add > 0:
                curr.append(str(add))
            curr = "".join(curr[::-1])
            ans = self.addStrings(ans, curr)
        
        return ans
    
    def addStrings(self, num1: str, num2: str) -> str:
        i, j = len(num1) - 1, len(num2) - 1
        add = 0
        ans = list()
        while i >= 0 or j >= 0 or add != 0:
            x = int(num1[i]) if i >= 0 else 0
            y = int(num2[j]) if j >= 0 else 0
            result = x + y + add
            ans.append(str(result % 10))
            add = result // 10
            i -= 1
            j -= 1
        return "".join(ans[::-1])
头条笔试题 - 折木棍

头条笔试题 - 折木棍

TODO: 用合理的思路再重刷一遍

int breakNum(vector<int>& nums) {
    int ans = 0;
    for (int i = nums.size() - 2; i >= 0; i--) {
        if (nums[i + 1] >= nums[i]) continue;
        int t = (nums[i] - 1) / nums[i + 1];
        ans += t;
        nums[i] /= (t + 1);
    }
    return ans;
}

基本思路大概知道,两个比较难理解的点:

  • int t = (nums[i] - 1) / nums[i + 1];
  • nums[i] /= (t + 1);

第一个,t表示折出多少段。如 12 6, 可以折 6 , 6,t=1

但是 12/6=2,所以- 1

第二个,折出t段,加上自己,一共t+1,故除 (t + 1)

逻辑题

100个人从1到100,每个回合去掉奇数序号的人,剩下的人向前排列,最后一回合剩下的人是第几个序号

双向链表的反转 补充题

补充题

字节跳动高频题——排序奇升偶降链表

字节跳动高频题——排序奇升偶降链表

class ListNode:    
    def __init__(self, x):        
        self.val = x        
        self.next = None

class Solution:    
    def sortOddEvenList(self,head):     
        if not head or not head.next:      
            return head        
        oddList,evenList = self.partition(head)        
        evenList = self.reverse(evenList)        
        return self.merge(oddList,evenList)    

    def partition(self, head: ListNode) -> ListNode:        
        evenHead = head.next        
        odd, even = head, evenHead        
        while even and even.next:            
            odd.next = even.next            
            odd = odd.next            
            even.next = odd.next            
            even = even.next        
        odd.next = None        
        return [head,evenHead]    
    def reverse(self,head):        
        dumpy = ListNode(-1)        
        p = head        
        while p:            
            temp = p.next            
            p.next = dumpy.next            
            dumpy.next = p            
            p = temp        
        return dumpy.next    
    def merge(self,p,q):        
        head = ListNode(-1)        
        r = head        
        while p and q:            
            if p.val <= q.val:               
                r.next = p                
                p = p.next            
            else:                
                r.next = q                
                q = q.next            
            r = r.next        
        if p:            
            r.next = p        
        if q:            
            r.next = q        
        return head.next

感觉交替切分链表不太熟,练了下

def partition(head: ListNode):
    if not head or not head.next:
        return head
    p1, p2 = head, head.next
    head1, head2 = p1, p2
    # p2 后面至少有1个结点
    while p2 and p2.next:
        p1.next = p2.next
        p1 = p1.next
        p2.next = p1.next
        p2 = p2.next
    p1.next = None
    return head1, head2

字节跳动高频题——圆环回原点问题

字节跳动高频题——圆环回原点问题

dp[i][j]为从0点出发走i步到j点的方案数

数据结构与算法重难点100道_时间复杂度_27

class Solution:
    def backToOrigin(self,n):
        #点的个数为10
        length = 10
        dp = [[0 for i in range(length)] for j in range(n+1)]
        dp[0][0] = 1
        for i in range(1,n+1):
            for j in range(length):
                #dp[i][j]表示从0出发,走i步到j的方案数
                dp[i][j] = dp[i-1][(j-1+length)%length] + dp[i-1][(j+1)%length]
        return dp[n][0]

面试字节前必看的高频题——木头切割问题

数据结构与算法重难点100道_链表_28

#include <iostream>
using namespace std;

const int N = 100010;
int a[N];
int n, k;

int check(int mid)
{
    int res = 0;
    for (int i = 0; i < n; i ++ ) res += a[i] / mid;
    return res;
}

int main()
{
    cin >> n >> k;
    int l = 1, r = -1;
    
    for (int i = 0; i < n; i ++ )
    {
        cin >> a[i];
        r = max(r, a[i]);
    }
    
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid) >= k) l = mid;
        else r = mid - 1;
    }
    
    cout << l << endl;
    return 0;
}
# 数组长度为【n】,切出至少【k】个相同长度为【m】的木块,已知【k】,求【m】
def check(nums, n, mid):
    res = 0
    for i in range(n):
        res += nums[i] // mid
    return res


def cut_wood(nums, k):
    n = len(nums)
    l, r = 1, max(nums)
    while l < r:
        cur_m = (l + r) // 2
        # m越大,k越小
        cur_k = check(nums, n, cur_m)
        if cur_k == k:
            r = cur_m
        elif cur_k < k:
            r = cur_m
        else:
            l = cur_m + 1
    return l


m = cut_wood([4, 7, 2, 10, 5], 5)
print(m)

求区间最小数乘区间和的最大值

求区间最小数乘区间和的最大值

五分钟掌握:二叉树的下一个节点

五分钟掌握:二叉树的下一个节点

[编程题]二叉树的下一个结点

  1. 二叉树为空,则返回空;
  2. 节点右孩子存在,则设置一个指针从该节点的右孩子出发,一直沿着指向左子结点的指针找到的叶子节点即为下一个节点;
  3. 节点不是根节点。如果该节点是其父节点的左孩子,则返回父节点;否则继续向上遍历其父节点的父节点,重复之前的判断,返回结果。
class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* pNode)
    {
        if(pNode==NULL)
            return NULL;
        if(pNode->right!=NULL)
        {
            pNode=pNode->right;
            while(pNode->left!=NULL)
                pNode=pNode->left;
            return pNode;
        }  
        while(pNode->next!=NULL)
        {
            TreeLinkNode *proot=pNode->next;
            if(proot->left==pNode)
                return proot;
            pNode=pNode->next;
        }
        return NULL;
    }
};