剑指Offer_week_3

61.序列化二叉树

image-20220919161118368

class Solution {
  private:
    string SerializeCore(TreeNode* root) {
        if (root == nullptr) {
            return "#!";	// !表示一个结点值的结束
        }

        string str;
        str = to_string(root->val) + "!";
        str += SerializeCore(root->left);
        str += SerializeCore(root->right);
        return str;
    }

    TreeNode* DeserializeCore(char*& str) {
        if (*str == '#') {
            str++;
            return nullptr;
        }

        int num = 0;
        while (*str != '!') {	// 当出现字符 ‘123!’,可以转换为int
            num = num * 10 + *str - '0';
            str++;
        }
        
        TreeNode* node = new TreeNode(num);
        node->left = DeserializeCore(++str);
        node->right = DeserializeCore(++str);
        return node;
    }

  public:
    char* Serialize(TreeNode* root) {
        string str = SerializeCore(root);
        char* res = new char[str.size()];
        for (int i = 0; i < str.size(); i++) {
            res[i] = str[i];
        }
        return res;
    }

    TreeNode* Deserialize(char* str) {
        return DeserializeCore(str);
    }
};

62.二叉搜索树的第k个节点

image-20220919162048578

class Solution {
    vector<int> vec;
public:
    void LDR(TreeNode* root) {
        if (!root) return;

        LDR(root->left);
        vec.push_back(root->val);
        LDR(root->right);
    }

    int KthNode(TreeNode* proot, int k) {
        LDR(proot);
        if (0 == k || k > vec.size()) return -1;

        return vec[k - 1];    
    }
};

63.数据流中的中位数

image-20220919163726350

使用容器,然后排序

class Solution {
public:
    vector<int> vec;
    void Insert(int num) {
        vec.push_back(num);
    }
  
    double GetMedian() { 
        sort(vec.begin(), vec.end());
        int len = vec.size();
  
        if (len % 2 == 1) {
            return double(vec[len / 2]);
        } else {
            return double(vec[len / 2] + vec[(len - 1) / 2]) / 2;
        }
    }
};

将其插入的所有数字看成一个集合,由大到小排列,然后左侧使用大顶堆,右侧使用小顶堆,这样就可以根据元素个数始终知道中位值,如果为奇数个,中位值就为大顶堆的top,如果为偶数个,中位值为大顶堆的top与小顶堆top的平均值

class Solution {
public:
    int count = 0;
    priority_queue<int, vector<int>, less<int>> left_big;         // 大顶锥, 降序排列, top指向大值,位于左侧
    priority_queue<int, vector<int>, greater<int>> right_small;   // 小顶锥, 升序排列, top指向小值,位于右侧

    void Insert(int num) {
        count++;
        if (count % 2 == 1) {
            right_small.push(num);
            left_big.push(right_small.top());
            right_small.pop();
        } else {
            left_big.push(num);
            right_small.push(left_big.top());
            left_big.pop();
        }
    }

    double GetMedian() { 
        if (count % 2 == 1) return left_big.top();
        else {
            return (left_big.top() + right_small.top()) / 2.0;
        }
    }
};

64.滑动窗口的最大值

image-20220920221358128

暴力循环

class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size) {
        int len = num.size();
        if (size > len || len == 0 || size == 0) return vector<int>();
        
        vector<int> ans;
        int cnt = len - size + 1;
        for (int i = 0; i < cnt; i++) {
            int maxNum = INT_MIN;
            for (int j = i; j < i + size; j++) {
                if (num[j] > maxNum) maxNum = num[j];
            }
            ans.push_back(maxNum);
        }
        return ans;
    }
};

双向队列

思路:

我们都知道,若是一个数字A进入窗口后,若是比窗口内其他数字都大,那么这个数字之前的数字都没用了,因为它们必定会比A早离开窗口,在A离开之前都争不过A,所以A在进入时依次从尾部排除掉之前的小值再进入,而每次窗口移动要弹出窗口最前面值,因此队首也需要弹出,所以我们选择双向队列。

具体做法:

  • step 1:维护一个双向队列,用来存储数列的下标。

  • step 2:首先检查窗口大小与数组大小。

  • step 3:先遍历第一个窗口,如果即将进入队列的下标的值大于队列后方的值,依次将小于的值拿出来去掉,再加入,保证队列是递增序。

  • step 4:遍历后续窗口,每次取出队首就是最大值,如果某个下标已经过了窗口,则从队列前方将其弹出。

  • step 5:对于之后的窗口,重复step 3,直到数组结束。

class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size) {
        int len = num.size();
        if (size > len || len == 0 || size == 0) return vector<int>();
        
        vector<int> ans;
        deque<int> dq;  // 维护一个双向队列,用来存储数列的下标
        for (int i = 0; i < len; i++) {
            // 去掉比自己先进队列的小于自己的值
            while (!dq.empty() && num[dq.back()] < num[i]) {
                dq.pop_back();
            }
            dq.push_back(i);

            // 判断队列的头部的下标是否不在窗口范围
            if (dq.front() + size <= i) {
                dq.pop_front();
            }
            
            // 判断是否形成了窗口
            if (i + 1 >= size) {
                ans.push_back(num[dq.front()]);
            }
        }
        return ans;
    }
};

65.矩阵中的路径

image-20220920230656832

DFS

class Solution {
public:
    bool DFS(vector<vector<char>>& board, char* str, int index, int x, int y, 
        vector<vector<bool>>& visited) 
    {
        if (index == strlen(str)) return true;
        if ((x < 0) || (y < 0) || (x >= board.size()) || (y >= board[0].size())) 				return false;
        if (visited[x][y]) return false;
        if (board[x][y] != str[index]) return false;
        visited[x][y] = true;

        if (DFS(board, str, index + 1, x, y - 1, visited) || //上
		DFS(board, str, index + 1, x, y + 1, visited) ||     //下
		DFS(board, str, index + 1, x - 1, y, visited) ||     //左
		DFS(board, str, index + 1, x + 1, y, visited))       //右
		return true; // 有符合要求的

        visited[x][y] = false; // 如果没有符合要求的,需要从新遍历搜索,改回false
	    return false;
    }

    bool hasPath(char* matrix, int rows, int cols, char* str)
    {
        if (str == nullptr || rows <= 0 || cols <= 0) return false;
        vector<vector<char>> board(rows, vector<char>(cols));
        
        // 将字符串装入二维数组中
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                board[i][j] = matrix[i * cols + j];
            }
        }

        vector<vector<bool>> visited(rows, vector<bool>(cols, false));
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (DFS(board, str, 0, i, j, visited) == true) return true;
            }
        }
        return false;
    }
};

66.机器人的运动范围

image-20220920232245806

class Solution {
public:
    int cnt = 0;

    int cal(int n) {
        int sum = 0;
        while (n) {
            sum += (n % 10);
            n /= 10;
        }
        return sum;
    }

    void DFS(int i, int j, int rows, int cols, 
             int threshold, vector<vector<bool> >& visited) {
        // 越界访问
        if (i < 0 || j < 0 || i >= rows || j >= cols) return;
        // 已经访问过
        if (visited[i][j]) return;
        // 不符合要求
        if (cal(i) + cal(j) > threshold) return;

        cnt += 1;
        visited[i][j] = true;

        // 上下左右
        DFS(i + 1, j, rows, cols, threshold, visited);
        DFS(i - 1, j, rows, cols, threshold, visited);
        DFS(i, j - 1, rows, cols, threshold, visited);
        DFS(i, j + 1, rows, cols, threshold, visited);
    }

    int movingCount(int threshold, int rows, int cols) {
        if (rows < 0 || cols < 0) return 0;
        vector<vector<bool>> visited(rows, vector<bool>(cols, false));
        DFS(0, 0, rows, cols, threshold, visited);
        return cnt;
    }
};

67.剪绳子

image-20220920234109974

  • 4 : 2+2

  • 5 : 2+3

  • 6 : 3+3

  • 7 : 2+2+3 或者4+3

  • 8 : 2+3+3

  • 9 : 3+3+3

  • 10:2+2+3+3 或者4+3+3

  • 分析后可以推出,最大乘积划分后 只有2和3,所以主要求出要分别划分为多少个2和3

class Solution {
public:
    int cutRope(int number) {
        if (number == 2) return 1;
        if (number == 3) return 2;

        int x = number % 3;
        int y = number / 3;

        if (x == 0) return pow(3, y);
        else if (x == 1) return 2 * 2 * pow(3, y - 1);  // 看成有两个2, y-1个3  而不是 y个3,1个1
        else return 2 * pow(3, y);      // 一个2,y个3
    }
};
class Solution {
public:
    int cutRope(int number) {
        if (number == 2) return 1;
        if (number == 3) return 2;

        int maxNum=1;
        while(number>4){
            maxNum*=3;
            number-=3;
        }
        maxNum*=number;
        return maxNum;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值