二叉树+字符串+回溯

179 阅读4分钟

二叉树

654.最大二叉树

和之前根据 前中 、 中后构造二叉树是一样的思路
不要重新构造vector,用下表来解决。 注意 遇到如下情况应该是数组越界

image.png

617.合并二叉树

递归 中左右 迭代 和之前判断二叉树是否对称的写法类似

700.二叉搜索树中的搜索

二叉搜索树性质

98.验证二叉搜索树

注意 需要 左子树所有节点 < 中间 < 右子树所有节点
如下代码过不了这种情况

image.png

bool isValidBST(TreeNode* root) { 
if(root == nullptr) return true; 
if(root->left && root->val <= root->left->val) return false; 
if(root->right && root->val >= root->right->val) return false; 
return isValidBST(root->left) && isValidBST(root->right); }

需要记录左树最大值

530.二叉搜索树的最小绝对差

二叉搜索树的性质,可以转换为一个有序的数组,
或可以遍历的过程记录pre和root

501.二叉搜索树中的众数

与上一题类似,因为二叉搜索树的中序遍历是一个有序的数组
1.全部塞入数组,最后遍历数组
2.用pre来记录上一个节点,cur不等cout置为1,等就++,最后count与maxcount比较,

    int maxcount = 0;
    int count = 0;
    TreeNode* pre = nullptr;
    void traversal(TreeNode* cur,vector<int> &result){
        if(cur == nullptr) return;
        if(cur->left) traversal(cur->left, result);   //左
        
        //中
        if(pre && cur->val == pre->val) count++; //相等++
        else count = 1;
        if(count == maxcount) result.push_back(cur->val); //相等放入结果集
        if(count > maxcount){
            maxcount = count;
            result.clear();                      //之前的结果均不满足,清除
            result.push_back(cur->val);
        }
        pre = cur;
        
        if(cur->right) traversal(cur->right, result); //右
    }

236. 二叉树的最近公共祖先

后序遍历 左右中就是天然的回溯 有返回值,就需要left,right分开写

235. 二叉搜索树的最近公共祖先

二叉搜索树有序,相当于有方向,只要满足[p,cur,q],cur就是最近祖先

701.二叉搜索树中的插入操作

不考虑重构,找到空位置直接插入

450.删除二叉搜索树中的节点

key只有一个子树,直接返回
key两个子树都有,key->left插入到key->right最左边的子树上

669. 修剪二叉搜索树

巧用递归,搞清楚 不在区间内的情况:cur->val<low向上给递归(右树);cur->val > high,向上给递归(左树)。

108.将有序数组转换为二叉搜索树

与前序中序vector构造二叉树,注意区间

538.把二叉搜索树转换为累加树

递归:右中左遍历,利用pre做累加

124. 二叉树中的最大路径和

1.递归传递值 MAX(左,右)+中
2.max保存本节点的值 左+右+中

哈希表

454.四数相加II

与两数之和类似
1.map的key存A+B,value存次数
2.最后在map里找(0-(B+C))存在就count+=value

383. 赎金信

由于只有小写字符,直接用数组做哈希比较好,unordered_map开销比较大。

15. 三数之和

真滴难,卡尔哥给的方法非常巧妙 双指针重在去重 a+b+c = 0

  1. a 去重注意nums[i] == nums[i+1]nums[i] == nums[i-1]区别 前者直接漏掉[-1,-1,2]
  2. bc去重注意位置,一定是已经得到结果的地方 开始就去重会少值

18. 四数之和

与上一题类似,就是不停去重, 1.用例[2,2,2,2,2] 8检查去重条件对不对 2.用例[1000000000,1000000000,1000000000,1000000000] 0,需要(long)

字符串

344.反转字符串

只用swap

541. 反转字符串II

在上一题基础上每次移动 i += 2*k

题目:剑指Offer 05.替换空格

不用额外的辅助空间,先扩充,再分两个指针,从old和new的末尾分别遍历

151.翻转字符串里的单词

不用额外的辅助空间, 1.去掉多余空格,逻辑和27移除元素一致 2.反转 3.每个单词再反转

题目:剑指Offer58-II.左旋转字符串

利用反转的办法

28. 实现 strStr()

KMP,该来的还是来了,主要是next数组的写法
next[i]表示最长相等前缀后缀长度,利用next[j-1]进行回退

459.重复的子字符串

同样利用KMP算法,如果 next[len-1] != 0 有最长相等前缀后缀 && len % (len-next[len-1]) == 0 除的尽

回溯

回溯就是递归,回溯本质是暴力破解

思路

T backtracking(args) {
    if (con) {
        结尾;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        //有时需要先按照条件处理
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

注意T一般是void,遇到找见一条可行路径就返回的用bool

组合

77. 组合

回溯展开 优化:剪枝,剪枝没看懂

216.组合总和III

与77类似,增加了一个条件 剪枝也类似i <= n - (k - path.size()) + 1

17.电话号码的字母组合

1.先整一个const letterMap存数据
2.多组集合求组合

39. 组合总和

本题组合中的内容可以重复,迭代不用+1

40.组合总和II

难点在去重,去掉的是同一层的重,不去深度上的重 方案有二:\

  1. 利用一个全部初始化为false的used[]来标注,同一层的话为false,深度上为true,利用used源数组需要排序\
  2. 利用index

分割

131.分割回文串

基本思想一样,也是按照树形去划分
每次分割一段就看符不符合要求

93.复原IP地址

子集

78.子集

直接push到result数组里

90.子集II

不能重复,同### 40.组合总和II用used,原数组需要排序

491.递增子序列

不能重复且需要按需递增(不能排序),每一层用set来做

排列

46.全排列

全局used

47.全排列 II

再次考虑去重问题,排序,用used来做,有所不同的是这次 used[i-1] == true (剪去竖向)还是 used[i-1] == false (剪去横向)都能过

棋盘

51. N皇后

单层循环,主要是条件函数的书写,顺序是列、左上45度方向,左下135度方向判断

37. 解数独

多层循环,1.循环2.判断条件
找起始位置的方法应该是通用的:一段数组,每n(这里是3)个一组,给定任意的i,寻找离他最近分组的起始位置

int start = (i/3)*3;

332.重新安排行程

难在用unordered_map<string,map<string,int>> 即按照出发地字母有序的unordered_map<出发地,map<目的地,票数>>解决问题