剑指offer之每日6题 ----- 第六天

原题链接

1.整数中1出现的次数

题目描述

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

思路

最简单直接的方法就是蛮力法,这种方法的时间复杂度是o(n^2),也可以使用更高效的方法,可以去分析每个位置上出现1的次数,然后将各个位置出现1的次数累计相加,就可以得到这个整数出现的次数,假设现在要求n = abcde,求出c这个位置上出现1的次数

  • if c == 0, 比如12013,那么在c这个位置上出现1的可能性就是100 ~ 199,1100 ~1199,2100 ~ 2199 …11100 ~ 11199 一共是1200个,也就是由最高位置决定的,也就是说ab从0到ab-1变化,cde从100 ~ 199变化,c这一个位置都是有可能会出现1的,那么次数也就是(ab-1-0+1) * (199 - 100 +1) = ab * 100
  • if c ==1, 比如12113,那么c这个位置出现1,也就是和上面一样,同时还多了12000 ~ 12113,这114个,也就是当ab == ab,c =1是,cde就从000到cde变化,所以次数是 ab * 100 + cde +1;
  • if c > 2,比如12213,和 c == 0一样,但是还是要加上 12100 ~ 12199,也就是(ab +1) *100;
    这种解法要比蛮力法快很多,循环次数和位数相同,一个数字n有o(logn)位,那么时间复杂度就是o(logn).
 public int numberOf1(int n) {
        int result = 0;
        int index = 1; //当前位
        int current = 0;//当前位置
        int after = 0; //低位
        int before = 0; //高位

        while ((n / index) != 0) {
            current = (n / index) % 10; 
            before = n / (index * 10);
            after = n - (n / index) * index;

            if (current == 0) result += before * index;
            else if (current == 1) result += before * index + after + 1;
            else result += (before+1) * index;

            index = index *10; //向前移一位
        }

        return result;
    }

2.把数组排成最小的数

题目描述

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

思路

将整数,转换成字符串数组,然后将字符串数组排好序,最后将排好序的字符串数组拼接出来,怎么制定大小规则,是去比较拼接好的字符串的大小,并不是直接比较单个字符串,因为 3 < 32 但是 332 > 323。
当然最简单直接的就是对数组的每个数字进行全排列,然后再进行比大小,这样效率比较低

 public String printMinNumber(int[] numbers) {
        List<Integer> list = new ArrayList<>();
        for (int number : numbers) {
            list.add(number);
        }
        Collections.sort(list, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                String str1 = o1 + "" + o2;
                String str2 = o2 + "" + o1;
                return str1.compareTo(str2);
            }
        });
        StringBuilder stringBuilder=new StringBuilder();
        for (Integer integer : list) {
            stringBuilder.append(integer);
        }
        return stringBuilder.toString();
    }

3. 丑数

题目描述

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

思路

基本思想就是用已知的丑数,去生成下一个丑数,因为丑数的定义是只包含2、3、5,那么下一个丑数一定是*2,*3,*5之中最小的那一定。

 public int getUglyNumber_Solution(int index) {
        if (index == 0)
            return 0;
        List<Integer> result = new ArrayList<>();
        result.add(1);
        int i = 0, j = 0, k = 0;
        
        while (result.size() < index) {
            int m1 = result.get(i) * 2;
            int m2 = result.get(j) * 3;
            int m3 = result.get(k) * 5;
            int min = Math.min(m1, Math.min(m2, m3));
            result.add(min);
            if (min == m1) i++;
            if (min == m2) j++;
            if (min == m3) k++;
        }
        return result.get(result.size() - 1);
    }

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

题目描述

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

思路

用map将对应的字符和次数映射起来

 public int firstNotRepeatingChar(String str) {
        Map<Character,Integer> map = new HashMap<>();
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (map.containsKey(c)){
                int time =map.get(c);
                time++;
                map.put(c,time);
            }else{
                map.put(c,1);
            }
        }
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (map.get(c) == 1){
                return i;
            }
        }
        return -1;
    }

5.数组中的逆序对

题目描述

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

思路

1.先把数组分隔成子数组
2.统计出子数组中的逆序对
3.统计出相邻的两个数组之间的逆序对的数目
实际就是归并排序

 public int inversePairs(int[] array) {
        if (array == null && array.length == 0)
            return 0;
        int[] copy = new int[array.length];
        for (int i = 0; i < array.length; i++) {
            copy[i] = array[i];
        }

        int cout = inversePairsCore(array, copy, 0, array.length - 1);
        return cout;
    }
private int inversePairsCore(int[] array,int[] copy,int low,int high){
        if(low==high)
            return 0;
        int mid = (low+high)>>1;
        int leftCount = inversePairsCore(array,copy,low,mid)%1000000007;
        int rightCount = inversePairsCore(array,copy,mid+1,high)%1000000007;
        int count = 0;int i=mid; int j=high; int locCopy = high;
        while(i>=low&&j>mid)
        {
            if(array[i]>array[j])
            {
                count += j-mid;
                copy[locCopy--] = array[i--];
                if(count>=1000000007)//数值过大求余
                    count%=1000000007;
            }else{
                copy[locCopy--] = array[j--];
            }
        }
        for(;i>=low;i--){
            copy[locCopy--]=array[i];
        }
        for(;j>mid;j--){
            copy[locCopy--]=array[j];
        }
        for(int s=low;s<=high;s++){
            array[s] = copy[s];
        }
        return (leftCount+rightCount+count)%1000000007;
    }

6.两个链表的第一个公共节点

题目描述

输入两个链表,找出它们的第一个公共结点。

思路

蛮力法,在第一个链表上遍历节点,每遍历一个节点,就在第二个链表上遍历所有节点,判断节点是否相等,相等就是公共节点,如果第一个链表节点是n个,第二个链表节点是m个,这样的做法的时间复杂度是O(mn)
还有一种解法,就是先遍历两个链表,得到两个链表的长度差,让较长的那个链表先走长度差个节点,然后长度短的那个链表,从头开始遍历,直到找到第一个公共节点,这样的时间复杂度是o(m+n)

 public ListNode findFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        //获取链表长度
        int length1=getListLength(pHead1);
        int length2=getListLength(pHead2);
        int diff=length1-length2;
        
        ListNode nodeLong = pHead1;
        ListNode nodeShort =pHead2;
        if (length2 > length1){
            nodeLong = pHead2;
            nodeShort = pHead1;
            diff = length2 -length1;
        }
        //让长链表先走几步
        for (int i = 0; i < diff; i++) {
            nodeLong = nodeLong.next;
        }
        while (nodeLong != null && nodeShort != null && nodeLong != nodeShort){
            nodeLong = nodeLong.next;
            nodeShort=nodeShort.next;
        }
        ListNode commonNode = nodeLong;
        return commonNode;
    }

猜你喜欢

转载自blog.csdn.net/YuQing_Cat/article/details/86481023