剑指offer-排序

剑指 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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MGonster

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值