阿翰 剑指offer 之 Day 16 排序 简单

本文介绍了一种特殊的排序算法应用案例,即将数组按特定规则排序以形成最小数值,及通过排序和遍历判断扑克牌是否能组成顺子的方法。文章提供了多种排序算法的实现细节,包括冒泡排序、快速排序等,并展示了如何通过集合Set优化判断过程。

目录

排序

1 把数组排成最小的数

1. 冒泡排序

​2. 内置函数:

​3. 快速排序:

3.2 快排写法改进

3.3 比较改进

 2 扑克牌中的顺子

1. 排序 + 遍历

​2. 集合 Set + 遍历

3. 排序+遍历


排序

1 把数组排成最小的数

剑指 Offer 45. 把数组排成最小的数https://leetcode-cn.com/problems/ba-shu-zu-pai-cheng-zui-xiao-de-shu-lcof/

此题求拼接起来的最小数字,本质上是一个排序问题。设数组 nums 中任意两数字的字符串为 x 和 y ,则规定 排序判断规则 为:

  • 若拼接字符串 x + y > y + x,则 x “大于” y ;
  • 反之,若 x + y < y + x ,则 x “小于” y ;

x “小于” y 代表:排序完成后,数组中 x 应在 y 左边;“大于” 则反之。

根据以上规则,套用任何排序方法对 nums 执行排序即可。

复杂度分析:

  • 时间复杂度 O(NlogN) : N 为最终返回值的字符数量( strs 列表的长度≤N );使用快排或内置函数的平均时间复杂度为 O(NlogN) ,最差为 O(N^2)  。
  • 空间复杂度 O(N) : 字符串列表 strs 占用线性大小的额外空间。

1. 冒泡排序

2. 内置函数:

需定义排序规则:

  • Python 定义在函数 sort_rule(x, y) 中;
  • Java 定义为 (x, y) -> (x + y).compareTo(y + x) ;
  • C++ 定义为 (string& x, string& y){ return x + y < y + x; } ;
class Solution {
    public String minNumber(int[] nums) {
        String[] strs = new String[nums.length];
        for(int i = 0; i < nums.length; i++)
            strs[i] = String.valueOf(nums[i]);
        Arrays.sort(strs, (x, y) -> (x + y).compareTo(y + x));
        StringBuilder res = new StringBuilder();
        for(String s : strs)
            res.append(s);
        return res.toString();
    } 
}

3. 快速排序:

需修改快速排序函数中的排序判断规则。字符串大小(字典序)对比的实现方法:

  • Python/C++ 中可直接用 < , >
  • Java 中使用函数 A.compareTo(B); 
class Solution {
    public String minNumber(int[] nums) {
        String[] strs = new String[nums.length];
        for(int i = 0; i < nums.length; i++)
            strs[i] = String.valueOf(nums[i]);
        quickSort(strs, 0, strs.length - 1);
        StringBuilder res = new StringBuilder();
        for(String s : strs)
            res.append(s);
        return res.toString();
    }
    void quickSort(String[] strs, int l, int r) {
        if(l >= r) return;
        int i = l, j = r;
        String tmp = strs[i];
        while(i < j) {
            while((strs[j] + strs[l]).compareTo(strs[l] + strs[j]) >= 0 && i < j) j--;
            while((strs[i] + strs[l]).compareTo(strs[l] + strs[i]) <= 0 && i < j) i++;
            tmp = strs[i];
            strs[i] = strs[j];
            strs[j] = tmp;
        }
        strs[i] = strs[l];
        strs[l] = tmp;
        quickSort(strs, l, i - 1);
        quickSort(strs, i + 1, r);
    }
} 

3.2 快排写法改进

class Solution {
    public String minNumber(int[] nums) {
        String[] strs = new String[nums.length];
        for (int i = 0; i < nums.length; i++) {
            strs[i] = String.valueOf(nums[i]);
        }
        quickSort(strs, 0, strs.length - 1);
        StringBuilder res = new StringBuilder();
        for (String s : strs)
            res.append(s);
        return res.toString();
    }

    public void quickSort(String[] strs, int low, int high) {
        if (low < high) {
            int middle = getMiddle(strs, low, high);
            quickSort(strs, low, middle - 1);
            quickSort(strs, middle + 1, high);
        }
    }

    public int getMiddle(String[] strs, int low, int high) {
        //数组的第一个数为基准元素
        String temp = strs[low];
        while (low < high) {
            //从后向前找比基准小的数
            while (low < high && (strs[high] + temp).compareTo(temp + strs[high]) >= 0)
                high--;
            //把比基准小的数移到低端
            strs[low] = strs[high];
            //从前向后找比基准大的数
            while (low < high && (strs[low] + temp).compareTo(temp + strs[low]) <= 0)
                low++;
            //把比基准大的数移到高端
            strs[high] = strs[low];
        }
        strs[low] = temp;
        return low;
    }
}

3.3 比较改进

不通过字符串,直接使用数字进行比较 同时考虑遇到后面的是0的情况必交换 

class Solution {
    public String minNumber(int[] nums) {
        StringBuilder sb = new StringBuilder(); 
        quickSort(nums,0, nums.length - 1); 
        for (int i = 0; i < nums.length; i++) {
            sb.append(nums[i]);
        }
        return sb.toString();
    }
    public void quickSort(int[] strs, int low, int high) {
        if (low < high) {
            int middle = getMiddle(strs, low, high);
            quickSort(strs, low, middle - 1);
            quickSort(strs, middle + 1, high);
        }
    }
    public int getMiddle(int[] strs, int low, int high) {
        //数组的第一个数为基准元素
        int temp = strs[low];
        while (low < high) {
            //从后向前找比基准小的数
            while (low < high && strs[high] != 0 && (strs[high] * Math.pow(10, getNum(temp))+ temp >= temp * Math.pow(10, getNum( strs[high])) + strs[high]))
                high--;
            //把比基准小的数移到低端
            strs[low] = strs[high];
            //从前向后找比基准大的数
            while (low < high && (strs[low] * Math.pow(10, getNum(temp))+ temp <= temp * Math.pow(10, getNum(strs[low])) + strs[low]))
                low++;
            //把比基准大的数移到高端
            strs[high] = strs[low];
        }
        strs[low] = temp;
        return low;
    }
    public int getNum(int a){
        int t = 0;
        while (a != 0){
            a  = a /10;
            t++;
        }
        return t;
    }
}

 2 扑克牌中的顺子

剑指 Offer 61. 扑克牌中的顺子https://leetcode-cn.com/problems/bu-ke-pai-zhong-de-shun-zi-lcof/

1. 排序 + 遍历

第一步:冒泡排序 如果遇到相同的 直接false

第二步:循环一遍找到有几个大小王(也就是有几个0)

第三步:比较最大值和最小值的差是否小于5 是则返回true 否则返回false

class Solution {
    public boolean isStraight(int[] nums) {
        int t = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == 0)   t++; 
        }
        for (int i = 0; i < nums.length - 1; i++) {
            for (int j = 0; j < nums.length - i - 1; j++) {
                if(nums[j] < nums[j+1])
                    swap(nums,j,j+1);
                else if (nums[j] == nums[j+1] && nums[j]!=0)
                    return false;
            }
        }
        if(nums[0] - nums[nums.length - t - 1] < 5)    return true;
        return false;
    }
    public void swap(int[] nums, int i, int j){
        int t;
        t = nums[i];
        nums[i] = nums[j];
        nums[j] = t;
    }
}

2. 集合 Set + 遍历

  • 遍历五张牌,遇到大小王(即 0 )直接跳过。
  • 判别重复: 利用 Set 实现遍历判重, Set 的查找方法的时间复杂度为 O(1) ;(提升!)
  • 获取最大 / 最小的牌: 借助辅助变量 ma 和 mi ,遍历统计即可。
class Solution {
    public boolean isStraight(int[] nums) {
        Set<Integer> repeat = new HashSet<>();
        int max = 0, min = 14;
        for(int num : nums) {
            if(num == 0) continue; // 跳过大小王
            max = Math.max(max, num); // 最大牌
            min = Math.min(min, num); // 最小牌
            if(repeat.contains(num)) return false; // 若有重复,提前返回 false
            repeat.add(num); // 添加此牌至 Set
        }
        return max - min < 5; // 最大牌 - 最小牌 < 5 则可构成顺子
    }
}

 复杂度分析:

  • 时间复杂度 O(N) = O(5) = O(1): 其中 N 为 nums 长度,本题中 N ≡ 5 ;遍历数组使用 O(N) 时间。
  • 空间复杂度 O(N) = O(5) = O(1) : 用于判重的辅助 Set 使用 O(N) 额外空间。

3. 排序+遍历

class Solution {
    public boolean isStraight(int[] nums) { 
        int joker = 0;
        Arrays.sort(nums); // 数组排序
        for(int i = 0; i < 4; i++) {
            if(nums[i] == 0) joker++; // 统计大小王数量
            else if(nums[i] == nums[i + 1]) return false; // 若有重复,提前返回 false
        }
        return nums[4] - nums[joker] < 5; // 最大牌 - 最小牌 < 5 则可构成顺子
    }   
}

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值