献芹奏曝-Python面试题-算法-数组篇
上一篇:献芹奏曝-Python面试题
开篇的话:本文目的是收集和归纳力扣上的算法题,希望用python语言,竭我所能做到思路最清奇、代码最简洁、方法最广泛、性能最高效,了解常见题目,找到最利于记忆的答案,更加从容的应对面试。希望广思集益,共同进步。
数组篇
-
26. 删除有序数组中的重复项(难度系数✯)
class Solution: def removeDuplicates(self, nums: List[int]) -> int: current_item = None for index,item in enumerate(nums[:]): if current_item == item: nums.remove(item) current_item = item return len(nums)
运行结果:1:耗时超过13%。2:内存超过62%
知识点/技巧: 数组有序,当前元素和下一个比较,相同就 remove
class Solution: def removeDuplicates(self, nums: List[int]) -> int: for i in range(len(nums) - 1, 0, -1): if nums[i] == nums[i - 1]: del nums[i] return len(nums)
运行结果:1:耗时超过47%。2:内存超过17%
知识点/技巧: 数组有序,倒叙循环,当前元素和前一个比较,相同就 del
class Solution: def removeDuplicates(self, nums: List[int]) -> int: """ 利用双指针,因为是有序的,鸠占鹊巢 """ slow = 0 fast = 0 while fast < len(nums): if nums[slow] != nums[fast]: slow += 1 nums[slow] = nums[fast] fast += 1 return slow + 1
运行结果:1:耗时超过62%。2:内存超过46%
知识点/技巧: 通过双指针,“鸠占鹊巢”
-
122. 买卖股票的最佳时机 II(难度系数✯)
class Solution: def maxProfit(self, prices: List[int]) -> int: # 一言以蔽之,如果明天比今天股价高,就买 is_have = False # 当前是否拥有股票 profit = 0 # 当前利润 if len(prices)<2: return profit for i in range(0,len(prices)): if is_have: # 如果手上还有股票,就抛 profit += prices[i] - prices[i-1] is_have = False if i+1<len(prices): if prices[i+1]>prices[i]: # 如果明天比今天股价高,就买。 is_have = True return profit
运行结果:1:耗时超过80%。2:内存超过33% 。3:思路奇特
知识点/技巧: 一言以蔽之,手上有股票就卖;明天比今天股价高,就买。
-
189. 轮转数组(难度系数✯)
class Solution: def rotate(self, nums: List[int], k: int) -> None: """ Do not return anything, modify nums in-place instead. """ if nums: if nums: k = k%len(nums) right = nums[:-k:] left = nums[-k::] nums.clear() nums.extend(left) nums.extend(right)
运行结果:1:耗时超过92%。2:内存超过18%
知识点/技巧: 先切片,再拼接
import copy from typing import List class Solution: def rotate(self, nums: List[int], k: int) -> None: """ Do not return anything, modify nums in-place instead. """ if nums: re_time = k % len(nums) len_nums = len(nums) if re_time > len_nums / 2: re_time = len_nums - re_time # 删除前面的元素往后面添加 for i in range(re_time): nums.append(nums.pop(0)) else: # 删除后面的元素往前面添加 for i in range(re_time): nums.insert(0, nums.pop(-1))
运行结果:直接超时
经验教训: 列表增删操作非常耗时
-
217. 存在重复元素(难度系数✯)
class Solution: def containsDuplicate(self, nums: List[int]) -> bool: return len(set(nums))!=len(nums)
运行结果:1:耗时超过40%。2:内存超过33%。3:代码简洁
知识点/技巧: 利用set去重,比较长度
class Solution: def containsDuplicate(self, nums: List[int]) -> bool: dict_resut = {} for item in nums: if dict_resut.get(item): return True else: dict_resut[item]=1 return False
运行结果:1:耗时超过76%。2:内存超过30%。
知识点/技巧: 利用字典记录字母出现次数
-
1. 两数之和(难度系数✯)
class Solution: def twoSum(self, nums: List[int], target: int) -> List[int]: for index,item in enumerate(nums): for i in range(index+1,len(nums)): if item + nums[i] == target: return [index,i]
运行结果:1:耗时超过36%。2:内存超过68%。3:虽慢但对
知识点/技巧: 遍历方法
class Solution: def twoSum(self, nums: List[int], target: int) -> List[int]: for index,item in enumerate(nums): result = target - item for i in range(index+1,len(nums)): if nums[i] == result: return [index,i]
运行结果:1:耗时超过36%。2:内存超过65%。3:虽然没有减少时间复杂度,但是减少了求和次数
知识点/技巧:在方法一的基础上减少了求和运算,但是时间复杂度仍是O(n*n)
class Solution: def twoSum(self, nums: List[int], target: int) -> List[int]: dict_res = {} for index,item in enumerate(nums): result=target-item if dict_res.get(result) != None: return [dict_res.get(result),index] dict_res[item]=index
运行结果:1:耗时超过58%。2:内存超过41%。3:利用哈希表
知识点/技巧: 哈希表作为读取和写入都很快的数据结构,常常用来实现空间换时间的效果。
-
167. 两数之和 II - 输入有序数组(难度系数✯)
class Solution: import bisect def twoSum(self, numbers: List[int], target: int) -> List[int]: for index,item in enumerate(numbers): remain = target-item j = bisect.bisect_left(numbers,remain,index+1) if j != len(numbers) and numbers[j] == remain: return [index+1,j+1]
运行结果:1:耗时超过67%。2:内存超过45%。3:利用二分查找法
知识点/技巧: 利用升序数据的特性,通过二分查找方法来快速找到我们需要的值。
class Solution: import bisect def twoSum(self, numbers: List[int], target: int) -> List[int]: i,j = 0, len(numbers)-1 while i<j: current = numbers[i] + numbers[j] if current == target: return [i+1,j+1] elif current<target: i += 1 else: j -= 1
运行结果:1:耗时超过53%。2:内存超过40%。3:指针移动
知识点/技巧: 使用指针移动技巧在时间复杂度和空间复杂度都更加优化的解法。
-
15. 三数之和(难度系数✯✯)
class Solution: def threeSum(self, nums: List[int]) -> List[List[int]]: nums.sort() result_list = [] for index, item in enumerate(nums): if index and item == nums[index-1]: # 当前元素和上一元素相同 continue elif item>0: # 点睛之笔 break target = -item i, j = index + 1, len(nums) - 1 is_move_i = None while i < j: current = nums[i] + nums[j] if is_move_i == True: if nums[i-1] == nums[i]: i += 1 continue elif is_move_i == False: if nums[j+1] == nums[j]: j -= 1 continue if current == target: result_list.append([item, nums[i], nums[j]]) i += 1 j -= 1 is_move_i = True elif current < target: i += 1 is_move_i = True else: is_move_i = False j -= 1 return result_list
运行结果:1:耗时超过58%。2:内存超过50%。3:三数之和转换成两数和问题。
知识点/技巧: 首先对数据进行排序,遍历的时候如果当前节点大于0,就无需继续遍历。
-
18. 四数之和(难度系数✯✯)
class Solution: def fourSum(self, nums: List[int], target: int) -> List[List[int]]: nums.sort() print(nums) result_list = [] for i in range(0, len(nums) - 3): if i > 0 and nums[i - 1] == nums[i]: continue if nums[i] + nums[len(nums) - 3] + nums[len(nums) - 2] + nums[len(nums) - 1] < target: # 当前数+后面最大的三个值仍小于目标值,遍历下一位 continue if nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target: # 当前数+后面最小的三个值仍大于目标值,退出 break for j in range(i + 1, len(nums) - 2): m, n = j + 1, len(nums) - 1 if nums[i] + nums[j] + nums[len(nums) - 2] + nums[len(nums) - 1] < target: continue if j > i + 1 and nums[j - 1] == nums[j]: continue if nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target: break is_move_m = None while m < n: current = nums[i] + nums[j] + nums[m] + nums[n] if is_move_m == True: if nums[m - 1] == nums[m]: m += 1 continue elif is_move_m == False: if nums[n + 1] == nums[n]: n -= 1 continue if current == target: result_list.append([nums[i], nums[j], nums[m], nums[n]]) m += 1 n -= 1 is_move_m = True elif current < target: m += 1 is_move_m = True else: is_move_m = False n -= 1 return result_list
运行结果:1:耗时超过82%。2:内存超过9%。
知识点/技巧: 在处理三数之和的基础,暴力循环,循环时进行优化
-
36. 有效的数独(难度系数✯✯)
class Solution: def get_dic(self): dic_result = {} for i in range(1, 10): key_1 = "0_%s" % i key_2 = "%s_0" % i dic_result.setdefault(key_1, []) dic_result.setdefault(key_2, []) if not i % 3: key_3 = "3_%s" % i key_6 = "6_%s" % i key_9 = "9_%s" % i dic_result.setdefault(key_3, []) dic_result.setdefault(key_6, []) dic_result.setdefault(key_9, []) return dic_result def isValidSudoku(self, board: List[List[str]]) -> bool: dic_result = self.get_dic() for raw_index, raw_item in enumerate(board): for col_index, col_item in enumerate(raw_item): if col_item != ".": dic_result[str(raw_index + 1) + "_0"].append(col_item) dic_result["0_" + str(col_index + 1)].append(col_item) key_sq_r = (raw_index // 3 + 1) * 3 key_sq_c = (col_index // 3 + 1) * 3 dic_result["%s_%s" % (key_sq_r, key_sq_c)].append(col_item) for value in dic_result.values(): if len(set(value)) != len(value): return False else: return True
运行结果:1:耗时超过88%。2:内存超过68%。
知识点/技巧: 只要我的方法够暴力,问题就难不倒我
-
48. 旋转图像(难度系数✯✯✯)
class Solution: def rotate(self, matrix: List[List[int]]) -> None: """ Do not return anything, modify matrix in-place instead. """ mid_line = len(matrix) // 2 if len(matrix) % 2 else len(matrix) // 2 for i in range(mid_line): for j in range(len(matrix)): matrix[i][j], matrix[len(matrix) - 1 - i][j] = matrix[len(matrix) - i - 1][j], matrix[i][j] for i in range(len(matrix)): for j in range(i, len(matrix)): matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
运行结果:1:耗时超过29%。2:内存超过74%。
知识点/技巧: 先水平翻转,再进行主对角线翻转
-
554. 砖墙(难度系数✯✯)
class Solution: def leastBricks(self, wall) -> int: dict_sum = {} for row in wall: sum = 0 for item in row[:len(row)-1]: sum += item dic_value = dict_sum.get(sum,0) dic_value += 1 dict_sum[sum] = dic_value if not dict_sum: return len(wall) max_ = max(dict_sum.values()) return len(wall) - max_
运行结果:1:耗时超过80%。2:内存超过11%。
知识点/技巧: 每行累计求和找到墙缝
-
539. 最小时间差(难度系数✯)
class Solution: def findMinDifference(self, timePoints: List[str]) -> int: # 如果长度大于24*60个 绝对会重复 if len(timePoints)>24*60: return 0 list_r = [] for item in timePoints: hh,mm = item.split(":") list_r.append(int(hh)*60+int(mm)) list_r.sort() result = 24*60 for i,item in enumerate(list_r): if i==0: # 首位判断,转一圈,绕过去 result = min( list_r[-1]-item,result-list_r[-1]+item) else: result = min(result,item-list_r[i-1]) if result == 0: return result return result
运行结果:1:耗时超过90%。2:内存超过96%。
知识点/技巧: 1:抽屉原理 判断极限 24*60。2:直接小时数乘60+分钟数,排序,最后再比较一下首尾就行。