2022五月算法学习日志
5.5
二叉树层序遍历
入队元素依出队元素而定
翻转二叉树
递归三步:1、确定递归函数的参数和返回值;2、确定终止条件;3、确定单层递归的逻辑。
一种递归,两种深度优先遍历迭代,一种广度优先遍历迭代。
对称二叉树
同时比较两棵树:传参传入两个指针;
迭代法,两个节点都为空:此时两节点对称,比较还没有完成;
5.6
二叉树的最大深度
二叉树的最小深度
当一个左子树为空,右不为空,这时并不是最低点。
求二叉树的最小深度和求二叉树的最大深度的差别主要在于处理左右孩子不为空的逻辑。
5.7
完全二叉树的节点个数
对于满二叉树,结点数为 2^树深度 - 1 ,注意这里根节点深度为1。
5.11
平衡二叉树
此题比较的是高度
求深度可以从上到下去查 所以需要前序遍历(中左右),而高度只能从下到上去查,所以只能后序遍历(左右中)
先用-1排除不是平衡二叉树的情况
二叉树所有路径
回溯和递归是一一对应的,有一个递归,就要有一个回溯
5.12
左叶子之和
后序遍历:为了使用左右节点返回值。
找数左下角的值
注意添加空值判断与处理;
路径的和
5.17
从中序和后序遍历序列构造二叉树的和
传入的参数是两个数组,传出的是一棵树——需要建立TreeNode;
中序切割需要后序推得的中间节点;后续切割需要中序前数组的size();
后序切割之前,应先减去最后的中间节点——使用resize函数改变数组规模;
postorder.resize(postorder.size()-1);
中序分割需要跳过中间节点,后序分割不需要
最大二叉树
函数开始要建立一个TreeNode,以递归连接成一棵树 & 满足递归条件(nums.size()==1)后及时返回。
TreeNode* node = new TreeNode(0); if(nums.size()==1){ node->val = nums[0]; return node; }
合并二叉树
t1、t2均为TreeNode*类型, t1 = t2,意为指向t2结构的内存;
迭代法没写t1不为空&&t2为空的情况,是默认此情况不用处理;
函数传入的参数是r1,但开始参与迭代的r1须命名为node1;
二叉搜索树中的搜索
搜索到目标节点就返回,就要立即return;如果不加return,就要(按函数顺序)遍历整棵树了。
5.18
验证二叉搜索树
vector有两个参数,一个是size,表示当前vector容器内存储的元素个数,一个是capacity,表示当前vector在内存中申请的这片区域所能容纳的元素个数。
搜索树里不能有相同元素
验证二叉搜索树,就相当于变成了判断一个序列是不是递增的,使用中序遍历
陷阱:二叉搜索树中,小于父节点的所有节点都在左子树上
在二叉树中常用通过两个前后指针作比较
二叉搜索树的最小绝对差
标准方法一:有序数组>>遍历得到最小差;
记录指针方法:遍历树时创建记录前一个结点的指针,选择排序;
用函数:
result = min(result,cur->val-pre->val);
二叉搜索树中的众数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z5fUeMP3-1654392697865)(C:\Users\hj\AppData\Roaming\Typora\typora-user-images\image-20220518093041053.png)]
处理完中间节点之后不要忘记右节点呀!
二叉树的最近公共祖先
条件终止判断return root而非NULL,因 root == NULL 时,等价于return NULL;
可以用栈存储比较路径,也可以用纯递归;
通过后序遍历(即:回溯)实现从底向上的遍历
5.19
二叉搜索树的最近公共祖先
迭代法简单到令人痛苦流涕
因为是搜索一条边,单层递归需要return返回值
修剪二叉搜索树
return …返回一个值
xx = fun(…) 接住这个值
5.23
在构造二叉树的时候尽量不要重新定义左右区间数组,而是用下标来操作原数组。
// 左闭右闭区间[left, right]
TreeNode* traversal(vector<int>& nums, int left, int right)
int mid = (left + right) / 2;
,这么写其实有一个问题,就是数值越界
所以可以这么写:int mid = left + ((right - left) / 2);
5.24
搜索树转累加树
记录前一个结点,初始化前一个结点在函数体外
回溯法理论
并不高效,但起码能解决一些穷举问题
可以抽象想象为一个二维结构:纵向递归,横向嵌套
没有返回值
组合问题
从1…n中取k个数的组合,可用剪枝简化问题
5.25
电话号码字母组合
将index指向的数字转为int
int digit = digits[index] - '0';
读题之后多想想,有没有啥隐藏条件/需要额外处理的特殊情况,比如上一题,输入空格怎么办
#include <cstring> //不可以定义string s;可以用到strcpy等函数 using namespace std; #include <string> //可以定义string s;可以用到strcpy等函数 using namesapce std; #include <string.h> //不可以定义string s;可以用到strcpy等函数 1)文件cstring,和string.h对应,c++版本的头文件,包含比如strcpy之类的字符串处理函数 2)文件string.h,和cstring对应,c版本的头文件,包含比如strcpy之类的字符串处理函数 3)文件string,包含std::string的定义,属于STL范畴 4)CString,MFC里的的字符串类
空(NUL)字符码值是0,而空格字符的码值是32,‘0’ 的ASCII是48,三者是完全不同的概念。
组合总和
通过给数组排序+单层递归增加判断条件来优化剪枝效率
5.27
分割回文串
节选字符串
string str = s.substr(startIndex,i-startIndex+1);
5.28
复原IP地址
递归调用时,下一层递归的startIndex要从i+2开始(因为需要在字符串中加入了分隔符
.
),同时记录分割符的数量pointNum 要 +1s.insert(s.begin() + i + 1 , '.'); // 在i的后面插入一个逗点 s.erase(s.begin() + i + 1); // 回溯删掉逗点 erase就不用指明删除内容了(-·-`;)
数字字符串转换为数字
num = num * 10 + (s[i] - '0');
开始不符合条件还是 return result,空集罢了
对const的解释:C++中const关键字的使用方法,烦透了一遍一遍的搜,总结一下,加深印象!!! - 菜鸟加贝的爬升 - 博客园 (cnblogs.com)
子集
画画图啊画画图
5.29
去重问题
主函数里要对处理数组排序(不要被题目给出的特殊的有序的测试用例迷惑)
设置初始值为false的used数组,在调用递归之前将值变为true,调用递归之后将值变为false:递归前的true用于树枝的判断,递归后的false用于回溯后树层的判断;
递增子序列
在uset中搜寻num[i]
unordered_set<int> uset; if(uset.find(num[i])!=uset.end()){...}
在 !path.empty 的情况下比较 num[i]和path.back()
find、insert是unordered_set中的函数,vector无法使用
对元素去重的set放在单层回溯逻辑中,是记录本层元素是否重复使用,新的一层uset都会重新定义(清空),所以要知道uset只负责本层!
全排列
每一层都从头循环,used定义到主函数中
全排列2
主函数里判断一下,要不要排序
区分
used[i]==false 和 used[i-1]==false