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

题目描述

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

示例1

输入:

1
13

返回值:

1
6

思路:

将整数按位数分成3段,初始化4个值当前位cur,高位high,低位low,位数d

以2134为例若当前位是个位,high=213,low=0,cur=4,d=1。个位可以有213-0+1个1。cur=0时有213个1

若当前位是十位,high=21,low=4,cur=3 ,d=10。十位可以有214-0+1个1。cur=0时有210个1

若当前位是百位,high=2,low=34,cur=1,d=100。百位可以有234-0+1个1。cur=0时有200个1

可以推出cur=0时,1的个数=high*d

cur=1时,1的个数=high*d+low+1

cur>1时,1的个数=(high+1)*d

每个位上出现的1的次数相加即为结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
int NumberOf1Between1AndN_Solution(int n) {
int res=0;
int high=n/10,low=0,cur=n%10,d=1;
while(high>0||cur>0){
if(cur==0)res+=high*d;
if(cur==1)res+=high*d+low+1;
if(cur>1)res+=(high+1)*d;
low+=cur*d;
cur=high%10;
d*=10;
high/=10;
}
return res;
}
};

32 把数组排成最小的数

题目描述

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

思路:

将数组中的元素转化位string类型两两相加然后比较,“3”和“321”比较,“3321”>“3213”所以“321”排在“3”前面。按上述规则比较然后排序,最后按顺序相加得出的值就是能拼接出的数中的最小值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public:
string PrintMinNumber(vector<int> numbers) {
if(numbers.size()==0)return "";
string res;
sort(numbers.begin(),numbers.end(),[](int &a,int &b){
string A=to_string(a);
string B=to_string(b);
A+=B;
B+=A;
return A<B;
});
for(auto n:numbers){
res+=to_string(n);
}
return res;
}
};

33 丑数

题目描述

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

思路:

维护一个数组,数组中的每个数都只包含2,3,5因子

假设有三个队列分别是2,3,5的倍数,每次比较三个栈中的最小值添加到数组中

arr:1

2

3

5

arr:1,2

4

3,6

5,10

arr:1,2,3

4,6

6,9

5,10,15

arr:1,2,3,4,5

6,8,10

6,9,12,15

10,15,20,25

当队首元素相同时同时出队。这样就得出了一个升序的丑数数组。

实际上我们没必要维护三个队列,只需要记录三个指针显示到了哪那一步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
int GetUglyNumber_Solution(int index) {
if(index<7)return index;
int p2=0,p3=0,p5=0;
vector<int> res;
res.push_back(1);
for(int i=1;i<index;i++){
int input=min(res[p2]*2,min(res[p3]*3,res[p5]*5));
if(res[p2]*2==input)p2++;
if(res[p3]*3==input)p3++;
if(res[p5]*5==input)p5++;
res.push_back(input);
}
return res.back();
}
};

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

题目描述

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

示例1

输入:

1
"google"

返回值:

1
4

思路:

用map记录每个字符出现的次数,再从头遍历字符串找到第一个出现1次的字符下标

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public:
int FirstNotRepeatingChar(string str) {
map<char,int> m;
for(int i=0;i<str.size();i++){
m[str[i]]++;
}
for(int i=0;i<str.size();i++){
if(m[str[i]]==1)return i;
}
return -1;
}
};

35 数组中的逆序对

题目描述

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

示例1

输入:

1
[1,2,3,4,5,6,7,0]

返回值:

1
7

思路:

利用归并排序的思想,归并排序是将数组分成若干子数组,每个子数组都是有序的,最后将子数组合并。

在合并之前我们需要比较两个数组中每个数的大小然后按顺序放入一个临时数组中,在比较时若前面的数组中某个数大于后面数组中的数,那么至少前面数组中比当前数大的数都可以和后面的数组成逆序对

如果区间有序,比如[3,4] 和 [1,2]
如果3 > 1, 显然3后面的所有数都是大于1, 这里为 4 > 1,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Solution {
public:
int InversePairs(vector<int> data) {
int left=0,right=data.size();
vector<int> tmp(data.size());
int res=0;
mergeSort(tmp,0,data.size()-1,data,res);
return res;
}
//临时数组的参数最好设为引用,不然会超时
void mergeSort(vector<int> &tmp,int left,int right,vector<int>& data,int &res){
if(left>=right)return;
int mid=left+(right-left)/2;
mergeSort(tmp, left,mid,data,res);
mergeSort(tmp, mid+1, right,data,res);
int k=0,i=left,j=mid+1;
while(i<=mid&&j<=right){
if(data[i]>data[j]){
tmp[k++]=data[j++];
res+=mid-i+1;
res%=1000000007;
}
else{
tmp[k++]=data[i++];
}
}
while(i<=mid)tmp[k++]=data[i++];
while(j<=right)tmp[k++]=data[j++];
for(int n=left,m=0;n<=right;n++){
data[n]=tmp[m++];
}
}
};