时间效率与空间效率的平衡
剑指 Offer 49. 丑数
题目
代码
方法一:最小堆
使用最小堆,会预先存储较多的丑数,导致空间复杂度较高,维护最小堆的过程也导致时间复杂度较高。
class Solution {
public:
int nthUglyNumber(int n) {
//最小堆
vector<int>factor = {2, 3, 5};
unordered_set<long>vis;
priority_queue<long, vector<long>, greater<long>>pq;
vis.insert(1L);
pq.push(1L);
int ugly = 0;
for (int i = 0; i < n; i++) {
long curr = pq.top();
pq.pop();
ugly = (int)curr;
for (auto a : factor) {
long next = curr * a;
if (!vis.count(next)) {
vis.insert(next);
pq.push(next);
}
}
}
return ugly;
}
};
方法二:动态规划
class Solution {
public:
int nthUglyNumber(int n) {
//动态规划
vector<int>dp(n+1);
dp[1] = 1;
int p2 = 1, p3 = 1, p5 = 1;
for(int i = 2; i <= n ; i++) {
int n2 = dp[p2] * 2;
int n3 = dp[p3] * 3;
int n5 = dp[p5] * 5;
dp[i] = min(min(n2, n3), n5);
//更新指针
if(dp[i] == n2) {
p2++;
}
if(dp[i] == n3) {
p3++;
}
if(dp[i] == n5) {
p5++;
}
}
return dp[n];
}
};
剑指 Offer 50. 第一个只出现一次的字符
题目
代码
方法一:
class Solution {
public:
char firstUniqChar(string s) {
//使用哈希表存储频数
int sz = s.size();
unordered_map<char, int> m;
for(auto a:s) {
m[a]++;
}
for(int i = 0; i < sz; i++) {
if(m[s[i]] == 1) {
return s[i];
}
}
return ' ';
}
};
方法二:队列
class Solution {
public:
char firstUniqChar(string s) {
unordered_map<char,int>m;
//队列里存储索引
queue<int>q;
int sz = s.size();
for(int i = 0; i < sz; i++) {
if(m.find(s[i]) == m.end()) {
m[s[i]] = i;
q.push(i);
}else {
m[s[i]] = -1;
//延迟删除,只判断队首元素是否重复出现
while(!q.empty() && m[s[q.front()]] == -1) {
q.pop();
}
}
}
if(!q.empty()) {
return s[q.front()];
}else {
return ' ';
}
}
};
剑指 Offer 51. 数组中的逆序对
题目
代码
方法一:归并
在高级排序算法(归并排序和快速排序)里,能够看到比较明显的阶段排序结果的就是归并排序。
class Solution {
public:
//计算跨越两个区间的逆序对
int mergrandsort(vector<int>& nums, int l, int mid, int r, vector<int>&tmp) {
for(int i = l; i <= r; i++) {
tmp[i] = nums[i];
}
int i = l;
int j = mid + 1;
int count = 0;
for(int k = l; k <= r; k++) {
if(i == mid + 1) {
nums[k] = tmp[j];
j++;
}else if(j == r + 1) {
nums[k] = tmp[i];
i++;
}else if(tmp[i] <= tmp[j]) {
nums[k] = tmp[i];
i++;
}else {
nums[k] = tmp[j];
j++;
//此时记录还没有被归并回去的数字个数,即对逆序对的贡献个数
count += (mid - i + 1);
}
}
return count;
}
//区间为[left,right],tmp为辅助数组
int rp(vector<int>& nums, int l, int r, vector<int>&tmp) {
if(l == r) {
return 0;
}
int mid = l + (r - l) / 2;
int count = rp(nums, l, mid, tmp) + rp(nums, mid + 1, r, tmp);
int crosspairs = mergrandsort(nums, l, mid, r, tmp);
return count + crosspairs;
}
int reversePairs(vector<int>& nums) {
int sz = nums.size();
if(sz < 2) {
return 0;
}
vector<int> tmp (sz);
return rp(nums, 0, sz - 1, tmp);
}
};
剑指 Offer 52. 两个链表的第一个公共节点
题目
代码
该题不能使用额外的空间
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *p1 = headA, *p2 = headB;
while(p1 != p2) {
// p1 走一步,如果走到 A 链表末尾,转到 B 链表
if(p1 == nullptr) {
p1 = headB;
}else {
p1 = p1->next;
}
// p2 走一步,如果走到 B 链表末尾,转到 A 链表
if(p2 == nullptr) {
p2 = headA;
}else {
p2 = p2->next;
}
}
//没有相同节点时,也可以包括
return p1;
}
};