代码随想录刷题笔记个人向

栈与队列

4.括号匹配()[] {}

括号匹配这种具有对称性的问题适合先进后出的栈数据结构。有两种思路

1,左括号入栈遇到右括号逐一比较栈顶元素是否匹配(三种对应情况),匹配则pop,不匹配则false

※2,遇到左括号,入栈相应的右括号,遇到右括号则只需要判断是否与栈顶相同(一种情况),匹配则pop,不匹配则false

5.删除字符串中所有相邻重复项

abbaca -> ca

类似括号匹配,用栈来压入字符,最后需要将栈内元素转为字符输出,注意栈先进后出特性,因此需要反转!

值得注意的地方有:

1.c++语法或逻辑若判断第一个为正确,则不进行第二个判断,因此需要先判断是否为空才能访问栈顶元素。

string removeDuplicates(string s) {
        //匹配问题用栈解决,最后需要反转
        stack<char> sta;
        for(int i = 0; i < s.size(); i++){
            if( sta.empty()||s[i] != sta.top() )  //不能写成 s[i] != sta.top()|| sta.empty() 
                sta.push(s[i]);
            else
                sta.pop();
        }
        string result = "";
        while(!sta.empty()){
            result += sta.top();
            sta.pop();
        }
        reverse(result.begin(), result.end());
        return result;
    }

2.可以直接用字符串做栈,省去转换为字符串并进行反转的操作

  1. push_back(char c):将字符 c 添加到字符串的末尾,即相当于栈的入栈操作,将元素压入栈顶。

  2. pop_back():从字符串的末尾删除一个字符,即相当于栈的出栈操作,将栈顶元素弹出。

  3. back() 函数是用来访问字符串的最后一个字符的,即栈顶元素。得到的 的  

string removeDuplicates(string S) {
        //用字符串直接作为栈
        string result;
        for(char s : S){
            if( result.empty() || s!= result.back() )
                result.push_back(s);
            else
                result.pop_back();
        }
        return result;
    }

 6.逆波兰表达式求值

栈的应用。本题后台数据为long long类型

tokens[i] 是一个运算符("+""-""*" 或 "/"),或是在范围 [-200, 200] 内的一个整数

※需要注意的是判断数字和判断运算符的逻辑。显然,判断为运算符较简单。

判断数字:bool isdigit()函数

for(string s : tokens){//使用string类型来遍历vector<string>容器

   //判断是否为数字isdigit():字符串开头是数字或者负号开头,后面跟数字 则为一个数字
    if(isdigit(s[0]) || ( s.size() > 1 && s[0] == '-' && isdigit(s[1]) )) 
       
        nst.push(stoll(s)); //stoll()函数将字符串转换为长整型数字

判断运算符:

 for(string s : tokens){//使用string类型来遍历vector<string>容器
   
      if(s == "+" || s == "-" || s == "*" || s == "/") {  //判断是否为运算符

※另外,可以用switch代替if-else分支.

switch()里面可以是整型或能隐式转换为整型的类型。case后面跟随相应的整型或者字符常量

 if(s == "+" || s == "-" || s == "*" || s == "/") {
                long long rnum = nst.top();
                nst.pop();
                long long lnum = nst.top();
                nst.pop();
                switch(s[0]){  //s本身是一个字符串,不是字符常量,不能直接用于switch语句
                    case '+' :    //case后面需要跟随整数或字符常量,而不是字符串"+"
                        nst.push(lnum + rnum); 
                        break;
                    case '-' :
                        nst.push(lnum - rnum); 
                        break;
                    case '*' :
                        nst.push(lnum * rnum); 
                        break;
                    case '/' :
                        nst.push(lnum / rnum); 
                        break;
                    default: 
                        break;

7.滑动窗口最大值

滑动窗口是队列的典型应用,可以选择单调队列(递增或者递减)来实现随着滑动窗口移动找出每个窗口的最大值。

(c++的STL库里stack的底层可以是vector,list,deque。常用的SGI STL栈和队列的缺省情况下底层结构都用deque实现) deque:双向队列

nums = [1,3,-1,-3,5,3,6,7], k = 3
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

解决思路:如果实现一个单调递减的队列,每次push(int value)(模拟窗口前移时进入新元素)后都保持让队头元素=窗口最大元素、pop(int value)(模拟窗口前移时去除旧元素),在窗口移动完成后输出队头元素则可以解决问题。

以类的形式实现单调队列

class MyQueue {
    public:
        deque<int> que; //用deque来实现单调递减队列
    //弹出前判断队列是否为空、
    //窗口后挪移除元素时,看是否弹出值等于队头元素如果是则弹出,需要重新选择最大的
        void pop(int value){
            if(!que.empty() && value == que.front())
                que.pop_front();
        }

    //若push的数值大于当前队尾元素,则弹出队列元素直到push的值小于等于队尾元素
        void push(int value){
            while(!que.empty() && value > que.back())
                que.pop_back();
            que.push_back(value);
        }
    //得到队头(最大)元素
        int getfront(){
            return que.front();
        }
    };

滑动窗口使用:

for (int i = k; i < nums.size(); i++) {
    que.pop(nums[i - k]); // 滑动窗口移除最前面元素
    que.push(nums[i]); // 滑动窗口前加入最后面的元素
    result.push_back(que.front()); // 记录对应的最大值
        }

8.前k个高频元素

……下次再写罢

二叉树

1.二叉树的递归遍历

递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,

※递归三要素:1.上下层需要传递的参数和函数返回值 2.终止条件 3.每层都需要做的操作

二叉树传递参数一般为节点指针,此题需要将访问元素输入到数组故增加结果数组。

终止条件一般为指针为空:①if (cur == NULL) return;

②访问左节点:traversal(cur->left, vec);

③访问右节点:traversal(cur->right, vec);

④对中间节点的处理:vec.push_back(cur->val);

先序:①④②③;中序:①②④③;后序遍历:①②③④

2.二叉树的迭代遍历

迭代遍历主要用到栈结构。

先序遍历根左右,访问顺序就是处理顺序。后序遍历可以用(根左右)—调整栈访问顺序—(根右左)—reverse(result数组)—(左右根)实现。

但是,中序遍历左根右逻辑不同,访问顺序不是处理顺序,增加TreeNode*t 指针进行访问压栈,退栈时对节点进行处理(输出)。

class Solution {
public:
//先访问,出栈的时候再处理
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        TreeNode* t = root;//创建一个t指针
        while(!st.empty() || t != nullptr){ //所要遍历的节点没完或者栈内数据未处理完
            if(t != nullptr){ //访问节点非空
                st.push(t); //则将访问节点入栈
                t = t->left; //改为访问当前节点左孩子
            }
            else{ //访问节点已空
                t = st.top(); //栈里要弹出的数据,就是要处理的节点
                st.pop();
                result.push_back(t->val); //中
                t = t->right;  //修改当前指针为处理节点右孩子
            } 
        }
        return result;
    }
};

3.二叉树的层序遍历

 利用队列这种数据结构。树的层序遍历相当于图的广度优先遍历

1.非递归的实现并不复杂

vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> result;
        queue<TreeNode*> que;
        if(root == nullptr) return result;
        que.push(root);
        while(!que.empty()){
        //每处理完一层进队列的节点个数等于每组需要输出的节点个数
            int size = que.size();
            vector<int> vec;
            for(int i = 0; i < size; i++){ //不能用 que.size()是因为其在循环里会改变
                TreeNode* node = que.front();
                que.pop();
                vec.push_back(node->val) ;
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);   
            }
            result.push_back(vec); 
        }
        return result;
    }

2.递归实现

//队列的应用,递归法
    //depth 表示当前节点所在的层数,一层一层来遍历
    void order(TreeNode* t, vector<vector<int>>& res,int depth){
        if(t == nullptr) return;
       /* res.size() 等于depth,说明当前层还没有被添加到结果数组中,
        需要先将一个空的向量 vector<int>() 添加到结果数组中,以便存储当前层的节点值。*/
       if(res.size() == depth) res.push_back(vector<int>());
        res[depth].push_back(t->val);
        order(t->left, res, depth + 1);
        order(t->right, res, depth + 1);
    }
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> result;
        int depth = 0;//为了适应空树,depth从0开始
        order(root, result, depth);
        return result;
    }

  • 24
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值