【剑指Offer】第 30 天 分治算法(困难)

这篇博客探讨了两个编程问题:一是使用递归生成从1到最大n位数的序列,二是利用归并排序算法计算数组中的逆序对数量。对于前者,通过深度优先搜索实现,后者则通过归并排序的过程统计逆序对。这些技术涉及到大数处理和高效排序算法的应用。
摘要由CSDN通过智能技术生成
剑指 Offer 17. 打印从1到最大的n位数

输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。

题解
class Solution {
    int[] res;
    int nine = 0, count = 0, start, n;
    char[] num, loop = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
    public int[] printNumbers(int n) {
        this.n = n;
        res = new int[(int)Math.pow(10, n) - 1];
        num = new char[n];
        start = n - 1;
        dfs(0);
        return res;
    }
    void dfs(int x) {
        if(x == n) {
            String s = String.valueOf(num).substring(start);
            if(!s.equals("0")) res[count++] = Integer.parseInt(s);
            if(n - start == nine) start--;
            return;
        }
        for(char i : loop) {
            if(i == '9') nine++;
            num[x] = i;
            dfs(x + 1);
        }
        nine--;
    }
}

该题考点在于大数问题

class Solution {
    StringBuilder res;
    int nine = 0, count = 0, start, n;
    char[] num, loop = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
    public String printNumbers(int n) {
        this.n = n;
        res = new StringBuilder();
        num = new char[n];
        start = n - 1;
        dfs(0);
        res.deleteCharAt(res.length() - 1);
        return res.toString();
    }
    void dfs(int x) {
        if(x == n) {
            String s = String.valueOf(num).substring(start);
            if(!s.equals("0")) res.append(s + ",");
            if(n - start == nine) start--;
            return;
        }
        for(char i : loop) {
            if(i == '9') nine++;
            num[x] = i;
            dfs(x + 1);
        }
        nine--;
    }
}
剑指 Offer 51. 数组中的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

题解

利用归并排序。
分: 不断将数组从中点位置划分开(即二分法),将整个数组的排序问题转化为子数组的排序问题;
治: 划分到子数组长度为 1 时,开始向上合并,不断将 较短排序数组 合并为 较长排序数组,直至合并至原数组时完成排序;
在这里插入图片描述

class Solution {
    int sum = 0;
    public int reversePairs(int[] nums) {
        merge(nums, 0, nums.length - 1);
        return sum;
    }

    public void merge(int[] nums, int left, int right){
        int mid = left + ((right - left) >> 1);
        if(left < right){
            merge(nums, left, mid);
            merge(nums, mid + 1, right);
            mergeSort(nums, left, mid, right);
        }
    }

    public void mergeSort(int[] nums, int left, int mid, int right){
        int[] arr = new int[right - left + 1];  // 保留合并后的有序数组,覆盖掉原来的
        int idx = 0, i = left, j = mid + 1;
        while(i <= mid && j <= right){  // 遍历左右两边, 选择排序
            if(nums[i] <= nums[j]){  // 将左边较小数放入数组中
                arr[idx++] = nums[i++];
            }else{   // 右边更小
                // 统计逆序对的个数
                sum += mid - i + 1;  // 因为左右两边都是有序的,nums[i]>nums[j],则左边[i,mid]的值都大于nums[j],逆序对个数为 mid - i + 1
                arr[idx++] = nums[j++];  // 把较小数放入数组中
            }
        }
        //把左边剩余放入数组中
        while(i <= mid){
            arr[idx++] = nums[i++];
        }
        //把右边剩余放入数组中
        while(j <= right){
            arr[idx++] = nums[j++];
        }
        // 将排好序的合并数组覆盖nums数组中原来的范围
        for(int k = 0; k < arr.length; k++){
            nums[k + left] = arr[k];
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值