目录
一、数组
1365. 有多少小于当前数字的数字
941. 有效的山脉数组
1207. 独一无二的出现次数
189. 轮转数组
724. 寻找数组的中心下标
922. 按奇偶排序数组 II
二、哈希表
205. 同构字符串
925. 长按键入
三、二叉树
129. 求根节点到叶节点数字之和
1382. 将二叉搜索树变平衡
四、回溯
52. N皇后 II
五、贪心
649. Dota2 参议院
1221. 分割平衡字符串
870. 优势洗牌
六、动态规划
132. 分割回文串 II[*]
673. 最长递增子序列的个数[*]
七、位运算
1356. 根据数字二进制下 1 的数目排序
vector smallerNumbersThanCurrent(vector& nums) {
vector numsSort = nums;
sort(numsSort.begin(), numsSort.end());
unordered_map record;
// 从后向前赋值,保证下标为第一个出现的元素
for(int i = numsSort.size() - 1; i >= 0; i--) {
record[numsSort[i]] = i;
}
vector res(nums.size());
for (int i = 0; i < nums.size(); i++) {
res[i] = record[nums[i]];
}
return res;
}
bool validMountainArray(vector& arr) {
// 双指针法
if (arr.size() <= 2) return false;
int left = 0;
int right = arr.size() - 1;
while (left < arr.size() - 1 && arr[left + 1] > arr[left]) {
left++;
}
while (right > 0 && arr[right - 1] > arr[right]) {
right--;
}
if (left == right && left != 0 && right != arr.size() - 1) return true;
else return false;
}
bool uniqueOccurrences(vector& arr) {
unordered_map arrCount;
for (int i : arr) {
arrCount[i]++;
}
set count;
for (auto it = arrCount.begin(); it != arrCount.end(); it++) {
count.insert(it->second);
}
if (arrCount.size() == count.size()) return true;
else return false;
}
void rotate(vector& nums, int k) {
// 参考翻转字符串问题
k = k % nums.size();
reverse(nums.begin(), nums.end());
reverse(nums.begin(), nums.begin() + k);
reverse(nums.begin() + k, nums.end());
}
int pivotIndex(vector& nums) {
int sum = accumulate(nums.begin(), nums.end(), 0);
int tempSum = 0;
for (int i = 0; i < nums.size(); i++) {
if (sum - nums[i] == 2 * tempSum) return i;
tempSum += nums[i];
}
return -1;
}
vector sortArrayByParityII(vector& nums) {
// 双指针法
int odd = 1;
for (int even = 0; even < nums.size(); even += 2) {
// 偶数位不符合,在奇数位寻找不符合的元素进行替换
if (nums[even] % 2 != 0) {
while (nums[odd] % 2 != 0) odd += 2;
swap(nums[odd], nums[even]);
}
}
return nums;
}
bool isIsomorphic(string s, string t) {
// 利用unordered_map直接记录字符与字符之间的映射
unordered_map map1;
unordered_map map2;
for (int i = 0, j = 0; i < s.size(); i++, j++) {
if (map1.find(s[i]) == map1.end()) { // map1保存s[i] 到 t[j]的映射
map1[s[i]] = t[j];
}
if (map2.find(t[j]) == map2.end()) { // map2保存t[j] 到 s[i]的映射
map2[t[j]] = s[i];
}
// 发现映射 对应不上,立刻返回false
if (map1[s[i]] != t[j] || map2[t[j]] != s[i]) {
return false;
}
}
return true;
}
bool isLongPressedName(string name, string typed) {
int index = 0;
// 记录重复元素数
int count = 0;
for (int i = 0; i < name.size(); i++) {
// 名字中有连续重复字符
if (i > 0 && name[i] == name[i - 1]) {
count--;
if (count < 0) return false;
}
// 名字中没有连续重复字符
else {
count = 0;
if (name[i] != typed[index]) return false;
if (index >= typed.size()) return false;
while (index + 1 < typed.size() && typed[index + 1] == typed[index]) {
index++;
count++;
}
index++;
}
}
// 是否有多余字符
if (index != typed.size()) return false;
return true;
}
int res;
vector path;
int sumNumbers(TreeNode* root) {
res = 0;
path.clear();
if (!root) return res;
path.push_back(root->val);
BackTracking(root);
return res;
}
void BackTracking(TreeNode* node) {
// 返回条件
if (!node->left && !node->right) {
res += VecToInt(path);
return;
}
// 前序遍历
if (node->left) {
path.push_back(node->left->val);
BackTracking(node->left);
path.pop_back();
}
if (node->right) {
path.push_back(node->right->val);
BackTracking(node->right);
path.pop_back();
}
return;
}
int VecToInt(vector& v) {
int res = 0;
for (int i : v) {
res = res * 10 + i;
}
return res;
}
vector elem;
TreeNode* balanceBST(TreeNode* root) {
// 搜索树元素存入数组
elem.clear();
Traversal(root);
BuildTree(root, 0, elem.size() - 1);
return root;
}
void Traversal(TreeNode* node) {
if (!node) return;
Traversal(node->left);
elem.push_back(node->val);
Traversal(node->right);
return;
}
void BuildTree(TreeNode* node, int start, int end) {
// 前序遍历
int mid = start + (end - start) / 2;
node->val = elem[mid];
if (start <= mid - 1) {
TreeNode* left = new TreeNode();
node->left = left;
BuildTree(left, start, mid - 1);
}
if (end >= mid + 1) {
TreeNode* right = new TreeNode();
node->right = right;
BuildTree(right, mid + 1, end);
}
return;
}
int res;
int totalNQueens(int n) {
res = 0;
vector chessboard(n, string(n, '.'));
BackTracking(0, chessboard, n);
return res;
}
void BackTracking(int row, vector& chessboard, const int n) {
if (row == n) {
res++;
return;
}
for (int col = 0; col < n; col++) {
// 棋盘有效时才继续递归,无效则进行下一次同层迭代
if (IsValid(row, col, chessboard, n)) {
chessboard[row][col] = 'Q';
BackTracking(row + 1, chessboard, n);
chessboard[row][col] = '.';
}
}
}
// 检查当前棋盘是否合法
bool IsValid(int row, int col, vector& chessboard, const int n) {
// 检查列
for (int i = 0; i < row; i++) {
if (chessboard[i][col] == 'Q') {
return false;
}
}
// 检查135度角是否有皇后
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if (chessboard[i][j] == 'Q') {
return false;
}
}
// 检查45度角是否有皇后
for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
if (chessboard[i][j] == 'Q') {
return false;
}
}
return true;
}
string predictPartyVictory(string senate) {
// 局部最优:有一次权利机会,就消灭自己后面的对手
// 全局最优:为自己的阵营赢取最大利益
// 利用队列储存两个阵营成员下标,出队代表被消灭
queue teamR, teamD;
for (int i = 0; i < senate.size(); i++) {
if (senate[i] == 'R') teamR.push(i);
else teamD.push(i);
}
if (teamR.empty()) return "Dire";
if (teamD.empty()) return "Radiant";
// 模拟投票过程
while(!teamR.empty() && !teamD.empty()) {
// 优先剥夺身后下一个位置的对方阵营权利
if (teamR.front() < teamD.front()) {
int R = teamR.front();
teamD.pop();
teamR.pop();
// 该成员进入下一轮投票
teamR.push(R + senate.size());
}
else {
int D = teamD.front();
teamR.pop();
teamD.pop();
// 该成员进入下一轮投票
teamD.push(D + senate.size());
}
}
return teamR.empty() ? "Dire" : "Radiant";
}
要注意巧妙运用各种数据结构,尤其是流程模拟类问题。
int balancedStringSplit(string s) {
// 贪心:每遇到一个平衡子串就收录
int res = 0;
stack st;
for (char str : s) {
if (st.empty() || str == st.top()) st.push(str);
else {
st.pop();
if (st.empty()) res++;
}
}
return res;
}
本题可以不利用堆栈,利用一个count记录即可。
vector advantageCount(vector& nums1, vector& nums2) {
vector res(nums1.size());
sort(nums1.begin(), nums1.end(), greater());
// 从大到小排序,同时还记录了原下标位置
multimap> nums2_index;
for (size_t i = 0; i < nums2.size(); i++) {
nums2_index.insert(make_pair(nums2[i], i));
}
int big = 0;
int small = nums1.size() - 1;
// 田忌赛马策略:如果当前马比得过对方,就派出当前马;若比不过,派出最小马保存实力。
for (auto it = nums2_index.begin(); it != nums2_index.end(); it++) {
if (nums1[big] > it->first) {
res[it->second] = nums1[big];
big++;
}
else {
res[it->second] = nums1[small];
small--;
}
}
return res;
}
int minCut(string s) {
if (s.size() <= 1) return 0;
// dp[i]:下标为0-i区间内字符串分割为回文子串的最小分割数
vector dp(s.size());
// 初始化:由于要取最小值,应将数组初始化为可能的最大值,即字符串长度
for (int i = 0; i < s.size(); i++) dp[i] = i;
// 递推公式
for (int i = 1; i < s.size(); i++) {
// 0-i是回文则为0
if (isPalindromic(s, 0, i)) {
dp[i] = 0;
continue;
}
// 0-i非回文,j从0至i-1遍历,若j+1至i为回文,只需再分割一次:dp[i] = dp[j] + 1
// i一定会被更新,取最小值即可
for (int j = 0; j < i; j++) {
if (isPalindromic(s, j + 1, i)) {
dp[i] = min(dp[j] + 1, dp[i]);
}
}
}
return dp[s.size() - 1];
}
bool isPalindromic(const string& s, int start, int end) {
for (int i = start, j = end; i < j; i++, j--) {
if (s[i] != s[j]) return false;
}
return true;
}
131. 分割回文串问题使用了回溯法,本题采用回溯法会超时。一般需要返回所有结果的采用回溯法,而只需要记录数量的问题可以采用动态规划法。
动态规划递推公式中存在min/max时,要注意初始化时将元素置为可能的最大值或最小值,以免影响结果。本题递推公式较难理解,dp[i]不止取决于上一个元素,而取决于从0至i-1所有的dp数组元素,因此需要j从0至i-1进行循环,找到最小值即可。
为提高速度,一种优化方法是仍利用动态规划法,采用二维dp[i][j]数组记录下标为i至j的字符串是否为回文串,保存这些信息在循环中使用。
int findNumberOfLIS(vector& nums) {
if (nums.size() == 1) return 1;
int maxLength = 1;
int res = 0;
// length[i]:0-i的序列中最长递增子序列的长度【更新方式说明见题目300】
// cnt[i]:以nums[i]结尾的最长递增子序列【子序列含nums[i]】的个数
vector length(nums.size(), 1);
vector cnt(nums.size(), 1);
// 递推公式:
for (int i = 1; i < nums.size(); i++) {
for (int j = 0; j < i; j++) {
// 出现了更大元素,需要进行遍历判断该元素的出现是否改变了最大递增子序列长度
if (nums[i] > nums[j]) {
// 不影响最大递增子序列长度
if (length[j] + 1 > length[i]) {
length[i] = length[j] + 1;
cnt[i] = cnt[j];
}
// 最大子序列长度改变
else if (length[j] + 1 == length[i]) {
cnt[i] += cnt[j];
}
}
}
maxLength = max(length[i], maxLength);
}
for (int i = 0; i < nums.size(); i++) {
if (length[i] == maxLength) res += cnt[i];
}
return res;
}
【本题仍需深入思考】,其技巧在于定义了两个dp数组。
class Solution {
private:
static int BitCount(int num) {
int count = 0;
while (num > 0) {
count += num & 1;
num >>= 1;
}
return count;
}
static bool cmp(int a, int b) {
int bitA = BitCount(a);
int bitB = BitCount(b);
if (bitA == bitB) return a < b;
else return bitA < bitB;
}
public:
vector sortByBits(vector& arr) {
sort(arr.begin(), arr.end(), cmp);
return arr;
}
};
按位进行“与”运算即可,要注意sort函数的使用。