剑指offer.C++.code31-35

31. 整数中1出现的次数(从1到n整数中1出现的次数)

  • 求出1-13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1-13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数。
// Soluntion:(1)计算1-n每个数字,余10后1的个数总和——效率O(n*logn);
// (2)计算每一位上出现1的次数——效率O(logn).
class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
        if (n < 0) {
            return 0;
        }
        int count = 0;
        int pos = 1;    //个位,循环*10,逐位相加
        while (n/pos != 0) {
            int high = n/(pos*10);    //高位
            int cur = (n/pos)%10;     //当前位
            int low = n%pos;          //低位
            if (cur == 0) {
                count += high*pos;
            } else if (cur == 1) {
                count += high*pos + low+1;
            } else {
                count += (high+1)*pos;
            }
            pos *= 10;
        }
        return count;
    }
};

Solution(2):
设N = abcde ,其中abcde分别为十进制中各位上的数字。
如果要计算百位上1出现的次数,它要受到3方面的影响:百位上的数字,百位以下(低位)的数字,百位以上(高位)的数字。
① 如果百位上数字为0,百位上可能出现1的次数由更高位决定。比如:12013,则可以知道百位出现1的情况可能是:100-199,1100-1199,2100-2199,,...,11100-11199,一共1200个。可以看出是由更高位数字(12)决定,并且等于更高位数字(12)乘以当前位数(100)。
② 如果百位上数字为1,百位上可能出现1的次数不仅受更高位影响还受低位影响。比如:12113,则可以知道百位受高位影响出现的情况是:100-199,1100-1199,2100-2199,,....,11100-11199,一共1200个,等于更高位数字(12)乘以 当前位数(100)。但同时它还受低位影响,百位出现1的情况是:12100-12113,一共114个,等于低位数字(113)+1。
③ 如果百位上数字大于1(2~9),则百位上出现1的情况仅由更高位决定,比如12213,则百位出现1的情况是:100-199,1100-1199,2100-2199,...,11100-11199,12100-12199,一共有1300个,并且等于更高位数字+1(12+1)乘以当前位数(100)。

32. 把数组排成最小的数【O(nlogn)】

  • 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
// Solution:(1)数字全排列拼接(类似于“字符串的排列”题),求最大值;
//          (2)拼接n和m,如果nm numbers) {
        int len = numbers.size();
        if (len == 0)
            return "";
        sort(numbers.begin(), numbers.end(), cmp);
        string res;
        for (int i = 0; i < len; i ++) {
            res += to_string(numbers[i]);
        }
        return res;
    }
    static bool cmp (int a, int b) {
        string str1 = to_string(a) + to_string(b);
        string str2 = to_string(b) + to_string(a);
        return str1 < str2;
    }
};

33. 丑数【空间换时间】

  • 把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
// Solution:
// (1)逐个数字循环除以2,3,5,最后得到1,则为丑数,时间效率极差;
// (2)用数组从小到大保存丑数,逐个计算。
class Solution {
public:
    int GetUglyNumber_Solution(int index) {
        if (index <= 0) {
            return 0;
        }
        vector uglyNumbers(index);
        uglyNumbers[0] = 1;
        int uglyIndex = 1;    // 从第二个数开始
        int p2 = 0, p3 = 0, p5 = 0;
        while (uglyIndex < index) {
            uglyNumbers[uglyIndex] = min(uglyNumbers[p2]*2,
                                        min(uglyNumbers[p3]*3,uglyNumbers[p5]*5));
            while (uglyNumbers[p2]*2 <= uglyNumbers[uglyIndex])
                p2 ++;
            while (uglyNumbers[p3]*3 <= uglyNumbers[uglyIndex])
                p3 ++;
            while (uglyNumbers[p5]*5 <= uglyNumbers[uglyIndex])
                p5 ++;
            uglyIndex ++;
        }
        return uglyNumbers[index-1];
    }
};
剑指offer.C++.code31-35_第1张图片
丑数

34. 第一个只出现一次的字符【哈希表,空间换时间】

  • 在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置
// Solution:(1)逐个统计每个字符出现的次数——时间效率O(n);
//          (2)使用哈希表的方法存每个字符出现的次数,扫描两遍原字符——时间O(n).
class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        if (str.size() == 0)
            return -1;
        int countChar[256] = {0}; //ASCII码8位,可表示256个字符
        for (int i = 0; i < str.size(); i ++) {
            countChar[str[i]] ++;
        }
        for (int i = 0; i < str.size(); i ++) {
            if (countChar[str[i]] == 1) 
                return i;
        }
        return -1;
    }
};
剑指offer.C++.code31-35_第2张图片
扩展

35. 数组中的逆序对【需要注意递归调用参数位置,并且需要使用long】

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

输入描述:
题目保证输入的数组中没有的相同的数字
数据范围:
对于%50的数据,size<=10^4
对于%75的数据,size<=10^5
对于%100的数据,size<=2*10^5
示例1:
输入:1,2,3,4,5,6,7,0
输出:7

// Solution:(1)顺序扫描,逐个比较后续数字——时间复杂度O(n^2);
//          (2)归并排序,过程中计算逆序对的数量——时间O(n*logn),空间O(n).
class Solution {
public:
    int InversePairs(vector data) {
        if (data.size() == 0)
            return 0;
        vector copy(data); // 复制data数组
        return InversePairsCore(data, copy, 0, data.size()-1);
    }
    
    long InversePairsCore(vector& data, vector& copy, long start, long end){
        if (start == end) {
            copy[start] = data[start];
            return 0;
        }
        long len = (end - start)/2;
        long left_count = InversePairsCore(copy, data, start, start+len); // 传地址,返回第二个参数是经过排序的数组
        long right_count = InversePairsCore(copy, data, start+len+1, end);// copy在前,是因为后续需要data数组是经过排序的
        // 数组左侧、右侧最后一个数字下标
        long left_index = start + len;
        long right_index = end;
        long copy_index = end;
        long count = 0;
        
        while (left_index >= start && right_index >= start+len+1) {
            if (data[left_index] > data[right_index]) {
                count += (right_index - (start+len));
                copy[copy_index --] = data[left_index --];
            } else {
                copy[copy_index --] = data[right_index --];
            }
        }
        while (left_index >= start) {
            copy[copy_index --] = data[left_index --];
        }
        while (right_index >= start + len + 1) {
            copy[copy_index --] = data[right_index -- ];
        }
        return (left_count% 1000000007 + right_count% 1000000007 + count% 1000000007) % 1000000007;
    }
};

你可能感兴趣的:(剑指offer.C++.code31-35)