leetcode落后了一天,今天已经刷了三个题了,学习一下算法框架吧,今天彻底弄懂回溯法。
回溯法其实就是暴力搜索,但是对于回溯法来说可以在每一次循环的时候明确自己的选择,其实就是DFS,BFS广度优先其实就是每一次游走决策的顺序不一样罢了。
//回溯法一般用于搜寻所有答案
result = []
def backtrack(当前路径,可以选择的列表):
if 当前找到了答案:
result.add(路径)
return
for 选择 in 可以选择的列表:
做出选择
//主要是result和选择列表更新
backtrack(路径,选择列表)
撤销一下选择
//路径撤销,选择撤销
//分枝限界
这里值得注意的是采用了递归的方式实现了这些算法,如果是有关于树的问题,一般采用栈和队列的方式比较合理。这里聊下去就比较深入了,我们今天浅浅的解析一下全排列问题:
vector<vector<int>> permute(vector<int>& nums) {
vector<int> track; // 维护已经做出的选择
vector<bool> used(nums.size(),0); //下一步选什么
backtrack(nums,track,used);
return ans;
}
void backtrack(vector<int> &nums ,vector<int> &track ,vector<bool> &used){
if(track.size() == nums.size())
{
ans.push_back(track);
return;
}
for(int i = 0 ; i < nums.size() ; i ++)
{
if(used[i]) continue;
track.push_back(nums[i]);
used[i] = 1;
backtrack(nums,track,used);
track.pop_back();
used[i] = 0;
}
}
变形原来数组里面有重复的值,如何保证不重复元素呢?仔细思考一下,回溯法的used已经保证了本身上下之间的不重复,但是如果要保证,去除不同位置则需要在每次的选择列表上下功夫,对于每一次的选择列表进行去重。
void backtrack(vector<int> &nums ,vector<int> &track ,vector<bool> &used){
if(track.size() == nums.size())
{
ans.push_back(track);
return;
}
set<int>choice;//集合去重
for(int i = 0 ; i < nums.size() ; i ++)
{
if(!used[i]) choice.insert(nums[i]);
}
for(int i = 0 ; i < nums.size(); i ++)
{
if(used[i]) continue;
if(choice.find(nums[i]) == choice.end()) continue;
track.push_back(nums[i]);
//使用了这个数字则将其移除选择列表
choice.erase(nums[i]);
used[i] = true;
backtrack(nums,track,used);
track.pop_back();
used[i] = false;
}
}
在之前的内容中,我们了解了数据库事务的基本特性AICD,和ORM框架的基本概念至于实际操作我们在之后的学习中再涉及吧。
今天开始,我们来进入数据库调优的相关内容学习:
首先了解一下数据库的调优目标,简单来说,我们肯定是希望数据库的运行速度变得更快,数据库的并行吞吐量也更大。具体来说如何评判数据库的运行情况呢?
1)用户的反馈
2)日志分析
3)服务器资源监控
4)数据库内部情况监控
调优手段:
1)最高级别,啥也别说了换数据库吧,哈哈哈。那么这里就需要了解不同数据库之间的不同区别,这里也包括了不同的储存引擎。SQL和NoSQL,InnoDB和MyISAM等等,以及列式存储和行存储,这里数据仓库等等,需要根据业务来定。
2) 第二级别,优化表的设计。这里有三范式设计,和反范式设计的设计思想。采用合适的字段类型。
1NF:字段不可分。
2NF:非主属性对候选键有着完全依赖关系,就是说一张表不能存在两种关系。
3NF:不存在两种非间接关系。
反范式设计
主要是为了减少表之间字段冗余和提供一个标准。
3) 优化逻辑查询
记住两个例子,第一个是IN和EXISTS的区别:
IN是外表取出一个元素去查询内表,因此内表越小则越快。
EXISTS(外表里面存在这个行)是内表去查外表,外表越小越快,取出来一个内表的行数据,去外表索引查找。
第二个例子:
WHERE里面如果对于字段进行计算可能会让索引失效,就是要多使用索引。例如使用通配符而不去使用函数去拼接字符串。左匹配问题。
4)优化物理查询
加索引,如何加索引?
1. 重复度过高,索引其实没啥必要,哈希碰撞过高。
2. 不要使得查询语句让索引失效。
3. 联合索引
4. 多个索引的使用,索引过多会使得优化器对索引评估时间过多。
5)使用Redis或者Memcached作为缓存(空间局部性)
6)库级优化
读写业务分离,表分库等等
索引:
1)数据量较小的时候,索引效率一般。
2)前面说过男女不应该创建索引,但是如果有一方的数量很少,则创建索引很快就能查询。
四种索引内容:
1) 普通索引,对字段建立哈希,MySQL只支持英文。
2) 唯一索引(可以为空,数据必须唯一,不产生哈希冲突),(3) 主键索引(非空)
4) 全文索引
物理实现方式:
1)聚集索引:索引指向的数据是按照主键来排序存储数据。
2)非聚集索引: 索引指向的数据是随机存储的,非聚集索引不会把数据直接放到索引后面,而是维护了单独的索引表。叶子节点装的是主键或者聚集索引的字段。
- 聚集索引的叶子节点存储的就是我们的数据记录,非聚集索引的叶子节点存储的是数据位置。非聚集索引不会影响数据表的物理存储顺序。
- 一个表只能有一个聚集索引,因为只能有一种排序存储的方式,但可以有多个非聚集索引,也就是多个索引目录提供数据检索。
- 使用聚集索引的时候,数据的查询效率高,但如果对数据进行插入,删除,更新等操作,效率会比非聚集索引低。
联合索引以及最左匹配原则:
联合索引存在最左匹配原则,也就是按照最左优先的方式进行索引的匹配。比如刚才举例的 (x, y, z),如果查询条件是 WHERE x=1 AND y=2 AND z=3,就可以匹配上联合索引;如果查询条件是 WHERE y=2,就无法匹配上联合索引。
数据库的数据结构B+树:平衡多路搜索树
HASH冲突
- Hash 索引不能进行范围查询,而 B+ 树可以。这是因为 Hash 索引指向的数据是无序的,而 B+ 树的叶子节点是个有序的链表。
- Hash 索引不支持联合索引的最左侧原则(即联合索引的部分索引无法使用),而 B+ 树可以。对于联合索引来说,Hash 索引在计算 Hash 值的时候是将索引键合并后再一起计算 Hash 值,所以不会针对每个索引单独计算 Hash 值。因此如果用到联合索引的一个或者几个索引时,联合索引无法被利用。
- Hash 索引不支持 ORDER BY 排序,因为 Hash 索引指向的数据是无序的,因此无法起到排序优化的作用,而 B+ 树索引数据是有序的,可以起到对该字段 ORDER BY 排序优化的作用。同理,我们也无法用 Hash 索引进行模糊查询,而 B+ 树使用 LIKE 进行模糊查询的时候,LIKE 后面前模糊查询(比如 % 开头)的话就可以起到优化作用。