剑指Offer30

剑指Offer第三十天

分治算法(困难)
在这里插入图片描述

题1:打印从1到最大的n位数

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

在这里插入图片描述

力扣中该题的返回类型是int[],这就将数据的范围固定为32位内了

但是剑指Offer原题中,返回类型为String,主要用于考察大数问题

//leetcode返回int[]解法
class Solution{
    public int[] printNumbers(int n){
        int end = (int)Math.pow(10,n) - 1;
        int[] res = new int[end];
        for(int i = 0; i < end; i++){
            res[i] = i+1;
        }
        return res;
    }
}

大数问题

当n较大时,会出现int32越界的问题,超过32位范围的数字无法正常存取值

  1. 无论short,int,long都有取值范围的限定,所以在大数问题出现时,大数的表示应用串应该为String

  2. String类型的进位效率较低,9999->10000需要进位4次,而其实该题,生成的列表其实是n位0-9的全排序,所以其实可以避开进位操作,进行递归遍历生成String列表

  3. 递归,先固定高位,然后往低位固定

//返回String时,大数问题的解法
class Solution{
    //定义返回的结果字符串
    StringBuilder sb;
    //定义全排序的字符数组
    char[] loop = {'0','1','2','3','4','5','6','7','8','9'};
    char[] num;
    public String printNumbers(int n){
        num = new char[n];
        for(int i = 1; i <= n; i++){
            //当i=1,生成长度为1的字符串,索引位置在最左端为0
            //..
			dfs(0,i);
        }
        return sb.deleteCharAt(sb.length()-1).toString();
    }
    //生成长度为n的字符串,当前位置为index
    public void dfs(int index, int n){
        if(index == n){
            sb.append(new String(num) + ",");
            return ;
        }
        int start;
        //高位的去零操作,第一位不能为0,不能写成0213,0321这种形式
        if(index == 0){
            start = 1;
        }else{
            start = 0;
        }
        //开始填数字,假设n=2
       	for(int i = start; i < 10; i++){
            //第一次填充num[0]=loop[i],填充十位上的值
            //第二次填充num[1]=loop[i],填充个位上的值
            num[index] = loop[i];
            //递归填充下一位
            dfs(index+1, n);
        }
    }
}
题2:数组中的逆序对

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

在这里插入图片描述

参考题解: https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/solution/jian-zhi-offer-51-shu-zu-zhong-de-ni-xu-pvn2h/

归并排序和逆序列密切相关

基本上一谈到逆序列,就该想到归并排序,归并过程就能对逆序列个数进行统计

详情可参考题解

class Solution {
    //要返回的值定义成全局变量,此处是统计逆序列的个数
    int count;
    public int reversePairs(int[] nums) {
        this.count = 0;
        merge(nums, 0, nums.length-1);
        return count;
    }

    //套模板
    private 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);
        }
    }
    private void mergeSort(int[] nums, int left, int mid, int right){
        //定义合并的临时数组
        int[] temp = new int[right-left+1];
        int index = 0;
        int l_index = left, r_index = mid+1;
        while(l_index <= mid && r_index <= right){
            if(nums[l_index] <= nums[r_index]){
                //如果不是逆序列,就数组排序
                temp[index++] = nums[l_index++]; 
            }else{
                //如果存在逆序对,则进行统计并排序
                count += (mid - l_index + 1);
                temp[index++] = nums[r_index++];
            }
        }
        //如果两边有任意一边到头了,全部装入临时数组中
        while(l_index <= mid){
            temp[index++] = nums[l_index++];
        }
        while(r_index <= right){
            temp[index++] = nums[r_index++];
        }

        //用新数组覆盖原数组
        for(int i = 0; i < temp.length; i++){
            nums[i+left] = temp[i];
        }
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值