分析:
动态规划:设dp[i][j]表示从(0, 0)位置开始到(i, j)位置的最大价值。边界条件:由于每次只能向右或者向下移动,所以第0列的方格只能由其上边的位置向下移动而来,同理第0行的方格只能由其左边的位置移动而来。所以有:
dp[i][0] = dp[i - 1][0] + grid[i][0], dp[0][j] = dp[0][j - 1] + grid[0][j]
转移方程:每一个格子都能由两个方向而来,取它们的最大值即可:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
代码:
class Solution {
public:
int maxValue(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
vector<vector<int>> dp(m, vector<int>(n, 0));
dp[0][0] = grid[0][0];
for(int j = 1; j < n; j++) {
dp[0][j] = dp[0][j - 1] + grid[0][j];
}
for(int i = 1; i < m; i++) {
dp[i][0] = dp[i - 1][0] + grid[i][0];
}
for(int i = 1; i < m; i++) {
for(int j = 1; j < n; j++) {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
return dp[m - 1][n - 1];
}
};
class Solution {
public:
int reversePairs(vector<int>& nums) {
//暴力求解
if(nums.size() == 0) {
return 0;
}
int n = nums.size();
int res = 0;
for(int i = 1; i < n; i++) {
for(int j = i - 1; j >= 0; j--) {
if(nums[j] > nums[i]) {
res++;
}
}
}
return res;
}
};
方法二:
官方题解:借助归并排序。
代码:
class Solution {
public:
int mergeSort(vector<int>& nums, vector<int>& tmp, int l, int r) {
if (l >= r) {
return 0;
}
int mid = (l + r) / 2;
int inv_count = mergeSort(nums, tmp, l, mid) + mergeSort(nums, tmp, mid + 1, r);
int i = l, j = mid + 1, pos = l;
while (i <= mid && j <= r) {
if (nums[i] <= nums[j]) {
tmp[pos] = nums[i];
++i;
inv_count += (j - (mid + 1));
}
else {
tmp[pos] = nums[j];
++j;
}
++pos;
}
for (int k = i; k <= mid; ++k) {
tmp[pos++] = nums[k];
inv_count += (j - (mid + 1));
}
for (int k = j; k <= r; ++k) {
tmp[pos++] = nums[k];
}
copy(tmp.begin() + l, tmp.begin() + r + 1, nums.begin() + l);
return inv_count;
}
int reversePairs(vector<int>& nums) {
int n = nums.size();
vector<int> tmp(n);
return mergeSort(nums, tmp, 0, n - 1);
}
};
class Solution {
public:
bool isMatch(string s, string p) {
int m = s.size();
int n = p.size();
auto matches = [&](int i, int j) {
if (i == 0) {
return false;
}
if (p[j - 1] == '.') {
return true;
}
return s[i - 1] == p[j - 1];
};
vector<vector<int>> f(m + 1, vector<int>(n + 1));
f[0][0] = true;
for (int i = 0; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (p[j - 1] == '*') {
f[i][j] |= f[i][j - 2];
if (matches(i, j - 1)) {
f[i][j] |= f[i - 1][j];
}
}
else {
if (matches(i, j)) {
f[i][j] |= f[i - 1][j - 1];
}
}
}
}
return f[m][n];
}
};
分析:
一开始是直接遍历原链表,然后生成新链表,但这样只是浅拷贝。解决办法:利用哈希表存储新旧结点之间的关系。
代码:
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if(!head) {
return NULL;
}
map<Node*, Node*> mp;
Node* ptr = head;
while(ptr) {
Node* temp = new Node(ptr->val);
mp[ptr] = temp;
ptr = ptr->next;
}
ptr = head;
while(ptr) {
mp[ptr]->next = mp[ptr->next];
mp[ptr]->random = mp[ptr->random];
ptr = ptr->next;
}
return mp[head];
}
};
分析:
最简单的思路就是排序再计算,不过这样会超时。题目要求求出中位数,我们并不需要知道除了中位数以外其他的数,因此可以维护两个堆,左边维护一个大根堆,其最右边为最大值,右边维护一个小根堆,其最左边为最小值。需要保证最左边大根堆的堆顶元素小于等于右边小根堆的堆顶元素。这样,中位数就只与两个堆的堆顶元素有关。
具体而言,如果当前两个堆元素个数相等,就将该数放入左边的大根堆,但是为了保证两个堆的大小关系成立,需要先将该元素放入小根堆,然后再取其堆顶元素放入到大根堆。如果当前两个堆元素个数不相等,就将该数加入到右边的小根堆。求中位数时,如果当前两个堆元素个数不相等,就取左边大根堆堆顶元素,否则取两个堆顶元素的平均值。
代码:
class MedianFinder {
public:
/** initialize your data structure here. */
priority_queue<int, vector<int>, less<int>> p; //大根堆
priority_queue<int, vector<int>, greater<int>> q; //小根堆
MedianFinder() {
}
void addNum(int num) {
if(p.size() == q.size()) {
q.push(num);
p.push(q.top());
q.pop();
}else {
p.push(num);
q.push(p.top());
p.pop();
}
}
double findMedian() {
if(p.size() == q.size()) {
return (p.top() + q.top()) / 2.0;
}else {
return p.top() / 1.0;
}
}
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/
分析:
二叉搜索树的中序遍历为一个升序序列。在进行二叉搜索树的中序遍历时,维护两个指针分别指向当前节点和上一个被访问到的节点,然后对两个节点进行链接。
代码:
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node() {}
Node(int _val) {
val = _val;
left = NULL;
right = NULL;
}
Node(int _val, Node* _left, Node* _right) {
val = _val;
left = _left;
right = _right;
}
};
*/
class Solution {
public:
Node *pre, *head;
void ldr(Node* cur) {
if(cur) {
ldr(cur->left);
//建立链接
if(pre) {
pre->right = cur;
}else {
head = cur;
}
cur->left = pre;
pre = cur;
ldr(cur->right);
}
}
Node* treeToDoublyList(Node* root) {
if(!root) {
return NULL;
}
ldr(root);
head->left = pre;
pre->right= head;
return head;
}
};
class Solution {
public:
int get(int n) {
int res = 0;
while(n) {
if(n % 10 == 1) {
res++;
}
n /= 10;
}
return res;
}
int countDigitOne(int n) {
int res = 0;
for(int i = 1; i <= n; i++) {
res += get(i);
}
return res;
}
};
方法二:
找规律:参考官方题解。
代码:
class Solution {
public:
int countDigitOne(int n) {
// 返回结果初始化为0
int res = 0;
// 先对最低位进行初始化
int cur = n % 10, high = n / 10, low = 0;
long digital = 1;
while(high != 0 || cur != 0){
if(cur == 0){
res += int(high * digital);
}else if(cur == 1){
res += int(high * digital + low + 1);
}else{
res += int((high+1) * digital);
}
low = int(low + digital * cur);
cur = high % 10;
high = high / 10;
digital *= 10;
}
return res;
}
};
分析:
暴力求解方法:每滑动一次就对窗口求一次最大值,该方法会超时。可以维护一个大根堆,初始将窗口内的数压入大根堆,此时堆顶为最大值,每滑动一个窗口就将一个数加入到大根堆中,但加入后大根堆中的最大值可能并不是目前窗口内的最大值,此时可以进行循环出栈操作,直至当前大根堆堆顶元素在窗口内。
代码:
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> res;
if(nums.size() == 0) {
return res;
}
int n = nums.size();
priority_queue<pair<int, int>> heap; //大根堆
for(int i = 0; i < k; i++) {
heap.emplace(nums[i], i);
}
res.push_back(heap.top().first);
int left = 1;
for(int right = k; right < n; right++) {
//最大值可能并不在窗口中
heap.emplace(nums[right], right);
while(heap.top().second < left) {
heap.pop();
}
left++;
res.push_back(heap.top().first);
}
return res;
}
};