抱佛脚一时爽,一直抱佛脚一直爽!这篇文章总结除了本系列总结的分类外的经典题~
参考链接:leetcode 剑指offer
rand
rand5 [0,1,2,3,4] 实现 rand7[0,1,2,3,4,5,6]
- 先random5,如果生成的是0-2,就视为0,否则视为1;重复3次这个操作,生成一个三位的二进制数
rand7实现rand10
- 先用rand7实现rand10*N(以rand40为例):(rand7 - 1) * 7 + rand7,就转为了 rand49
- 用 rand49() 生成一个 [1, 49] 范围内的随机数,如果其在 [1, 40] 范围内,则进入下一步,否则重新rand49;这样就转为了rand40
- rand40 % 10 + 1转为 rand10
Trie(前缀树、字典树)
实现Trie(lc208)
class TrieNode {
public:
bool isWord;
TrieNode* child[26];
TrieNode(): isWord(false) {
for (auto& a : child) a = nullptr;
}
};
class Trie {
public:
/** Initialize your data structure here. */
Trie() {
root = new TrieNode();
}
/** Inserts a word into the trie. */
void insert(string word) {
TrieNode* cur = root;
for (char c : word) {
int i = c - 'a';
if (!cur->child[i]) cur->child[i] = new TrieNode();
cur = cur->child[i];
}
cur->isWord = true;
}
/** Returns if the word is in the trie. */
bool search(string word) {
TrieNode* cur = root;
for (char c : word) {
int i = c - 'a';
if (!cur->child[i]) return false;
cur = cur->child[i];
}
return cur->isWord;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
bool startsWith(string prefix) {
TrieNode* cur = root;
for (char c : prefix) {
int i = c - 'a';
if (!cur->child[i]) return false;
cur = cur->child[i];
}
return true;
}
private:
TrieNode* root;
};
队列与栈
字符串解码(lc394)
class Solution {
public:
string decodeString(string s) {
string t = "";
stack s_num;
stack s_str;
int cnt = 0;
for (int i = 0; i < s.size(); ++i) {
if (s[i] >= '0' && s[i] <= '9') {
cnt = 10 * cnt + s[i] - '0';
} else if (s[i] == '[') {
s_num.push(cnt);
s_str.push(t);
cnt = 0; t.clear();
} else if (s[i] == ']') {
int k = s_num.top(); s_num.pop();
for (int j = 0; j < k; ++j) s_str.top() += t;
t = s_str.top(); s_str.pop();
} else {
t += s[i];
}
}
return s_str.empty() ? t : s_str.top();
}
};
用队列实现栈(lc225)
- 法一:一个队列用来放最后加进来的数,模拟栈顶元素。剩下所有的数都按顺序放入另一个队列中
class MyStack {
public:
/** Initialize your data structure here. */
MyStack() { }
/** Push element x onto stack. */
void push(int x) {
if (!q2.empty()) {
q1.push(q2.front());
q2.pop();
}
q2.push(x);
}
/** Removes the element on top of the stack and returns that element. */
int pop() {
int res = top();
q2.pop();
return res;
}
/** Get the top element. */
int top() {
if (q2.empty()) {
int n = q1.size();
for (int i = 0; i < n - 1; ++i) {
q1.push(q1.front());
q1.pop();
}
q2.push(q1.front());
q1.pop();
}
return q2.front();
}
/** Returns whether the stack is empty. */
bool empty() {
return q1.empty() && q2.empty();
}
private:
queue q1;
queue q2;
};
- 法二:每次把新加入的数插到前头;只要实现对了 push() 函数,后面三个直接调用队列的函数即可
class MyStack {
public:
/** Initialize your data structure here. */
MyStack() { }
/** Push element x onto stack. */
void push(int x) {
q.push(x);
int n = q.size();
for (int i = 0; i < n - 1; ++i) {
q.push(q.front());
q.pop();
}
}
/** Removes the element on top of the stack and returns that element. */
int pop() {
int res = q.front();
q.pop();
return res;
}
/** Get the top element. */
int top() {
return q.front();
}
/** Returns whether the stack is empty. */
bool empty() {
return q.empty();
}
private:
queue q;
};
实现队列O(1)返回当前最大值
当一个元素入列时,在最大值序列尾部的所有小于当前元素的都可以直接删除,然后当前元素直接入到最大值队列中
N-SUM问题
2sum(jz42)
- 使用hash表,建立HashMap
map,键值对为 。遍历数组nums,对每个数nums[i],查询hash表中是否有key等于target-nums[i]的记录,若有,则找到;否则,将num[i]和下标i存入hash表
3sum(lc15)
- 固定一位+双指针
3sum closest(lc16)
int threeSumClosest(vector& nums, int target) {
int n = nums.size();
if (n < 3) return -1;
int res = nums[0] + nums[1] + nums[2];
int diff = abs(res - target);
sort(nums.begin(), nums.end());
for (int i = 0; i < n - 2; ++i) {
int left = i + 1, right = n - 1;
while (left < right) {
int curSum = nums[i] + nums[left] + nums[right];
int newDiff = abs(curSum - target);
if (newDiff < diff) {
res = curSum;
diff = newDiff;
}
if (curSum > target) --right;
else ++left;
}
}
return res;
}
其他题目
第k大的数(lc215)
- 堆排序法:小根堆;num > heap.top 才push num;元素不足k个时返回空vector
- 快排法
int findKthLargest(vector& nums, int k) {
int left = 0, right = nums.size() - 1;
while (true) {
int pos = partition(nums, left, right);
if (pos == k - 1) return nums[pos];
if (pos > k - 1) right = pos - 1;
else left = pos + 1;
}
}
int partition(vector& nums, int left, int right) {
int pivot = nums[left], l = left + 1, r = right;
while (l <= r) {
if (nums[l] < pivot && nums[r] > pivot) {
swap(nums[l++], nums[r--]);
}
if (nums[l] >= pivot) ++l;
if (nums[r] <= pivot) --r;
}
swap(nums[left], nums[r]);
return r;
}
丑数(jz33)
int GetUglyNumber_Solution(int index) {
if (index <= 0) return 0;
vector results;
results.reserve(index);
int p2 = 0, p3 = 0, p5 = 0;
results[0] = 1;
for (int i = 1; i < index; ++i) {
results[i] = min(results[p2] * 2, min(results[p3] * 3, results[p5] * 5));
if (results[i] == results[p2] * 2) ++p2;
if (results[i] == results[p3] * 3) ++p3;
if (results[i] == results[p5] * 5) ++p5;
}
return results[index - 1];
}
扑克牌顺子(jz45)
- 最大值-最小值<5 且无重复数字即可
- 遇到为0的元素不做处理
圆圈游戏(jz46)
- 当我们知道了 f(n - 1, m) 对应的答案 x 之后,我们也就可以知道,长度为 n 的序列最后一个删除的元素,应当是从 m % n 开始数的第 x 个元素。因此有 f(n, m) = (m % n + x) % n = (m + x) % n
- f[n] = (f[n-1] + m) % n
- 也可以用链表来模拟整个过程
数据流中位数(jz63)
- 后半段是小顶堆,前半段是大顶堆
- 奇数放大顶堆:但需先检查它是否比小顶堆的最小值大,大则放小顶堆,然后把小顶堆的最小值放到大顶堆中
- 偶数放小顶堆:但需先检查它是否比大顶堆的最大值小,小则放大顶堆,然后把大顶堆的最大值放到小顶堆中
- 小顶堆:priority_queue
, greater > - 大顶堆:priority_queue
class Solution {
private:
int cnt = 0;
priority_queue maxHeap;
priority_queue, greater> minHeap;
public:
void Insert(int num) {
++cnt;
if (cnt % 2) {
if (!minHeap.empty() && num > minHeap.top()) {
minHeap.push(num);
num = minHeap.top();
minHeap.pop();
}
maxHeap.push(num);
} else {
if (!maxHeap.empty() && num < maxHeap.top()) {
maxHeap.push(num);
num = maxHeap.top();
maxHeap.pop();
}
minHeap.push(num);
}
}
double GetMedian() {
if (cnt % 2) {
return (double) maxHeap.top();
} else {
return (double) (maxHeap.top() + minHeap.top()) / 2;
}
}
};
滑动窗口的最大值(jz64)
vector maxInWindows(const vector& num, unsigned int size) {
vector res;
int n = num.size();
if (size > n || n <= 0 || size <= 0) return res;
deque dq; // 维护一个单调递减的双端队列,但存的是index
for (int i = 0; i < n; ++i) {
while (!dq.empty() && num[dq.back()] <= num[i])
dq.pop_back();
dq.push_back(i);
if (dq.front() + size <= i)
dq.pop_front();
if (i + 1 >= size)
res.push_back(num[dq.front()]);
}
return res;
}
LFU(lc460)
class LFUCache {
public:
LFUCache(int capacity) {
cap = capacity;
}
int get(int key) {
if (m.count(key) == 0) return -1;
freq[m[key].second].erase(iter[key]);
++m[key].second;
freq[m[key].second].push_back(key);
iter[key] = --freq[m[key].second].end();
if (freq[minFreq].size() == 0) ++minFreq;
return m[key].first;
}
void put(int key, int value) {
if (cap <= 0) return;
if (get(key) != -1) {
m[key].first = value;
return;
}
if (m.size() >= cap) {
m.erase(freq[minFreq].front());
iter.erase(freq[minFreq].front());
freq[minFreq].pop_front();
}
m[key] = {value, 1};
freq[1].push_back(key);
iter[key] = --freq[1].end();
minFreq = 1;
}
private:
int cap, minFreq;
unordered_map> m;
unordered_map> freq;
unordered_map::iterator> iter;
};
LRU(lc146)
class LRUCache {
public:
LRUCache(int capacity) {
capacity_ = capacity;
}
int get(int key) {
auto m_iter = m.find(key);
if (m_iter == m.end()) return -1; // 找不到返回-1
auto l_iter = m_iter->second;
l.splice(l.begin(), l, l_iter); // 找到了则把它移到表头:在l.begin()前面插入
return l_iter->second; // 返回key对应的value
}
void put(int key, int value) {
auto m_iter = m.find(key); // 找到list中key对应的kv对
if (m_iter != m.end()) {
l.erase(m_iter->second);
m.erase(key);
}
l.push_front(make_pair(key, value)); // kv对放入表头
m[key] = l.begin();
if (l.size() > capacity_) {
int k = l.rbegin()->first;
l.pop_back();
m.erase(k);
}
}
private:
list> l; // 存储kv对,最近使用的在表头
unordered_map>::iterator> m; // key,key对应的kv对在链表l中的位置
int capacity_;
};
缺失的第一个正整数(lc41)
- 如果数组中有数字i+1,把i+1放到nums[i]中
int firstMissingPositive(vector& nums) {
int n = nums.size();
if (!n) return 1;
for (int i = 0; i < n; ++i) { // 如果数组中有数字i+1,把i+1放到nums[i]中
while (nums[i] >= 1 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) {
swap(nums[i], nums[nums[i] - 1]);
}
}
for (int i = 0; i < n; ++i) {
if (nums[i] != i + 1) return i + 1;
}
return n + 1;
}
幂(lc50)
- 注意:用n*=-1会导致溢出
double myPow(double x, int n) {
if (n == 0) return 1;
double half = myPow(x, n / 2);
// n 为偶数
if (n % 2 == 0) return half * half;
// n 为奇数
if (n > 0) return half * half * x;
return half * half / x;
}
长度为n的一个数字,要删掉其中m位,求删掉后最大的数字是什么(lc402)
- 法一:最高位必然在前m+1位中,获取最高位之后开始递归
- 法二:维护一个递增栈,只要发现当前的数字小于栈顶元素的话,就将栈顶元素移除;因为此时栈顶元素在高位上,就算后面的数字再大,也是在低位上,我们只有将高位上的数字尽可能的变小,才能使整个剩下的数字尽可能的小
string removeKdigits(string num, int k) {
string res = "";
int n = num.size(), keep = n - k;
for (char c : num) {
while (!res.empty() && c < res.back() && k) {
res.pop_back();
--k;
}
res.push_back(c);
}
res.resize(keep);
while (!res.empty() && res[0] == '0') res.erase(res.begin());
return res.empty() ? "0" : res;
}
n条直线相交时最多时有几个平面
- 直线最多可以与原来的每一条直线都相交,也就是说与(n-1)条直线都相交,从而产生(n-1)个交点,该直线被分成n部分,而每一部分将所在区域一分为二,从而多出了n个部分