CUFMH2Blog

LeetCode - 剑指Offer - Part5

Word count: 2.8k Reading time: 14 min
2020-09-09 calculating Share

桃李春风一杯酒,江湖夜雨十年灯。

剑指 Offer 41. 数据流中的中位数**

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

例如,

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
示例 1:

输入:
[“MedianFinder”,”addNum”,”addNum”,”findMedian”,”addNum”,”findMedian”]
[[],[1],[2],[],[3],[]]
输出:[null,null,null,1.50000,null,2.00000]
示例 2:

输入:
[“MedianFinder”,”addNum”,”findMedian”,”addNum”,”findMedian”]
[[],[2],[],[3],[]]
输出:[null,null,2.00000,null,2.50000]

限制:

最多会对 addNum、findMedia进行 50000 次调用。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class MedianFinder {
public:
// 1.插入排序
// 本想利用lower_bound,可以实现二分查找的插入
// 但insert时后面的数还是要移动,所以干脆直接从后往前,边比较边移动
// 不幸的是,超出了时间限制...
vector<int> nums;
MedianFinder() { }
void addNum(int num) {
nums.push_back(num);
for(int i=nums.size()-2;i>=0;i--) {
if(nums[i]>nums[i+1]) {
swap(nums[i],nums[i+1]);
}
}
}

double findMedian() {
int i=nums.size();
if(i%2) return nums[i/2];
else return (nums[i/2]+nums[i/2-1])/2.0;
}
};

// 2.二分查找
// leetcode上随便找了段二分查找段代码
// 链接:https://leetcode-cn.com/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof/solution/you-xian-dui-lie-by-z1m/
// 提交显示通过了所有样例,看来内部insert还是更好的
class MedianFinder2 {
vector<int> store;
public:
void addNum(int num) {
if (store.empty())
store.push_back(num);
else
store.insert(lower_bound(store.begin(), store.end(), num), num);
}
double findMedian()
{
int n = store.size();
return n & 1 ? store[n / 2] : (store[n / 2 - 1] + store[n / 2]) * 0.5;
}
};


class MedianFinder3 {
public:
// 3.堆
// 小根堆和大根堆分别存后一半和前一半
// 99.58% 78.62%
priority_queue<int, vector<int>> max; //大根堆
priority_queue<int, vector<int>, greater<int>> min; //小根堆
MedianFinder() {}

void addNum(int num) {
// 添加
if(min.empty() || num>=min.top()) min.push(num);
else max.push(num);
// 调整
if(min.size()==max.size()+2) {
max.push(min.top());
min.pop();
} else if(max.size()==min.size()+2) {
min.push(max.top());
max.pop();
}
}

double findMedian() {
if(max.size()==min.size()) return (max.top()+min.top())/2.0;
else if(max.size()>min.size()) return max.top();
else return min.top();
}
};

剑指 Offer 42. 连续子数组的最大和*

输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。

要求时间复杂度为O(n)。

示例1:

输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

提示:

1 <= arr.length <= 10^5
-100 <= arr[i] <= 100

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lian-xu-zi-shu-zu-de-zui-da-he-lcof

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
int maxSubArray(vector<int> &nums) {
// dp
// 77.45% 68.59%
int len=nums.size();
int ans=nums[0];
for (int i=1; i<len; i++) {
if (nums[i-1] > 0) {
nums[i] += nums[i-1];
}
ans = max(nums[i], ans);
}
return ans;
}
};

剑指 Offer 43. 1~n整数中1出现的次数*

输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。

例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。

示例 1:

输入:n = 12
输出:5
示例 2:

输入:n = 13
输出:6

限制:

1 <= n < 2^31

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
int countDigitOne(int n) {
// 100.00% 36.38%
// 学习题解方法:逐位求次数
// (2^31那么大还用int传入吗?)
long ans=0;
long i=1;
while(n/i) {
long high=(n/i)/10;
long cur=(n/i)%10;
if(!cur) {
ans+=high*i;
} else if(cur==1) {
ans += high*i + n-(n/i)*i+1;
} else {
ans += high*i+i;
}
i*=10;
}
return ans;
}
};

剑指 Offer 44. 数字序列中某一位的数字

数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。

请写一个函数,求任意第n位对应的数字。

示例 1:

输入:n = 3
输出:3
示例 2:

输入:n = 11
输出:0

限制:

0 <= n < 2^31

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zi-xu-lie-zhong-mou-yi-wei-de-shu-zi-lcof

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
class Solution {
public:
int findNthDigit(int n) {
// 100.00% 83.71%
if(n<10) return n;
long wei=1;
long len=9;
long sum=9;
long num=1;
while(n>sum) {
num*=10;
wei+=1;
len=(len*10*wei)/(wei-1);
sum+=len;
}
sum-=len;
long n1=((n-sum)-1)/wei+1;
long n2=((n-sum)-1)%wei+1;
n2=wei-n2;
num+=(n1-1);
while(n2--) {
num/=10;
}
num%=10;
return num;
}
};

剑指 Offer 45. 把数组排成最小的数*

输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。

示例 1:

输入: [10,2]
输出: “102”
示例 2:

输入: [3,30,34,5,9]
输出: “3033459”

提示:

0 < nums.length <= 100

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ba-shu-zu-pai-cheng-zui-xiao-de-shu-lcof

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public:
// 95.37% 52.93%
string minNumber(vector<int>& nums) {
vector<string> strs;
string res;
for(auto num:nums) strs.push_back(to_string(num));
sort(strs.begin(), strs.end(), helper);
for(auto str:strs) res+=str;
return res;
}
static bool helper(string &a, string &b) {
return a+b<b+a;
}
};

剑指 Offer 46. 把数字翻译成字符串

给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。

示例 1:

输入: 12258
输出: 5
解释: 12258有5种不同的翻译,分别是”bccfi”, “bwfi”, “bczi”, “mcfi”和”mzi”

提示:

0 <= num < 2^31

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ba-shu-zi-fan-yi-cheng-zi-fu-chuan-lcof

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//dp
class Solution {
public:
int translateNum(int num) {
// 100.00% 56.62%
int dp[20]={0};
dp[0]=1;
dp[1]=1;
int i=2;
int pre=num%10;
while(num/=10) {
int cur=num%10;
if(cur*10+pre<26 && cur*10+pre>9) {
dp[i]=dp[i-1]+dp[i-2];
} else {
dp[i]=dp[i-1];
}
pre=cur;
i++;
}
return dp[i-1];
}
};

剑指 Offer 47. 礼物的最大价值

在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?

示例 1:

输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 12
解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物

提示:

0 < grid.length <= 200
0 < grid[0].length <= 200

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/li-wu-de-zui-da-jie-zhi-lcof

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
34
35
36
37
38
39
40
class Solution {
public:
int m[200][200];
int maxValue(vector<vector<int>>& grid) {
// 1.dp
// 87.13% 76.81%
int len1=grid.size();
if(!len1) return 0;
int len2=grid[0].size();
m[len1-1][len2-1]=grid[len1-1][len2-1];
for(int i=len1-2;i>=0;i--) m[i][len2-1]=m[i+1][len2-1]+grid[i][len2-1];
for(int i=len2-2;i>=0;i--) m[len1-1][i]=m[len1-1][i+1]+grid[len1-1][i];
for(int i=len1-2;i>=0;i--) {
for(int j=len2-2;j>=0;j--) {
if(m[i+1][j]>=m[i][j+1]) m[i][j]=grid[i][j]+m[i+1][j];
else m[i][j]=grid[i][j]+m[i][j+1];
}
}
return m[0][0];
}
};

class Solution2 {
public:
int m[205];
int maxValue(vector<vector<int>>& grid) {
// 2.一维dp
// 写完发现没有必要倒着来,还可以用滚动数组逐行存(此外,还可以在原数组上操作)
// 87.13% 88.92%
int len1=grid.size();
if(!len1) return 0;
int len2=grid[0].size();
for(int i=0;i<len1;i++) {
for(int j=0;j<len2;j++) {
m[j+1]=grid[i][j]+max(m[j],m[j+1]);
}
}
return m[len2];
}
};

剑指 Offer 48. 最长不含重复字符的子字符串

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

示例 1:

输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:

输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:

输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,”pwke” 是一个子序列,不是子串。

提示:

s.length <= 40000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zui-chang-bu-han-zhong-fu-zi-fu-de-zi-zi-fu-chuan-lcof

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
class Solution {
public:
int lengthOfLongestSubstring(string s) {
// 滑动窗口,也可以用动态规划
// 35.22% 21.22%
int len=s.size();
if(!len) return 0;
if(len==1) return 1;
int l=0, r=0;
int ans=1;
set<char> flag;
flag.insert(s[0]);
while(r<len-1) {
int i=l;
r++;
if(flag.count(s[r])) {
for(;i<r;i++) {
if(s[i]==s[r]) break;
flag.erase(s[i]);
}
l=i+1;
} else {
flag.insert(s[r]);
}
if(ans<r-l+1) ans=r-l+1;
}
return ans;
}
};

剑指 Offer 49. 丑数

我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。

示例:

输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
说明:

1 是丑数。
n 不超过1690。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/chou-shu-lcof

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public:
int nthUglyNumber(int n) {
// 三指针
// 74.44% 67.24%
if(!n) return 0;
vector<int>num(n,0);
num[0]=1;
int p1=0,p2=0,p3=0;
for(int i=1;i<n;i++) {
// 两次min
int temp=min(num[p1]*2,min(num[p2]*3,num[p3]*5));
num[i]=temp;
if(temp==num[p1]*2) p1++;
if(temp==num[p2]*3) p2++;
if(temp==num[p3]*5) p3++;
}
return num[n-1];
}
};

剑指 Offer 50. 第一个只出现一次的字符

在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。

示例:

s = “abaccdeff”
返回 “b”

s = “”
返回 “ “

限制:

0 <= s 的长度 <= 50000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/di-yi-ge-zhi-chu-xian-yi-ci-de-zi-fu-lcof

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
class Solution {
public:
char firstUniqChar(string s) {
// 97.81 84.89%
int len=s.size();
if(!len) return ' ';
vector<int> vec(26,0);
for(int i=0;i<len;i++) {
int t=s[i]-'a';
if(!vec[t]) {
vec[t]=i+1;
} else if(vec[t]>0){
vec[t]=-1;
}
}
int ans=len+1;
for(int i=0;i<26;i++) {
if(vec[i]>0 && ans>vec[i]) {
ans=vec[i];
}
}
if(ans==len+1) return ' ';
return s[ans-1];
}
};

CATALOG
  1. 1. 剑指 Offer 41. 数据流中的中位数**
  2. 2. 剑指 Offer 42. 连续子数组的最大和*
  3. 3. 剑指 Offer 43. 1~n整数中1出现的次数*
  4. 4. 剑指 Offer 44. 数字序列中某一位的数字
  5. 5. 剑指 Offer 45. 把数组排成最小的数*
  6. 6. 剑指 Offer 46. 把数字翻译成字符串
  7. 7. 剑指 Offer 47. 礼物的最大价值
  8. 8. 剑指 Offer 48. 最长不含重复字符的子字符串
  9. 9. 剑指 Offer 49. 丑数
  10. 10. 剑指 Offer 50. 第一个只出现一次的字符