剑指offer 31-35

31.整数中1出现的次数

求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?
为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。
ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。

这个题在前面的一篇文章里面有写到过更普及的K出现的次数,关键点有两个:
1.统计的是k出现的次数,而不是包含k的数字的个数,例如统计1出现的次数,那么11出现了2次,因此可以直接采用对每一位进行分析出现K的次数,然后把所有的结果相加即可;
2.对每一位进行分析的时候要分大于K,小于K,等于K三种情况来讨论,以n=31245,k=2为例
1这一位,a=3,b=245,最大的是22999,因此可能结果为2000-2999,12000-12999,22000-22999,共a1000=3000个;
2这一位,a=31,b=45,前面的从0-30都没问题,可以取31
100种,但当前面为31时,只能取31200-31245共46种,故共计3100+46=3146种;
4这一位,与小于K大致相同,但更多一些,a=312,b=5,那么从200-299,1200-1299,2200-2299....31200-31299均可,共(a+1)*10=3130种
按这个思路,把每一位出现K的可能加起来就是最后K出现的总次数
代码如下

public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        if(n<0){
            return 0;
        }
        //用copyn来设置循环中止的条件,每次循环都使得copyn = copyn/10;
        int copyn = n;
        //用cnt来记录当前的位置,如个位就是10的0次方位
        int cnt = 0;
        //用sum来记录最后的结果
        int sum = 0;
        while(copyn!=0){
            copyn /= 10;
            //对于a,mid,b以312的2这一位为例
            //a = 312/10=31
            int a = n/(int)Math.pow(10,cnt+1);
            //mid = 312%10/1=2
            int mid = n%(int)Math.pow(10,cnt+1)/(int)Math.pow(10,cnt);
            //b = 312%10%1 = 0
            int b = n%(int)Math.pow(10,cnt+1)%(int)Math.pow(10,cnt);
            if(mid<1){
                sum += a*(int)Math.pow(10,cnt);
            }
            if(mid==1){
                sum += a*(int)Math.pow(10,cnt)+b+1;
            }
            if(mid>1){
                sum += (a+1)*(int)Math.pow(10,cnt);
            }
            cnt++;
        }
        return sum;
    }
}

32.把数组排成最小的数

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

这个题的思路就是比较给定数组中的任意两个数,例如3和32,由于323小于332,所以32排在3前面,再比较3和321,由于3213小于3321,所以321排在3前面,最后比较32和321,由于32132小于32321,所以321排在32前面,故排序大小顺序为[321,32,3]最后拼接起来得到的就是最后的结果,即最小的数321323,由于考虑整数可能越界,而且整数拼接起来可能比较麻烦,因此这里采用字符串进行操作,且在对字符串进行排序的时候用到lamda表达式;
代码如下

import java.util.Arrays;

public class Solution {
    public String PrintMinNumber(int [] numbers) {
        if(numbers==null||numbers.length==0){
            return "";
        }
        //用一个字符串数组存储所有数字转化成的字符串
        String[] nums = new String[numbers.length];
        for(int i=0;i<numbers.length;i++){
            //+""代表转化为字符串
            nums[i] = numbers[i]+"";
        }
        //这里使用Arrays.sort进行排序时用来了lambda表达式
        //即将s1+s2和s2+s1进行升序排列
        Arrays.sort(nums,(s1,s2)->(s1+s2).compareTo(s2+s1));
        //最后将全部结果拼接起来,得到最后的输出
        String res = "";
        for(String s:nums){
            res += s;
        }
        return res;
    }
}

33.丑数

把只包含质因子2、3和5的数称作丑数(Ugly Number)。
例如6、8都是丑数,但14不是,因为它包含质因子7。 
习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。

该题的核心思想就是新的丑数依然是由老的丑数构成的,我们需要建立一个新的数组不断存储新加入的丑数,然后用三个指针指向2,3,5所在的该丑数数组的位置,然后用2,3,5指针所指的位置的丑数分别去乘以2,3,5选择最小的,然后使得构成这个新丑数的这个数的指针向后移动一位,如果相同,则同时移动一位,例如23和32,则index_2和index_3同时后移一位;
代码如下

public class Solution {
    public int GetUglyNumber_Solution(int index) {
        if(index<=0){
            return 0;
        }
        //用3个指针指向2,3,5指向的丑数数组中的位置
        int index_2 = 0,index_3 = 0,index_5 = 0;
        int[] ugly_nums = new int[index];
        ugly_nums[0] = 1;
        for(int i=1;i<index;i++){
            //选择最小的最为新丑数
            ugly_nums[i] = Math.min(Math.min(ugly_nums[index_2]*2,ugly_nums[index_3]*3),Math.min(ugly_nums[index_2]*2,ugly_nums[index_5]*5));
            int t2 = ugly_nums[i]/2,t3 = ugly_nums[i]/3,t5 = ugly_nums[i]/5;
            //判断哪个指针需要后移
            if(t2==ugly_nums[index_2]){
                index_2++;
            }
            if(t3==ugly_nums[index_3]){
                index_3++;
            }
            if(t5==ugly_nums[index_5]){
                index_5++;
            }
        }
        //返回丑数数组总最后一个元素为第index个丑数
        return ugly_nums[index-1];
        
    }
}

34.第一个只出现一次的字符

在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,
并返回它的位置, 如果没有则返回 -1(需要区分大小写).

本题最直观的就是用hashmap来统计字符出现的次数,也可以建立一个整数数组,通过将字符转化为ASCII码,然后整数数组对应位置上加一,然后再遍历原字符串或转化为的字符数组,返回第一个在整数数数组中为1的值
流程为

1.将String转为一个字符数组c,如“abc”--->{‘a’,‘b’,‘c’};
2.建立一个容量为256的整型数组arr,每一位的下标对应着ASCII值
3.遍历字符数组c,例如遍历到了'a',那么就在arr[(int)'a']的位置对应的值+1
4.最后再遍历一遍c,找到第一个在arr中的值为1的那个字符,例如遍历到了'a'且arr[int(a)]==1,那么就返回这个'a'所在的位置即可

代码如下

public class Solution {
    public int FirstNotRepeatingChar(String str) {
        if(str.length()==0||str==null){
            return -1;
        }
        //将String转化为字符数组
        char[] arr = str.toCharArray();
        //建立一个整型数组,作用类似于hashmap,其中res的下标i--->字符的ASCII值
        int[] res = new int[256];
        //遍历一遍arr,使res中对应位置的hash值加一
        for(char c:arr){
            res[(int)c]++;
        }
        //找到第一个hash值为1的字符,输出它的位置
        for(int i=0;i<arr.length;i++){
            if(res[(int)arr[i]]==1){
                return i;
            }
        }
        return -1;
    }
}

35.数组中的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。
输入一个数组,求出这个数组中的逆序对的总数P。
并将P对1000000007取模的结果输出。 即输出P%1000000007

该题的思路就是使用归并排序,就是通过递归将数组拆分为最小的依次相邻的两个单元,然后用两个指针分别指向左右数组的头部,若左边指针指向的元素大于右边指针指向的元素,那么在左边数组中指针指向的元素后面的数全部都大于右边数组中指针指向的元素,这些全部都构成逆序对,归并排序中我们还需要一个辅助数组去存储顺序大小排列好的元素,这才叫做归并,最后统计所有这些逆序对,就是我们需要的结果,注意不要超出界限。
代码如下

public class Solution {
    //防止cnt溢出所以用一个long类型
    private long cnt = 0;
    private int[] tmp;  

    public int InversePairs(int[] nums) {
        tmp = new int[nums.length];
        mergeSort(nums, 0, nums.length - 1);
        return (int)(cnt % 1000000007);
    }

    private void mergeSort(int[] nums, int l, int h) {
        if (h - l < 1)
            return;
        //求出中值分界线
        int m = l + (h - l) / 2;
        //归并排序,这里采用递归,可以想象,第一次递归到最结尾的时候
        //一个长度为2的数组,对左右继续递归都只有一个元素了,因此直接return
        //然后再执行merge这个函数,此时长度为2,相当于最小的一个子单元的归并排序
        mergeSort(nums, l, m);
        mergeSort(nums, m + 1, h);
        merge(nums, l, m, h);
    }

    private void merge(int[] nums, int l, int m, int h) {
        //指针i指向左边数组的头部,j指向右边数组的头部,k指向辅助数组的当前元素位置,
        //我们要将两个数组中的元素依次比较大小,然后存入辅助数组中
        //再将原数组中从l到h的所有元素替换为辅助数组中从l到h的所有元素
        int i = l, j = m + 1, k = l;
        //这里不用&&而用||是因为可能左右中某个数组的指针走到头了
        //那么此时我们需要做的就是把另外一个数组的所有元素替换到辅助数组中
        //因此while中是并列的四个条件
        while (i <= m || j <= h) {
            if (i > m)
                tmp[k] = nums[j++];
            else if (j > h)
                tmp[k] = nums[i++];
            else if (nums[i] <= nums[j])
                tmp[k] = nums[i++];
            else {
                tmp[k] = nums[j++];
                //这里是说,当左边的某个元素大于右边的元素的时候
                //那么左边数组中这个元素后的所有元素都大于当前右边指向的这个元素
                //统计左边数组中这个元素后的所有元素的个数就是我们要求的逆序对
                this.cnt += m - i + 1;
            }
            k++;
        }
        for (k = l; k <= h; k++)
            nums[k] = tmp[k];
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,339评论 0 2
  • 选择题部分 1.()部门负责日常监督检查工作,安全巡视的同时进行消防检查,推动消防安全制度的贯彻落实。 A: 消防...
    skystarwuwei阅读 15,140评论 0 3
  • 专业考题类型管理运行工作负责人一般作业考题内容选项A选项B选项C选项D选项E选项F正确答案 变电单选GYSZ本规程...
    小白兔去钓鱼阅读 8,981评论 0 13
  • 咳嗽感冒爱上了你 没日没夜地纠缠不清 人家高高兴兴跨年迎元旦 而你每天发高烧打针忙 为娘心疼啊 一天半夜醒来看你脸...
    爱上一叶浮萍阅读 382评论 14 24
  • 分享: 企业做大,必须把人搞透。 企业无法做大的核心原因在于:无法把员工留在身边。人比钱值钱,有人就会有钱。 80...
    创新你的创造阅读 66评论 0 1