赞
踩
数组是存放在连续内存空间上的相同类型数据的集合。数组索引从零开始,增删需将元素移动覆盖。
[left, right]
在left == right
时有意义,需要判断left <= right
O(n)
:快慢双指针和相向双指针链表是通过指针域的指针链接的内存中的各个节点,并非连续分布。C++中最好手动释放内存。
数据结构 | 内存分布 | 查询复杂度 | 增删复杂度 |
---|---|---|---|
数组 | 连续分布 | O(1) | O(n) |
链表 | 离散分布 | O(n) | O(1) |
// 单链表
struct ListNode {
int val; // 节点上存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};
while (index--) { cur = cur->next; }
哈希表是根据关键码的值 (Key-Value) 而直接进行访问的数据结构。一般用来快速判断一个元素是否出现集合里。牺牲了空间换取了时间。
集合 | 底层实现 | 是否有序 | 是否能重复 | 是否能修改 | 查询效率 | 增删效率 |
---|---|---|---|---|---|---|
std::set | 红黑树 | 是 | 否 | 是 | O(log n) | O(log n) |
std::multiset | 红黑树 | 是 | 是 | 是 | O(log n) | O(log n) |
std::unordered_set | 哈系表 | 否 | 否 | 是 | O(1) | O(1) |
集合 | 底层实现 | 是否有序 | 是否能重复 | 是否能修改 | 查询效率 | 增删效率 |
---|---|---|---|---|---|---|
std::map | 红黑树 | key有序 | key不能 | key不能 | O(log n) | O(log n) |
std::multimap | 红黑树 | key有序 | key能 | key不能 | O(log n) | O(log n) |
std::unordered_map | 哈系表 | key无序 | key不能 | key不能 | O(1) | O(1) |
字符数组,有字符串处理的相关接口如
size
,substr
,split
,reverse
next
数组:前缀表或前缀表统一减1,初始j = -1
栈先进后出,队列先进先出。是容器适配器 Container adapter:不允许遍历,不提供迭代器,对外提供统一的接口(push, pop, top),底层容器(deque, vector, list)可插拔。SGI STL默认以deque(内存非连续)为底层结构。
std::stack<int, std::vector<int> > third; // 使用vector为底层容器的栈
std::queue<int, std::list<int>> third; // 定义以list为底层容器的队列
class comp { // 自定义类型的排序
public:
bool operator() (const pair<int, int>& lhs, const pair<int, int>& rhs) {
return lhs.second > rhs.second;
}
}
std::priority_queue<pair<int, int>, vector<pair<int, int>>, comp> pri_que;
O(n)
vs 优先级队列维护窗口内所有元素顺序 O(nlogn)
// 链式储存二叉树定义
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int val) : val(val), left(nullptr), right(nullptr) {}
TreeNode(int val, TreeNode* left, TreeNode* right) : val(val), left(left), right(right) {}
}
// 最大深度 // 前序求深度(回溯) int mdepth; void getDepth(TreeNode* node, int depth) { mdepth = depth > mdepth ? depth : mdepth; if (node->left == nullptr && node->right == nullptr) return; if (node->left) getDepth(node->left, depth + 1); if (node->right) getDepth(node->right, depth + 1); } // 后序求高度 int getHeight(TreeNode* root) { if (root == nullptr) return 0; return max(maxDepth(root->left), maxDepth(root->right)) + 1; } // 最小深度 // 前序求深度(回溯) int mdepth; void getDepth(TreeNode* node, int depth) { if (node->left == nullptr && node->right == nullptr) { mdepth = min(mdepth, depth); return; } if (node->left && mdepth > depth) getDepth(node->left, depth + 1); if (node->right && mdepth > depth) getDepth(node->right, depth + 1); } // 后序求高度 int getHeight(TreeNode* root) { if (root == nullptr) return 0; if (root->left == nullptr && root->right == nullptr) return 1; if (root->left == nullptr) return 1 + minDepth(root->right); if (root->right == nullptr) return 1 + minDepth(root->left); return min(minDepth(root->left), minDepth(root->right)) + 1; }
本质是穷举,可通过剪枝提高效率。可以抽象为树形结构,在集合中递归查找子集, 集合的大小构成树的宽度,递归的深度构成树的深度(高度有限)。可以解决组合问题、切割问题、子集问题、排列问题、棋盘问题等。
// 回溯模板
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
本质:每一阶段局部最优,从而达到全局最优。想不到反例时可以考虑用贪心,将问题分解为若干子问题,用贪心策略求解子问题最优解,堆叠成全局最优解。
本质:重叠子问题,当前状态由上一状态推导出来。
五部曲:
1. 确定dp数组(dp table)以及下标的含义
2. 确定递推公式
3. dp数组如何初始化
4. 确定遍历顺序
5. 举例推导dp数组
(打印数组debug)
背包问题
// 二维数组 略 // 一维滚动数组 int maxValue1D(int bagweight, vector<int>& weights, vector<int>& values) { vector<int> dp(bagweight + 1, 0); for (int i = 0; i < weights.size(); i++) { // for (int j = 1; j <= bagweight; j++) { // 会重复添加同一物品->完全背包 // if (j >= weights[i]) // dp[j] = max(dp[j], dp[j - weights[i]] + values[i]); // } // 倒序确保物品只加入一次 for (int j = bagweight; j >= weights[i]; j--) { // 如果能放下 dp[j] = max(dp[j], dp[j - weights[i]] + values[i]); // 放不放 } } return dp[bagweight]; // 如果先遍历背包容量,相当于每个dp[j]只放入一个物品 // for (int j = bagweight; j >= 1; j--) { // for (int i = 0; i < weights.size(); i++) { // if (j >= weights[i]) // dp[j] = max(dp[j], dp[j - weights[i]] + values[i]); // } // } // return dp[bagweight]; }
// 一维滚动数组 int maxValue1D(int bagweight, vector<int>& weights, vector<int>& values) { vector<int> dp(bagweight + 1, 0); // 先遍历物品,再遍历背包 => 求组合数 for (int i = 0; i < weights.size(); i++) { for (int j = 1; j <= bagweight; j++) { // 可重复添加同一物品 if (j >= weights[i]) dp[j] = max(dp[j], dp[j - weights[i]] + values[i]); // 求最大价值 } } // 先遍历背包,再遍历物品 => 求排列数 // for (int j = 0; j <= bagweight; j++) { // 遍历背包容量 // for (int i = 0; i < weights.size(); i++) { // 遍历物品 // // 递推公式 // } // } // 求最小数目等情况,两种遍历顺序都行 return dp[bagweight]; }
int maxValue1D(int bagweight, vector<int>& weights, vector<int>& values, vector<int>& nums) { // 一维滚动数组 vector<int> dp(bagweight + 1, 0); for (int i = 0; i < weights.size(); i++) { for (int j = bagweight; j >= weights[i]; j--) { // 如果能放下 // 遍历物品个数 for (int k = 1; k <= nums[i] && j >= k * weights[i]; k++) { dp[j] = max(dp[j], // 不放 dp[j - k * weights[i]] + k * values[i]); // 放k个 } } } return dp.back();
dp[j] = max(dp[j], dp[j - weights[i]] + values[i]); // 求最大价值
dp[j] += dp[j - nums[i]]; // 求组合或排列数
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]); // 求能否装满或最多装多少
dp[j] = min(dp[j - coins[i]] + 1, dp[j]); // 求装满背包最小物品数目
打家劫舍
股票问题
子序列问题 (编辑距离、回文字串、回文子序列)
栈里元素递增或递减,通常是一维数组,用于寻找任一个元素的右边/左边第一个比自己大/小的元素的位置
深度优先搜索DFS:深搜三部曲
void dfs(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本节点所连接的其他节点) {
处理节点;
dfs(图,选择的节点); // 递归
回溯,撤销处理结果
}
}
广度优先搜索BFS:用队列记录同层节点,加入队列就需要标记,防止重复访问
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。