各大厂面试算法真题

华为

ICT光产品部二面

整数拆分

牛客原题:整数拆分

一个整数总可以拆分为2的幂的和,例如:
7 = 1+2+4
7 = 1+2+2+2
7 = 1+1+1+4
7 = 1+1+1+2+2
7 = 1+1+1+1+1+2
7 = 1+1+1+1+1+1+1
总共有六种不同的拆分方式。
再比如:4可以拆分成:
4 = 4
4 = 1 + 1 + 1 + 1
4 = 2 + 2
4 = 1+1+2
用f(n)表示n的不同拆分的种数,例如f(7)=6. 要求编写程序,读入n(不超过1000000),输出f(n)%1000000000。

分析:
分为奇数和偶数进行讨论
当N为奇数时,它里面一定包含一个奇数成分1,因为其他成分全是偶数。所以它只能通过它的前一个数N-1通过+1得到
当N为偶数时,它里面可以是有奇数成分1,也可以没有。所以它可以通过它的前一个数N-1通过+1得到,也可以通过N//2数的成分乘以2得到,这样里面的成分就全是偶数
综上动归方程就为:
d p [ i ] = { d p [ i − 1 ] , i % 2 ! = 0 d p [ i − 1 ] + d p [ i / / 2 ] , i % 2 = = 0 dp[i] = \begin{cases} dp[i-1], & i\%2 != 0 \\ dp[i-1] + dp[i//2], & i\%2 == 0 \end{cases} dp[i]={ dp[i1],dp[i1]+dp[i//2],i%2!=0i%2==0

Python源码之动态规划:

number = int(input())

dp = [0] * (number + 1)
dp[1] = 1

for i in range(2, number + 1):
    if i&1 == 1:
        dp[i] = dp[i-1] % 1000000000
    else:
        dp[i] = (dp[i-1] + dp[i>>1]) % 1000000000

print(dp[-1])

Python源码之回溯:

def demo(n):
    res = []
    path = []

    def backtracking(val, startIndex):
        if val == 0:
            res.append(path[:])
            return
        if val < 0:
            return

        i = startIndex
        while 2 ** i <= val:
            val -= 2 ** i
            path.append(2 ** i)
            backtracking(val, i)
            path.pop()
            val += 2 ** i
            i += 1
    
    backtracking(n, 0)
    return res

res = demo(7)
print(len(res))
print(res)

打印结果:

6
[[1, 1, 1, 1, 1, 1, 1], 
[1, 1, 1, 1, 1, 2], 
[1, 1, 1, 2, 2], 
[1, 1, 1, 4], 
[1, 2, 2, 2], 
[1, 2, 4]]

升级版:
一个整数被拆分成小于等于它的非负数数之和的拆分方式,即不限制其拆分成分为2的幂

分析:
整数拆分问题的四种解法
问题定义:
dp[i][j]:表示数字i可以被最大为j的整数拆分的种数

动态迁移方程,分为三种情况:
1)当i=j的时候,此时的拆分种数dp[i][j]只会比dp[i][j-1]的种数多一种情况,即i可以用j来表示。所以dp[i][j]=dp[i][j-1]+1
2)当i<j的时候,此时的拆分种数dp[i][j]等于dp[i][i],因为对于大于i的j来说他们是无法去拆分整数i的,也就相当于大于i的部分j是没有作用的。所以dp[i][j]=dp[i][i]
3)当i>j的时候,此时的拆分种数dp[i][j]有两种情况组成,第一,拆分里面存在数字j,这种情况的种数为dp[i-j][j];第二,拆分里面不存在数字j,这种情况的种数为dp[i][j-1]
d p [ i ] [ j ] = { d p [ i ] [ j − 1 ] + 1 , i = = j d p [ i ] [ i ] , i < j d p [ i − j ] [ j ] + d p [ i ] [ j − 1 ] , i > j dp[i][j] = \begin{cases} dp[i][j-1] + 1, & i==j \\ dp[i][i], & i<j \\ dp[i-j][j] + dp[i][j-1], & i>j \end{cases} dp[i][j]= dp[i][j1]+1,dp[i][i],dp[ij][j]+dp[i][j1],i==ji<ji>j

初始化:
当i=1或者j=1时,情况只有1种

Python源码之动态规划:

def dfs(n):
    dp = [[0] * (n+1) for _ in range(n+1)]
    for i in range(1, n+1): dp[i][1] = 1
    for i in range(1, n+1): dp[1][i] = 1

    for i in range(2, n+1):
        for j in range(2, n+1):
            if i == j: dp[i][j] = dp[i][j-1] + 1
            elif i < j: dp[i][j] = dp[i][i]
            else: dp[i][j] = dp[i-j][j] + dp[i][j-1]

    return dp[-1][-1]

Python源码之回溯:

def demo(n):
    res = []
    path = []

    def backtracking(val, startIndex):
        if val == 0:
            res.append(path[:])
            return
        if val < 0:
            return

        i = startIndex
        while i <= val:
            val -= i
            path.append(i)
            backtracking(val, i)
            path.pop()
            val += i
            i += 1
    
    backtracking(n, 1)
    return res

res = demo(4)
print(len(res))
print(res)

打印结果:

5
[[1, 1, 1, 1], 
[1, 1, 2], 
[1, 3], 
[2, 2], 
[4]]

终端BG-AI与智慧全场景一面

字符串转换

牛客面经:华为-AI工程师-消费者BG-面经
面经整理计划——第六弹
在这里插入图片描述
分析:
贪心思想
1)针对target中的每个字符就去block中去一一匹配
2)如果匹配成功,就target和block都右移一位,继续匹配
3)如果匹配不成功,则res+1,同时block从头开始匹配
4)如果找完整个block都没有匹配成功,则返回-1,说明无法从block中得到target

Python源码:

def demo(target, block):
    res = 0
    i = 0
    while i < len(target):
        j = 0
        flag = True
        while j < len(block):
            if i < len(target) and target[i] == block[j]:
                i += 1
                flag = False
            j += 1
        if flag: return -1
        res += 1
    return res

target = 'abcd'  # 'aaa', 'abcd'
block = 'bcad'  # 'ad', 'bcad'
print(demo(target, block))

无连续1的最小字符串

给定一个只包含0和1的字符串,判断其中有无连续的1。若有,则输出比该串大的无连续1的最小值串。若无,则不做操作。
例:给定 ‘11011’ ,则输出 ‘100000’ ;给定 ‘10011’ ,则输出 ‘10100’ 。

分析:
还是贪心的思想
1)从左往右寻找,找到最开始连续的两个1,如果没有则直接返回原字符串
2)在连续的位置上,将前一个符号(0)变为1,后面的符号全变为0
3)变换之后再继续检查时候有连续的1,如果没有就直接返回

Python源码:

def demo(strInput):
    start = check(strInput)

    while start != -1:
        if start == 0:
            strInput = '1' + '0'*len(strInput)
        else:
            strInput = strInput[:start-1] + '1' + '0'*(len(strInput)-start)
        start = check(strInput)

    return strInput

def check(strInput):
    for i in range(len(strInput)-1):
        if strInput[i] == strInput[i+1] and strInput[i] == '1':
            return i
    return -1

print(demo('10011'))

集合子集

给定一个集合,输出该集合的所有子集,并给出所编写算法的时间复杂度。
例:给定集合 [1, 2, 3],输出 [[1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]。

LeetCode原题:剑指 Offer II 079. 所有子集

分析:
回溯思想,分为两种情况,即当前元素是否放入子集中

Python源码:

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        res = []
        path = []

        def backtracking(cur):
            if cur == len(nums):
                res.append(path[:])
                return
            
            # 当前元素放入path中
            path.append(nums[cur])
            backtracking(cur + 1)
            path.pop()

            # 当前元素不放入path中
            backtracking(cur + 1)

        backtracking(0)
        return res

大数乘法

在这里插入图片描述

以字符串的形式读入两个数字,编写一个函数计算它们的乘积,以字符串形式返回。

牛客原题:NC10 大数乘法

分析:
优秀题解:
【算法】大数乘法问题及其高效算法
Karatsuba大数乘法算法
python大数相乘

Python源码:

class Solution:
    def solve(self , s: str, t: str) -> str:
        # write code here
        if s == '0' or t == '0': return '0'
        res = [0] * (len(s) + len(t))
        sList = list(s)
        tList = list(t)
        sList.reverse()
        tList.reverse()
        # 将t里面的每个数字与s相乘,并保存在对应为res位数上
        for i in range(len(sList)):
            for j in range(len(tList)):
                res[i+j] += int(sList[i]) * int(tList[j])
        # 将res位数上的数字进行取余和求商运算,余数放在当前位置上,商进行进位
        for i in range(len(res)):
            temp = res[i]
            res[i] = str(temp % 10)
            if i < len(res)-1: res[i+1] = res[i+1] + temp // 10
        # 返回结果,注意移除res前面的0
        res.reverse()
        resStr = ''.join(res)
        for i in range(len(resStr)):
            if resStr[i] != '0': break
        resStr = resStr[i:]
        return resStr

最长回文子串

LeetCode原题:5. 最长回文子串

终端BG-AI与智慧全场景二面

滑动窗口

LeetCode原题:209. 长度最小的子数组

百度

百度自动驾驶一面

重复字符的速记方式

有一种重复字符的速记方式
(1)重复的部分会被以“(重复内容)<重复次数>”形式记录,并且可能存在嵌套缩写关系
(2)不重复的部分按原样记录;
先给定一个字符串str,请实现一个函数恢复字符串速记前的内容,str仅由小写字母、数字、()、<>组成。
例如:
str=“abc(d)<2>”,速记前字符串为:“abcdd”
str=“a(b(c)<2>d)<3>e”,速记前字符串为:“abccdbccdbccde”

分析:
第一想法是简单的,那就是要使用栈,那怎么来记录这些特殊字符和字母让我再面试的犯难了,一时间没想出来,后面静静一下就明白,步骤如下:
1)根据下标i循环将str中的字符压入栈stack中
2)判断当前字符是不是符号")“,如果是就说明当前遇到重复字符了,这里需要注意一个点,如果是")“符号,则执行步骤3)、4)、5),执行完后循环下标i需要跳过”<>“里面的内容,如果不是”)"符号,则i+1,进入下一轮的循环
3)读取符号”)“后面”<>“里面的内容,即获取重复的次数
4)读取符号”)“前面到最近的一个”(“符号,实现方法是,while循环从栈stack中pop出元素,将弹出来的元素放在一个新的临时栈temp_stack中,知道遇到符号”(“,则停止循环,此时temp_stack里面就存放了当前的重复内容
5)根据重复的次数,再依次地将temp_stack的元素放回栈stack中,注意放回去的时候还是要从temp_stack的栈顶开始遍历
6)直到字符串str中所有的元素遍历结束,stack里面就存放了速记前的内容了饿,直接返回”".join(stack),就完成整个函数

Python源码

def demo(strInput):
    stack = []
    i = 0
    while i < len(strInput):
        if strInput[i] == ')':
            # 读取 <> 里面的重复次数
            j = i + 2 # 跳过 < 符号
            times = ""
            while strInput[j] != '>':
                times += strInput[j]
                j += 1
            times = int(times)
            # 读取 ) 符号到最近 ( 符号里面的重复内容
            str_val = stack.pop()
            temp_stack = []
            while str_val != '(':
                temp_stack.append(str_val)
                str_val = stack.pop()
            # 根据重复次数依次将temp_stack里面的元素再放回stack中
            for i in range(times):
                for v in range(len(temp_stack)-1, -1, -1):
                    stack.append(temp_stack[v])
            # 下标 i 跳位,跳过 <> 符号以及里面的重复次数
            i = j + 1
        else:
            stack.append(strInput[i])
            i += 1
    return "".join(stack)

有效的括号

因为上面那道题面试的时候没有做出来,面试官比较和谐,又给了一道简单题,这题是LeetCode的一道原题,还好做过,一口气就写出来了
LeetCode:20. 有效的括号
代码随想录:20. 有效的括号

给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
1)左括号必须用相同类型的右括号闭合。
2)左括号必须以正确的顺序闭合。
例子:
输入:s = “(]”,输出:false
输入:s = “()[]{}”,输出:true
输入:s = “([)]”,输出:false

分析:
1)快速判断,如果str的长度不是一个偶数,说明肯定不匹配,可直接返回False
2)依次将str放入stack栈中
3)判断当前字符是不是")“、”]“、”}"中的一个,如果是,就将stack里面的元素pop一个出来,判断弹出来的元素是不是跟当前字符是不是匹配的,如果不是直接返回False
4)最后返回的时候只需判断stack是否为空,如果是的则返回True

Python源码

def demo(strInput):
    # 快速判断
    if len(strInput) % 2 != 0: return False
    stack = []
    str_pairs = {
    
    ')': '(', ']': '[', '}': '{'}
    for v in strInput:
        if v in str_pairs:
            # 细节问题:应该要先判断stack是否为空
            # 不然当第一个元素就为')'、']'或'}'时,程序会报错
            # 面试时候这点没有考虑到,直接判断了pop出来的元素
            if len(stack) == 0 or stack[-1] != str_pairs[v]:
                return False
            else:
                stack.pop()
        else:
            stack.append(v)
    # 细节问题:我面试的时候写的是
    # if len(stack) == 0: return True
    # else: return False
    # 这里被面试官提醒了,说这个返回太臃肿了,可以不可以简化一下
    # 结果我没说出来
    # 面试官直接说了:
    # return len(stack) == 0
    # return not stack 可能是最简洁的了,又学习了
    return not stack

字节

字节智能创作一面

有序数组的平方

直接出了一道简单题,但因为前面被PUA得太厉害了,脑袋一片空白,这道刷过的题没有AC掉,心里非常的难受
LeetCode:977. 有序数组的平方
代码随想录:977. 有序数组的平方
与LeetCode原题有一点细微差别就是要求返回的是不重复的元素

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
例子:
输入:nums = [-5,-4,-1,0,1,3,5,5],输出:[0,1,9,16,25]
输入:nums = [-4,-1,0,3,10],输出:[0,1,9,16,100]

分析
1)定义首尾双指针,即left和right指针,分别指向输入数组的第一个元素和最后一个元素
2)判断left和right指向的元素的平方值,将较大的那个指针的平方值放入result的末端,注意一定是末端,所以得使用insert,面试的时候我直接使用了append,直接导致我这道题失败,因为脑袋已经空白,这么简单问题也没有发现,然后移动指针,如果是left就右移,如果是right就左移

Python源码

def demo(nums):
    result = []
    left, right = 0, len(nums)-1
    pre = -1 # 记录添加到result里面的最新元素,用来避免重复元素的添加
    while left <= right: # 一定要有等于,不然会漏掉一个元素
        left_square = nums[left] * nums[left]
        right_square = nums[right] * nums[right]
        if left_square >= right_square: # 取出那个较大的平方值
            if left_square != pre:
                # 注意一定是insert,而不是append!!!
                # 插入到数组的末端
                result.insert(0, left_square)
                pre = left_square
            left += 1
        else:
            if right_square != pre:
                result.insert(0, right_square)
                pre = right_square
            right -= 1
    return result

TnS内容安全一面

手机屏幕解锁方式

输入:m=2,n=2,s=2。m,n表示矩阵的大小,即m行n列,s表示解锁的步数,即划过几个点
输出:‘1->2’, ‘1->3’, ‘1->4’, ‘2->1’, ‘2->3’, ‘2->4’, ‘3->1’, ‘3->2’, ‘3->4’, ‘4->1’, ‘4->2’, ‘4->3’

如果m=2,n=2的话,矩阵就为:
[[1, 2],
[3, 4]]
如果m=3,n=3的话,矩阵就为:
[[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]

分析:
本题思路还是不难,直接就能想到用回溯
难点就是代码能力考察
相似的题
LeetCode:79. 单词搜索

Python源码

def demo(m, n, s):
    res = []
    path = []

    # 生成矩阵
    # m=2, n=2: [[1,2], [3,4]]
    # m=3,n=3: [[1,2,3], [4,5,6], [7,8,9]]
    board = [[0] * n for _ in range(m)]
    start = 1
    for i in range(m):
        for j in range(n):
            board[i][j] = start
            start += 1

    def backtracking(board, i, j):
        # 如果路径长度等于s,说明该路径已经完成,直接返回True
        if len(path) == s:
            res.append('->'.join(path))
            return True
        # 如果回溯的下标不满足矩阵的范围,就返回False
        if not (0<=i and i<m and 0<=j and j<n): return False
        # 用-1表示这个位置已经经过,就返回False
        if board[i][j] == -1: return False

        # 在path中添加当前元素
        path.append(str(board[i][j]))
        temp = board[i][j]
        # 将当前元素置为-1,表示当前元素已经经过
        board[i][j] = -1
        flag = False
        for si in range(-1, 2):
            for sj in range(-1, 2):
                if si == 0 and sj == 0: continue
                if backtracking(board, i+si, j+sj):
                    flag = True
                    break
            if flag: break
        # 弹出当前元素
        path.pop()
        # 重新归位当前元素
        board[i][j] = temp

    # 遍历所有元素,定义路径的起点
    for i in range(m):
        for j in range(n):
            backtracking(board, i, j)

    return res

print(demo(2, 2, 2))

TnS内容安全二面

查找问题

LeetCode原题:33. 搜索旋转排序数组
81. 搜索旋转排序数组 II
在这里插入图片描述
分析:
核心思想:
将数组一分为二,其中一定有一个是有序的,另一个可能是有序,也能是部分有序。
此时有序部分用二分法查找。无序部分再一分为二,其中一个一定有序,另一个可能有序,可能无序。就这样循环
1)将中间值与当前左边的值进行比较,如果中间值大于等于当前左边的值,说明中间值的左边是有序的。然后再判断目标值是否在左边这个有序数组中,如果在,缩小右边范围;如果不在,则缩小左边范围
2)如果中间值小于当前左边的值,说明中间值的右边是有序的。然后再判断目标值是否在右边这个有序数组中,如果在,缩小左边范围;如果不在,则缩小右边范围
具体见二分法之旋转数组

Python源码:

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

            if nums[mid] >= nums[left]:
                if nums[left] <= target <= nums[mid]:
                    right = mid - 1
                else:
                    left = mid + 1
            else:
                if nums[mid] <= target <= nums[right]:
                    left = mid + 1
                else:
                    right = mid - 1

        return -1

TnS内容安全三面

滑动窗口

给定一个字符串,找到其中长度最长的没有重复字符的字串
例如:输入 ‘abbbcdefff’,输出 ‘bcdef’

Python源码:

def check(alphaDict):
    for key, val in alphaDict.items():
        if val > 1: return False
    return True

def demo(strInput):
    maxLen = 0
    res = ''
    left = right = 0
    alphaDict = dict()
    while right < len(strInput):
        if strInput[right] not in alphaDict: alphaDict[strInput[right]] = 1
        else: alphaDict[strInput[right]] += 1
        while not check(alphaDict):
            alphaDict[strInput[left]] -= 1
            left += 1
        tmpLen = right - left + 1
        if tmpLen > maxLen:
            maxLen = tmpLen
            res = strInput[left:right+1]
        right += 1
    return res

print(demo('abbbcdefff'))

蔚来

自动驾驶二面

字符串转换

同华为BG的一面题

快手

计算机视觉一面

计算IoU

美团

北斗-无人配送车一面

旋转链表

LeetCode原题:61. 旋转链表

滑动窗口最大值

LeetCode原题:239. 滑动窗口最大值

猜你喜欢

转载自blog.csdn.net/qq_33757398/article/details/125814937