大厂面试高频题目——其他

数组

347. 前 K 个高频元素

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

思考

  1. 哈希+内置排序
class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        num_dict = defaultdict(int)
        for c in nums:
            num_dict[c]+=1
        index_dict = defaultdict(list)
        for key in num_dict:
            index_dict[num_dict[key]].append(key)
        keys = list(index_dict.keys())
        keys.sort(reverse=True)
        cnt = 0
        res = []
        for key in keys:
            res+=index_dict[key]
            cnt=len(res)
            if cnt == k:
                break
        return res
class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        num_dict = defaultdict(int)
        for c in nums:
            num_dict[c]+=1
        kvlist = [] 
        for num,freq in num_dict.items():
            kvlist.append((freq,num))
        kvlist.sort(key=lambda x:x[0],reverse=True)    
        res = []
        for item in kvlist[:k]:
            res.append(item[1])
        return res
  1. 哈希+最小堆
import heapq
class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        num_dict = defaultdict(int)
        for c in nums:
            num_dict[c]+=1
        pri_que = [] #小顶堆
        for num,freq in num_dict.items():
            heapq.heappush(pri_que,(freq,num))
            if len(pri_que)>k:
                heapq.heappop(pri_que) 
        res = []
        print(pri_que)
        for item in pri_que:
            res.append(item[1])
        return res

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

思考

堆排序(小顶堆,上面的是最小的,堆的大小维持K,则堆中保持的是前K大。)

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        pq = []
        for c in nums:
            heapq.heappush(pq,c)
            if len(pq) > k:
                heapq.heappop(pq)
        #print(pq)
        return pq[0]

快速选择排序

def quick_select(arr,low,high,k):
    if low<=high:
        pi = partition(arr,low,high)
        if pi == len(arr) - k:
            return arr[pi]
        elif pi < len(arr) - k:
            return quick_select(arr,pi+1,high,k)         
        else:
            return quick_select(arr,low,pi-1,k)
def partition(a,low,high):
    # 左闭右闭
    pi_value = a[high]
    i = low -1
    for j in range(low,high):
        if a[j] < pi_value:
            i+=1
            a[i],a[j] = a[j],a[i]
    i+=1
    a[i],a[high] = a[high],a[i]
    return i 

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        return quick_select(nums,0,len(nums)-1,k)

442. 数组中重复的数据

给你一个长度为 n 的整数数组 nums ,其中 nums 的所有整数都在范围 [1, n] 内,且每个整数出现 一次 或 两次 。请你找出所有出现 两次 的整数,并以数组形式返回。

你必须设计并实现一个时间复杂度为 O(n) 且仅使用常量额外空间的算法解决此问题。

思路

  1. 值i放在nums[i-1]里,如果不是这种情况,就一直交换。这种解法不确定while是否会陷入死循环。
class Solution:
    def findDuplicates(self, nums: List[int]) -> List[int]:
        # 值i放在nums[i-1]里
        #[1,2,3,4,5]
        for i in range(len(nums)):
            while nums[i] != nums[nums[i]-1]:
                #swap
                p = nums[i]
                # 
                nums[i] , nums[p-1] =  nums[p-1],nums[i]
        res = []
        for i,num in enumerate(nums):
            if num != i+1:
                res.append(num)
        return res
  1. nums本身当做哈希,出现过一次的值,以其值为下标的数组元素值取反,作为访问的标记。思想很妙,容易理解。
class Solution:
    def findDuplicates(self, nums: List[int]) -> List[int]:
        # nums[i] 当做哈希,取相反数表示值i-1被访问过
        res = []
        for i in range(len(nums)):
            p = abs(nums[i])-1
            if nums[p] < 0:
                res.append(abs(nums[i]))
            else:
                nums[p] = -nums[p]
        return res

33. 搜索旋转排序数组

整数数组 nums 按升序排列,数组中的值 互不相同 。

在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

思考

将数组一分为二,其中一定有一个是有序的,另一个可能是有序,也能是部分有序。
此时有序部分用二分法查找。无序部分再一分为二,其中一个一定有序,另一个可能有序,可能无序。就这样循环.

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums)-1
        #[left,right]
        mid = -1
        while left<=right:
            mid = int((left+right)/2)
            if nums[mid] == target:
                return mid
            if nums[mid] >= nums[left]:
                #左有序
                if target >= nums[left] and target < nums[mid]:
                    # 二分
                    right = mid - 1
                else:
                    left = mid + 1
            else:
                #右有序
                if target > nums[mid] and target <= nums[right]:
                    left = mid + 1
                else:
                    right = mid -1
        return -1

34. 在排序数组中查找元素的第一个和最后一个位置

思考

两个二分搜索分别确定左右边界

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        def getRightBorder(nums, target):
            left = 0 
            right = len(nums)-1
            right_border = -2
            while left<=right:
                mid = int((left+right)/2)
                if nums[mid] > target:
                    right = mid - 1
                else:
                    left = mid + 1
                    right_border = left
            return right_border
        
        def getLeftBorder(nums, target):
            left = 0 
            right = len(nums)-1
            left_border = -2
            while left<=right:
                mid = int((left+right)/2)
                if nums[mid] < target:
                    left = mid + 1
                else:
                    right = mid - 1
                    left_border = right
            return left_border
        
        right_border = getRightBorder(nums,target)
        left_border = getLeftBorder(nums, target)
        print(left_border,right_border)
        if left_border == -2 or right_border == -2 :
            return [-1,-1]
        if right_border-1 >= left_border+1:
            return [left_border+1,right_border-1]
        else:
            return [-1,-1]

哈希

560. 和为 K 的子数组

  1. 暴力
    固定left,逐个移动right ,会超时
class Solution:
    def subarraySum(self, nums: List[int], k: int) -> int:
        count = 0
        for i in range(len(nums)):
            left = i
            sum_ = 0
            for j in range(left,len(nums)):
                sum_+=nums[j]
                if sum_ == k:
                    count+=1
        return count
  1. 前缀和
    会超时
class Solution:
    def subarraySum(self, nums: List[int], k: int) -> int:
        count = 0
        presum = [0] * (len(nums)+1)
        for i in range(len(nums)):
            presum[i+1] = presum[i] + nums[i]
        for i in range(len(nums)):
            left = i
            for j in range(left,len(nums)):
                #[left,right]的和
                sum_ = presum[j+1] - presum[i]
                if sum_ == k:
                    count+=1
        return count
  1. 前缀和+哈希优化(类似两数之和,好难想到)
    注意哈希初始化presum_dict[0]=1
class Solution:
    def subarraySum(self, nums: List[int], k: int) -> int:
        count = 0
        presum_dict = defaultdict(int)
        presum_dict[0]=1
        presum = 0
        for i in range(len(nums)):
            presum+=nums[i]
            if presum - k in presum_dict:
                count+=presum_dict[presum - k]
            presum_dict[presum]+=1
        return count

排序

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

  1. 堆排序
class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        pq = []
        for c in nums:
            heapq.heappush(pq,c)
            if len(pq) > k:
                heapq.heappop(pq)
        return pq[0]

快速选择排序

def quick_select(arr,low,high,k):
    if low<=high:
        pi = partition(arr,low,high)
        if pi == len(arr) - k:
            return arr[pi]
        elif pi < len(arr) - k:
            return quick_select(arr,pi+1,high,k)         
        else:
            return quick_select(arr,low,pi-1,k)
def partition(a,low,high):
    # 左闭右闭
    pi_value = a[high]
    i = low -1
    for j in range(low,high):
        if a[j] < pi_value:
            i+=1
            a[i],a[j] = a[j],a[i]
    i+=1
    a[i],a[high] = a[high],a[i]
    return i 

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        return quick_select(nums,0,len(nums)-1,k)

快速排序

  1. 非原地排序
def quick_sort(arr):
    if len(arr) <= 1:
        return arr
    else:
        pivot = arr[0]  # 选择第一个元素作为基准
        less = [x for x in arr[1:] if x <= pivot]  # 所有小于等于基准的元素
        greater = [x for x in arr[1:] if x > pivot]  # 所有大于基准的元素

        # 递归排序子序列,并将结果与基准合并
        return quick_sort(less) + [pivot] + quick_sort(greater)

# 示例使用
arr = [3, 6, 8, 10, 1, 2, 1]
sorted_arr = quick_sort(arr)
print(sorted_arr)

2.原地排序

def quicksort_inplace(arr, low, high):
    if low < high:
        pi = partition(arr, low, high)
        quicksort_inplace(arr, low, pi - 1)  # Before pi
        quicksort_inplace(arr, pi + 1, high)  # After pi

def partition(arr, low, high):
    pivot = arr[high]  # 选择最右侧的元素作为基准
    i = low - 1
    for j in range(low, high):
        if arr[j] < pivot:
            i += 1
            arr[i], arr[j] = arr[j], arr[i]  # 交换元素
    arr[i + 1], arr[high] = arr[high], arr[i + 1]  # 交换基准到正确的位置
    return i + 1

# 示例数组
array = [3, 6, 8, 10, 1, 2, 1]

# 执行原地快速排序
quicksort_inplace(array, 0, len(array) - 1)
print(array)

哈希+链表

146. LRU 缓存

请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。

思考

这道题,代码量太大了,算是hard。尤其是手动实现双向链表及其操作。


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

    def __init__(self, capacity: int):
        self.cache = {}
        self.head = DListNode()
        self.tail = DListNode()
        self.head.next = self.tail
        self.tail.prev = self.head
        self.capacity = capacity
        self.size = 0         

    def get(self, key: int) -> int:
        if key not in self.cache:
            return -1
        # 先定位,再移动到头
        node = self.cache[key]
        self.moveToHead(node)
        return node.value

    def put(self, key: int, value: int) -> None:
        if key not in self.cache:
            node = DListNode(key,value)
            self.cache[key] = node
            self.addToHead(node)
            self.size+=1
            if self.size > self.capacity:
                removed = self.removeTail()
                # 删哈希对应的项
                self.cache.pop(removed.key)
                self.size-=1
        else:
            # 先定位,修改value,再移动到头部
            node = self.cache[key]
            node.value = value
            self.moveToHead(node)
    def addToHead(self, node):
        node.prev = self.head
        node.next = self.head.next
        self.head.next = node
        node.next.prev = node   
    def removeNode(self, node):
        node.prev.next = node.next
        node.next.prev = node.prev
    def moveToHead(self, node):
        #先删再插
        self.removeNode(node)
        self.addToHead(node)
    def removeTail(self):
        node = self.tail.prev
        self.removeNode(node)
        #要删哈希,所以返回
        return node

二叉树

102. 二叉树层序遍历


# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if root is None:
            return []
        result = []
        queue = deque()
        queue.append(root)
        while queue:
            level = []
            for i in range(len(queue)):
                node = queue.popleft()
                level.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            result.append(level)
        return result

模拟

阿拉伯数字转中文字符串表达

思考

非常蛋疼的一道题,尤其是零的处理,面试感觉至少要写半小时

def num2ch(num):
    num_map = {
        '0': '零', '1': '一', '2': '二', '3': '三', '4': '四',
        '5': '五', '6': '六', '7': '七', '8': '八', '9': '九'
    }
    one = (num%10000)
    wan = int((num/10000)%10000)
    yi = int(num/100000000)
    wei_str = ['','十','百','千']
    
    print(yi,wan,one)
    def num2ch_4(num):
        num = str(num)
        temp = []
        for i,c in enumerate(num[::-1]):
            if c == '0':
                temp.append(num_map[c])
            else:
                temp.append(num_map[c]+wei_str[i])
        temp.reverse()
        # 删除个位的零
        res = ''.join(temp).rstrip('零')
        # 删除连续的零
        #print(res)
        new_res = res[0]
        for i in range(1,len(res)):
            if res[i] != res[i-1]:
                new_res+=res[i]
        res = new_res               
        # 跨10**4,不足10**3的的数补个0
        if len(num)<4:
            res = '零'+res
        return res
    ch_str = ''
    if yi!=0:
        ch_str+=num2ch_4(yi)+'亿'
    if wan!=0:
        ch_str+=num2ch_4(wan)+'万'
    if one!=0:
        ch_str+=num2ch_4(one)
    ch_str = ch_str.lstrip('零')
    return ch_str

print(num2ch(123456789123))
print(num2ch(123005078920))
print(num2ch(10001))
print(num2ch(11001))
print(num2ch(10011001))

单调栈

739. 每日温度

请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。

例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。

提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。

思考

单调栈经典题目。

class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        stack = []
        res = [0] * len(temperatures)
        stack.append((0,temperatures[0]))
        for i in range(1,len(temperatures)):
            while len(stack) > 0 and stack[-1][1] < temperatures[i]:
                index,_ = stack.pop()
                res[index] = i-index
            stack.append((i,temperatures[i]))
        return res

链表

143. 重排链表

思考

链表无法按下标访问,逐个遍历后放进数组里面,可以按索引访问,然后双指针一前一后依次添加。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reorderList(self, head: Optional[ListNode]) -> None:
        """
        Do not return anything, modify head in-place instead.
        """
        node_list = []
        cur = head
        while cur:
            node_list.append(cur)
            cur = cur.next
        i = 0 
        j = len(node_list)-1
        count = 0
        last_index = 0
        while count <= len(node_list)-1:
            if count%2 == 0:
                node_list[i].next = node_list[j]
                last_index = i
                i+=1
            else:
                node_list[j].next = node_list[i]
                last_index = j
                j-=1
            count+=1
        node_list[last_index].next = None

25. K 个一组翻转链表

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
def reverseNode(head):
    pre = None
    cur = head
    while cur:
        temp = cur.next
        cur.next = pre
        pre = cur
        cur = temp
    return pre
    
class Solution:
    def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
        dummy = ListNode(0)
        dummy.next = head
        cur = dummy
        pre = dummy
        i = 0
        while cur:
            if i > 0 and i % k == 0:
                temp = cur.next
                cur.next = None
                temp2 = pre.next
                reverseNode(pre.next)
                pre.next = cur
                temp2.next = temp
                cur = temp2
                pre = temp2
            cur = cur.next
            i+=1
        return dummy.next
posted @ 2024-06-01 18:58  forrestr  阅读(24)  评论(0)    收藏  举报