数字与位操作——168、670、233、357、400

168. Excel表列名称(简单)

给你一个整数 columnNumber ,返回它在 Excel 表中相对应的列名称。

例如:

A -> 1
B -> 2
C -> 3
...
Z -> 26
AA -> 27
AB -> 28 
...

示例 1:

输入:columnNumber = 1
输出:"A"

示例 2:

输入:columnNumber = 28
输出:"AB"

示例 3:

输入:columnNumber = 701
输出:"ZY"

示例 4:

输入:columnNumber = 2147483647
输出:"FXSHRXW"

提示:

  • 1 <= columnNumber <= 2^31 - 1

解法一、进制转换

相当于26进制中0-25变成了1-26,每轮开始之前减回去就好

class Solution {
public:
    string convertToTitle(int columnNumber) {
        string ans;
        while(columnNumber) {
            --columnNumber;
            int re = columnNumber % 26;
            ans.push_back('A' + re);
            columnNumber /= 26;
        }
        reverse(ans.begin(), ans.end());
        return ans;
    }
};

670. 最大交换(中等)

给定一个非负整数,你至多可以交换一次数字中的任意两位。返回你能得到的最大值。

示例 1 :

输入: 2736
输出: 7236
解释: 交换数字2和数字7。

示例 2 :

输入: 9973
输出: 9973
解释: 不需要交换。

注意:

  1. 给定数字的范围是 [0, 10^8]

解法一、暴力

i从前往后,j从后往前,寻找最大值。当最大值有复数个的时候,找最后一个。

第二个循环交换,时间复杂度O(n^2)

class Solution {
    public static int maximumSwap(int num) {
        String a = String.valueOf(num);
        StringBuffer sb = new StringBuffer();
        int left = -1,right = -1;
        for(int i = 0;i<a.length();i++){
            for(int j = a.length()-1;j > i;j--){
                if(a.charAt(j) > a.charAt(i) && (right == -1 || a.charAt(j) > a.charAt(right))){
                    left = i;
                    right = j;
                }
            }
            if((left ^ right) != 0)break;
        }
        for(int i = 0;i < a.length();i++){
            if(i == left){
                sb.append(a.charAt(right));
            } else if(i == right) {
                sb.append(a.charAt(left));
            }else{
                sb.append(a.charAt(i));
            }
        }
        return Integer.parseInt(sb.toString());
    }
}

解法二、枚举

其实就是全转一遍,一共有28种方法。感觉这个转字符组然后swap的方式很有意思

class Solution {
    public int maximumSwap(int num) {
        char[] charArray = String.valueOf(num).toCharArray();
        int n = charArray.length;
        int maxNum = num;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                swap(charArray, i, j);
                maxNum = Math.max(maxNum, Integer.parseInt(new String(charArray)));
                swap(charArray, i, j);
            }
        }
        return maxNum;
    }

    public void swap(char[] charArray, int i, int j) {
        char temp = charArray[i];
        charArray[i] = charArray[j];
        charArray[j] = temp;
    }
}

作者:力扣官方题解
链接:https://leetcode.cn/problems/maximum-swap/solutions/1818457/zui-da-jiao-huan-by-leetcode-solution-lnd5/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

解法三、贪心

和解法一差不多,但是只需要一次循环。从右往左遍历,用maxId记录最大值的下标,其中满足(如果最大值只有一个,记录最大值;如果最大值有多个,记录最靠右的)。之后再往左看,一旦有小的就更新,记录值对。

class Solution {
    public int maximumSwap(int num) {
        char[] charArray = String.valueOf(num).toCharArray();
        int n = charArray.length;
        int maxIdx = n - 1;
        int idx1 = -1, idx2 = -1;
        for (int i = n - 1; i >= 0; i--) {
            if (charArray[i] > charArray[maxIdx]) {
                maxIdx = i;
            } else if (charArray[i] < charArray[maxIdx]) {
                idx1 = i;
                idx2 = maxIdx;
            }
        }
        if (idx1 >= 0) {
            swap(charArray, idx1, idx2);
            return Integer.parseInt(new String(charArray));
        } else {
            return num;
        }
    }

    public void swap(char[] charArray, int i, int j) {
        char temp = charArray[i];
        charArray[i] = charArray[j];
        charArray[j] = temp;
    }
}

作者:力扣官方题解
链接:https://leetcode.cn/problems/maximum-swap/solutions/1818457/zui-da-jiao-huan-by-leetcode-solution-lnd5/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

233. 数字 1 的个数(困难)

给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。

示例 1:

输入:n = 13
输出:6

示例 2:

输入:n = 0
输出:0

提示:

  • 0 <= n <= 10^9

解法一、分类讨论

见↓

. - 力扣(LeetCode)

class Solution {
    public int countDigitOne(int n) {
        int m = 1;
        int ans = 0;
        while (n >= m){
            ans += (1+(n/m-1)/10)*m;
            if (n/m%10 == 1) ans = ans-m+1+n%m;
            m *= 10;
        }
        return ans;
    }
}

作者:嘉然
链接:https://leetcode.cn/problems/number-of-digit-one/solutions/937955/gong-shi-tui-dao-qiu-mei-yi-wei-shang-1d-5qvu/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


357. 统计各位数字都不同的数字个数

给你一个整数 n ,统计并返回各位数字都不同的数字 x 的个数,其中 0 <= x < 10n 。

示例 1:

输入:n = 2
输出:91
解释:答案应为除去 11、22、33、44、55、66、77、88、99 外,在 0 ≤ x < 100 范围内的所有数字。 

示例 2:

输入:n = 0
输出:1

提示:

  • 0 <= n <= 8

解法一、打表

抖个机灵(

解法二、排列组合

0位有一个(0),个位有十个(0-9),再往上,例如两位数,有9*9,三位数有9*9*8···

class Solution {
    public int countNumbersWithUniqueDigits(int n) {
        if (n == 0) {
            return 1;
        }
        if (n == 1) {
            return 10;
        }
        int res = 10, cur = 9;
        for (int i = 0; i < n - 1; i++) {
            cur *= 9 - i;
            res += cur;
        }
        return res;
    }
}

解法三、动态规划 

状态转移方程

dp[i]=dp[i-1]+(dp[i-1] - dp[i-2]) * (10 - (i-1))

class Solution {
    //对于1位数,全都可以,10。二位数,9*9.对于三位数,9*9*8 对于四位数 9*9*8*7
    public int countNumbersWithUniqueDigits(int n) {
        if(n ==0)return 1;
       int[] dp = new int[n+1];
       dp[0] = 1;
       dp[1] = 10;
       for(int i = 2;i <= n;i++){
           dp[i] = dp[i-1] + (dp[i-1]-dp[i-2])*(11-i);
       }
       return dp[n];
    }
}

400. 第 N 位数字

给你一个整数 n ,请你在无限的整数序列 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...] 中找出并返回第 n 位上的数字。

示例 1:

输入:n = 3
输出:3

示例 2:

输入:n = 11
输出:0
解释:第 11 位数字在序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ... 里是 0 ,它是 10 的一部分。

提示:

  • 1 <= n <= 2^31 - 1

解法一、模拟遍历 

这次真有点在乱命名了···i是位数,t是模拟那个9/90/900的每位数总值,pre是sum的上一格记录。while里确认区间,n所在的位置是[pre,sum],然后回退超出了一格的i,减pre把n在本位上多出来的留出。k计算余数(这里用了168题的算法,对于只有1——n而非0——n-1的数字,求余应该是(x-1) %t +1),num是最终的数字。用最后一行把num的个位变成所求位,返回10的余数(即个位)。

class Solution {
    //1-9 1 10-99 2/ 180 100-999 3/2700 1000-9999 4 36000
    //181 如果是50 对应的是2位数
    // 9 90 900 9000 90000 900000
    public static int findNthDigit(int n) {
        if(n < 10)return n;
        long pre = 0;
        long sum = 0,i = 1,t = 9;
        while(sum < n){
            pre = sum;
            sum += t * i;
            t*=10;
            i++;
        }
        i--;
        n -=pre;
        long k = (n-1) % i + 1;
        long num = t / 90 + (n-1) / i;//假如是59987的第四位,i = 5,num = 59987,k = 4;
        num /=(int) Math.pow(10,i-k);
        return (int)num % 10;

    }
}

 

题解区更明确的。(这个while的目的一致,写法不同)

public int findNthDigit(int n) {
        int digit = 1;   // n所在数字有几位数
        long start = 1;  // 每digit位的起始数字,1位数从2开始,2位数从10开始..
        long count = 9;  // 所有digit位数的数位数量, 所有1位数有9个位,所有的2位数90x2个位..
        // 1. 确定n所在的数字的位数digit
        while (n > count) { 
            n -= count; // n分别减去1位数 2位数 3位数的个数..
            digit += 1;
            start *= 10;
            count = 9 * start * digit; 
        } 
        long num = start + (n - 1) / digit; // 2. 确定n所在的数字num,n-1是从0开始计算偏移量 
        int x = (n - 1) % digit; // 3. 确定所求数位是 num 的第几位数字,从0算起
        // 从num中分割出第x位,如 num = 1234, x = 1, 需割掉2位数,再求模可得2
        int times = digit - (x + 1);
        for (int i = 0; i < times; i++) num /= 10; 
        return (int)(num % 10);
    }

 


碎碎念

  • 168学到了1-n位进制转换,670学会了单向遍历,233挺难的考察分类讨论,357靠自己写出了动态规划,400要考虑的太碎了,很多边界情况。。。。
  • 明天开始出门!路上两天,休息一周多点,再之后回学校了~
  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值