剑指 Offer 03. 数组中重复的数字
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
方法一:哈希表
class Solution:
def findrepeatNumbers(self, nums: [int]) -> int:
dic = set()
for num in nums:
if num in dic: return num
dic.add(num)
return -1
方法二:原地交换
class Solution:
def findRepeatNumber(self, nums:[int]) -> int:
i = 0
while i < len(nums):
if nums[i] == i:
i += 1
continue
# 索引 nums[i] 处和索引 i 处的元素值都为 nums[i], 即找到一组重复值.
if nums[nums[i]] == nums[i]: return nums[i]
# 交换索引为 i 和 nums[i] 的元素值,将此数字交换至对应索引位置.
nums[nums[i], nums[nums[i]] = nums[nums[i]], nums[i]]
return -1
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的前半部分,所有偶数在数组的后半部分。
示例:
输入:nums = [1,2,3,4]
输出:[1,3,2,4]
注:[3,1,2,4] 也是正确的答案之一。
class Solution:
def exchange(self, nums: List[int]) -> List[int]:
# 初始化双指针
i, j = 0, len(nums) - 1
while i < j:
# x & 1 位运算 等价于 x % 2 取余运算
while i < j and nums[i] & 1 == 1: i += 1
while i < j and nums[j] & 1 == 0: j -= 1
nums[i], nums[j] = nums[j], nums[i]
return nums
剑指 Offer 39. 数组中出现次数超过一半的数字
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2
哈希表统计法:
遍历数组 nums ,用 HashMap 统计各数字的数量,即可找出 众数 。此方法时间和空间复杂度均为 O(N) 。
class Solution:
def majorityElement(self, nums: List[int]) -> int:
counts = collections.Counter(nums)
return max(counts.keys(), key=counts.get)
数组排序法:
将数组 nums 排序,数组中点的元素 一定为众数。
class Solution:
def majorityElement(self, nums: List[int]) -> int:
nums.sort()
return nums[len(nums) // 2]
Boyer-Moore摩尔投票法:
核心理念为 票数正负抵消 。此方法时间和空间复杂度分别为 O(N) 和 O(1) ,为本题的最佳解法。
**思路:**如果我们把众数记为 +1,把其他数记为 -1,将它们全部加起来,显然和大于 0
,从结果本身我们可以看出众数比其他数多。
class Solution:
def majorityElement(self, nums: List[int]) -> int:
count,votes = 0, 0
for num in nums:
if votes == 0: x = num # 假设x = num为众数
votes += 1 if num = x else -1
# 验证x是否为众数
for num in nums:
if num == x: count += 1
return x if count > len(nums) //2 else 0 # 当无众数时返回0
剑指 Offer 40. 最小的k个数
输入整数数组 arr
,找出其中最小的 k
个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
示例 1:
输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
示例 2:
输入:arr = [0,1,2,1], k = 1
输出:[0]
快速排序原理:
快速排序算法有两个核心点,分别为 “哨兵划分” 和 “递归” 。
**哨兵划分操作:**以数组某个元素(一般选取首元素)为 基准数 ,将所有小于基准数的元素移动至其左边,大于基准数的元素移动至其右边。
class Solution:
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
def quick_sort(arr, l, r):
i, j = l, r # 初始化哨兵
while i < j:
while i < j and nums[i] <= num[j]: i += 1
while i < j and nums[i] >= num[j]: j -= 1
num[i], num[j] = num[j], num[i]
num[i], num[l] = num[l], num[i]
quick(arr, l, i - 1)
quick(arr, i - 1, r)
quick(arr, 0, len(arr) - 1)
return arr[:k]
方法二:基于快排的数组划分
题目只要求返回最小的 k 个数,对这 k 个数的顺序并没有要求。因此,只需要将数组划分为 最小的 k 个数 和 其他数字 两部分即可,而快速排序的哨兵划分可完成此目标。
根据快速排序原理,如果某次哨兵划分后 基准数正好是第 k+1 小的数字 ,那么此时基准数左边的所有数字便是题目所求的 最小的 k 个数 。
根据此思路,考虑在每次哨兵划分后,判断基准数在数组中的索引是否等于 k ,若 true 则直接返回此时数组的前 k 个数字即可。
算法流程:
getLeastNumbers() 函数:
若 k 大于数组长度,则直接返回整个数组;
执行并返回 quick_sort() 即可;
quick_sort() 函数:
注意,此时 quick_sort() 的功能不是排序整个数组,而是搜索并返回最小的 k个数。
哨兵划分:
划分完毕后,基准数为 arr[i] ,左 / 右子数组区间分别为 [l, i - 1][l,i−1] , [i + 1, r][i+1,r] ;
递归或返回:
若 k < i ,代表第 k + 1 小的数字在 左子数组 中,则递归左子数组;
若 k > i ,代表第 k + 1 小的数字在 右子数组 中,则递归右子数组;
若 k = i ,代表此时 arr[k] 即为第 k + 1小的数字,则直接返回数组前 k 个数字即可;
class Solution:
def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
if k >= len(arr): return arr
def quick_sort(arr, l, r):
i, j = l, r
while i < j:
while i < j and nums[i] <= num[l]: i += 1
while i < j and nums[j] >= num[l]: j -= 1
num[i], num[j] = num[j], num[i]
num[i], num[l] = num[l], num[i]
if k > i: return quick(arr, i - 1, r)
if k < i: return quick(arr, l, i - 1)
return arr[:k]
return quick(arr, 0, len(arr) - 1)
剑指 Offer 45. 把数组排成最小的数
输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
示例 1:
输入: [10,2]
输出: "102"
示例 2:
输入: [3,30,34,5,9]
输出: "3033459"
本文列举 快速排序 和 内置函数 两种排序方法,其他排序方法也可实现。
class Solution:
def minNumber(self, nums: List[int]) -> str:
def quick_sort(l , r):
if l >= r: return
i, j = l, r
while i < j:
while i < j and strs[i], strs[l] < strs[l], strs[i]: i += 1
while i < j and strs[j], strs[l] > strs[l], strs[j]: j -= 1
strs[i],strs[j] = strs[j], strs[i]
strs[i], strs[l] = strs[l], strs[i]
quick_sort(l, i - 1 )
quick_sort(i - 1, r )
strs = [str(num) for num in nums]
quick_sort(0, len(strs) - 1)
return ''.join(strs)
内置函数:
class Solution:
def minNumber(self, nums: List[int]) -> str:
def sort_rule(x, y):
a, b = x + y, y + x
if a > b: return 1
elif a < b: return -1
else: return 0
strs = [str(num) for num in nums]
strs.sort(key = functools.cmp_to_key(sort_rule)) # 对sort_rule函数进行升序
return ''.join(strs)