LeetCode 405. 数字转换为十六进制数(补码的问题) / 166. 分数到小数(模拟长除法) / 482. 密钥格式化

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 405. 数字转换为十六进制数(补码的问题) / 166. 分数到小数(模拟长除法) / 482. 密钥格式化相关的知识,希望对你有一定的参考价值。

405. 数字转换为十六进制数

2021.10.2 每日一题

题目描述

给定一个整数,编写一个算法将这个数转换为十六进制数。对于负整数,我们通常使用 补码运算 方法。

注意:

十六进制中所有字母(a-f)都必须是小写。
十六进制字符串中不能包含多余的前导零。如果要转化的数为0,那么以单个字符’0’来表示;对于其他情况,十六进制字符串中的第一个字符将不会是0字符。
给定的数确保在32位有符号整数范围内。
不能使用任何由库提供的将数字直接转换或格式化为十六进制的方法。

示例 1:

输入:
26
输出:
“1a”

示例 2:

输入:
-1
输出:
“ffffffff”

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/convert-a-number-to-hexadecimal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

四个一组变就行了
要注意这里右移的时候,要用>>>,如果用>>就死循环了
>>:带符号右移。正数右移高位补0,负数右移高位补1
>>>:无符号右移,正数负数右移都高位补0

class Solution {
    static Map<Integer, String> map = new HashMap<>();
    static{
        map.put(10, "a");
        map.put(11, "b");
        map.put(12, "c");
        map.put(13, "d");
        map.put(14, "e");
        map.put(15, "f");
    }
    public String toHex(int num) {
        //对于二进制位,每四位转换成一个十六进制数就好了
        if(num == 0)
            return "0";
        StringBuilder sb = new StringBuilder();
        while(num != 0){
            int temp = num & 15;
            if(map.containsKey(temp)){
                sb.append(map.get(temp));
            }else{
                sb.append(temp);
            }
            num >>>= 4;
        }
        return sb.reverse().toString();
    }
}

看了三叶姐的,又学到知识点了
对于负数来说,补码 + 该负数的相反数 = 2^32
好像这才是为什么叫补码的意思
然后知道得到了负数的表达形式以后,就可以用除16取余这种常规进制转换的方法做了

另一种解释,也很值得品味
所以,在计算机中,[0, 2^{31} - 1]范围内的数对应的十六进制存储方式为 00000000-7fffffff,[-2^{31}, -1]范围内的数对应的十六进制存储方式为 80000000-ffffffff。所以,我们可以先将负数统一加上 2^{32} ,使其映射到 [2^{31}, 2^{32} - 1]范围内,就可以直接进行十六进制转换了。

class Solution {   
    public String toHex(int num) {
        //最直观的思路肯定是除以16取余法,但是不会处理负数
        //学习三叶姐的
        //现在好像才明白为什么叫补码,补就是互补的意思
        //补码 + 相反数的原码 = 2^32
        if(num == 0)
            return "0";
        long n = num;
        StringBuilder sb = new StringBuilder();
        if(n < 0)
            n = (long)(Math.pow(2, 32) + n);    //关键
        while(n != 0){
            long u = n % 16;
            char c = (char)(u + '0');
            if(u >= 10)
                c = (char)((u - 10) + 'a');
            sb.append(c);
            n = n / 16;
        }
        return sb.reverse().toString();
    }
}

166. 分数到小数

2021.10.3 每日一题

题目描述

给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以 字符串形式返回小数 。

如果小数部分为循环小数,则将循环的部分括在括号内。

如果存在多个答案,只需返回 任意一个 。

对于所有给定的输入,保证 答案字符串的长度小于 104 。

示例 1:

输入:numerator = 1, denominator = 2
输出:“0.5”

示例 2:

输入:numerator = 2, denominator = 1
输出:“2”

示例 3:

输入:numerator = 2, denominator = 3
输出:“0.(6)”

示例 4:

输入:numerator = 4, denominator = 333
输出:“0.(012)”

示例 5:

输入:numerator = 1, denominator = 5
输出:“0.2”

提示:

-2^31 <= numerator, denominator <= 2^31 - 1
denominator != 0

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/fraction-to-recurring-decimal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

挺难的,确实挺难的
思路就是模拟除法
因为有MIN_VALUE的例子,所以需要转换成long进行处理
因为有负数,所以先都转换成正数就行处理
然后难点就是处理循环,用一个map去记录,当前余数和商,还有下标,如果当前余数和商都相同,那么说明就出现了循环
其实只要出现了相同的余数就肯定是出现了循环,因为除数是不变的

class Solution {
    public String fractionToDecimal(int _numerator, int _denominator) {
        //这题咋做了,感觉就是模拟除法?
        //想想怎么写
        //循环怎么写
        //有MIN_VALUE的例子

        long numerator = _numerator;
        long denominator = _denominator; 
        StringBuilder sb = new StringBuilder();
        //对负数进行处理
        if(numerator < 0 && denominator > 0){
            numerator = -numerator;
            sb.append("-");
        }
        else if(numerator > 0 && denominator < 0){
            denominator = -denominator;
            sb.append("-");
        }

        long base = numerator / denominator;
        long remainer = numerator % denominator;
        if(remainer == 0){
            sb.append(String.valueOf(base));
            return sb.toString();
        }
            

        //存放当前余数,和base,还有下标
        Map<Long, long[]> map = new HashMap<>();
        sb.append(base);
        sb.append(".");
        int idx = sb.length();
        boolean flag = false;

        while(remainer != 0){
            long temp = remainer * 10;
            base = temp / denominator;
            remainer = temp % denominator;
            //如果base和余数都是相同的,那么说明开始循环
            if(map.containsKey(remainer) && map.get(remainer)[0] == base){
                //到这里说明循环起来了
                flag = true;
                break;
            }
            sb.append(base);
            map.put(remainer, new long[]{base, sb.length() - 1});
        }
        if(!flag)
            return sb.toString();
        idx = (int)map.get(remainer)[1];
        sb.insert(idx, "(");
        sb.append(")");
        return sb.toString();
    }
}

答案思路也差不多就是这样
然后看三叶姐的题解,发现一个从来没有思考过的问题,那就是两个数相除,可能出现无限不循环小数吗
然后根据这个题思考一下,因为余数的上限就是除数,所以相除的过程中,肯定不可能出现无限个余数,所以两数相除只能是有限小数或者无限循环小数,妙啊

学习一下,三叶姐的这个格式化字符串的写法

class Solution {
    public String fractionToDecimal(int numerator, int denominator) {
        // 转 long 计算,防止溢出
        long a = numerator, b = denominator;
        // 如果本身能够整除,直接返回计算结果
        if (a % b == 0) return String.valueOf(a / b);
        StringBuilder sb = new StringBuilder();
        // 如果其一为负数,先追加负号
        if (a * b < 0) sb.append('-');
        a = Math.abs(a); b = Math.abs(b);
        // 计算小数点前的部分,并将余数赋值给 a
        sb.append(String.valueOf(a / b) + ".");
        a %= b;
        Map<Long, Integer> map = new HashMap<>();
        while (a != 0) {
            // 记录当前余数所在答案的位置,并继续模拟除法运算
            map.put(a, sb.length());
            a *= 10;
            sb.append(a / b);
            a %= b;
            // 如果当前余数之前出现过,则将 [出现位置 到 当前位置] 的部分抠出来(循环小数部分)
            if (map.containsKey(a)) {
                int u = map.get(a);
                //学习这个写法
                return String.format("%s(%s)", sb.substring(0, u), sb.substring(u));
            }
        }
        return sb.toString();
    }
}

482. 密钥格式化

2021.10.4 每日一题

题目描述

有一个密钥字符串 S ,只包含字母,数字以及 ‘-’(破折号)。其中, N 个 ‘-’ 将字符串分成了 N+1 组。

给你一个数字 K,请你重新格式化字符串,使每个分组恰好包含 K 个字符。特别地,第一个分组包含的字符个数必须小于等于 K,但至少要包含 1 个字符。两个分组之间需要用 ‘-’(破折号)隔开,并且将所有的小写字母转换为大写字母。

给定非空字符串 S 和数字 K,按照上面描述的规则进行格式化。

示例 1:

输入:S = “5F3Z-2e-9-w”, K = 4
输出:“5F3Z-2E9W”
解释:字符串 S 被分成了两个部分,每部分 4 个字符;
注意,两个额外的破折号需要删掉。

示例 2:

输入:S = “2-5g-3-J”, K = 2
输出:“2-5G-3J”
解释:字符串 S 被分成了 3 个部分,按照前面的规则描述,第一部分的字符可以少于给定的数量,其余部分皆为 2 个字符。

提示:

S 的长度可能很长,请按需分配大小。K 为正整数。
S 只包含字母数字(a-z,A-Z,0-9)以及破折号’-’
S 非空

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/license-key-formatting
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

从前向后写的,有点复杂,简单题写成这样确实不应该

class Solution {
    public String licenseKeyFormatting(String s, int k) {
        int l = s.length();
        StringBuilder sb = new StringBuilder();
        
        for(int i = 0; i < l; i++){
            char c = s.charAt(i);
            if(c == '-')
                continue;
            if(c >= 'a' && c <= 'z')
                c = (char)(c - ('a' - 'A'));
            sb.append(c);
        }
        int len = sb.length();
        int count = len / k;
        int first = len % k;
        s = sb.toString();

        sb = new StringBuilder();
        if(first != 0){
            sb.append(s.substring(0, first));
            if(sb.length() != s.length())
                sb.append("-");
        }
        int idx = 0;
        while(idx < count){
            sb.append(s.substring(first + idx * k, first + idx * k + k));
            if(idx != count - 1){
                sb.append("-");
            }
            idx++;
        }
        return sb.toString();

    }
}

从后向前写,
用toUpperCase来小写变大写

class Solution {
    public String licenseKeyFormatting(String s, int k) {
        int l = s.length();
        StringBuilder sb = new StringBuilder();
        int count = 0;
        for(int i = l - 1; i >= 0; i--){
            if(s.charAt(i) == '-')
                continue;
            if(count == k){
                sb.append("-");
                count = 0;
            }
            sb.append(s.charAt(i));
            count++;
        }
        return sb.reverse().toString().toUpperCase();
    }
}

以上是关于LeetCode 405. 数字转换为十六进制数(补码的问题) / 166. 分数到小数(模拟长除法) / 482. 密钥格式化的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 405 数字转换为十六进制数[异或 位运算] HERODING的LeetCode之路

405. 数字转换为十六进制数

405. 数字转换为十六进制数

405 Convert a Number to Hexadecimal 数字转换为十六进制数

LeetCode练习目录

《LeetCode之每日一题》:166.数字转换为十六进制数