剑指offer61-67

2020.2 刷题笔记

剑指offer https://www.nowcoder.com/ta/coding-interviews?page=1

61-67

  1. 序列化二叉树
  2. 二叉搜索树的第k个结点
  3. 数据流的中位数
  4. 滑动窗口的最大值
  5. 矩阵中的路径
  6. 机器人的运动范围
  7. 剪绳子

  1. 序列化二叉树

    请实现两个函数,分别用来序列化和反序列化二叉树。

    二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 !表示一个结点值的结束(value!)。

    二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。

    /*
    struct TreeNode {
        int val;
        struct TreeNode *left;
        struct TreeNode *right;
        TreeNode(int x) :
                val(x), left(NULL), right(NULL) {
        }
    };
    */
    class Solution {
    public:
        char* Serialize(TreeNode *root) {    
            string str = "";
            queue q;
            q.push(root);
            for (bool flag = true;flag;) {
                flag = false;
                int count = q.size();
                for (int i = 0;i < count;++i) {
                    TreeNode* now = q.front();
                    q.pop();
                    if (now) {
                        if (now->left || now->right) flag = true;
                        q.push(now->left);
                        q.push(now->right);
                        str += to_string(now->val) + "!";
                    }
                    else {
                        q.push(NULL);
                        q.push(NULL);
                        str += "#!";
                    }
                }
            }
            char* cstr = new char[(int)str.size() + 1];
            strcpy(cstr, str.c_str());
            return cstr;
        }
        TreeNode* Deserialize(char *str) {
            string sstr = str;
            delete[] str;
            if (sstr == "#!") return NULL;
            vector nodes;
            int pos = sstr.find('!', 0), pre = 0;
            for (;pos != string::npos;pre = pos + 1, pos = sstr.find('!', pre)) {
                string tmp = sstr.substr(pre, pos - pre);
                if (tmp != "#") {
                    TreeNode* node = new TreeNode(atoi(tmp.c_str()));
                    nodes.push_back(node);
                    int size = nodes.size();
                    if (size > 1) {
                        if (size % 2 == 0) nodes[size / 2 - 1]->left = nodes[size - 1];
                        else nodes[size / 2 - 1]->right = nodes[size - 1];
                    }
                }
                else {
                    TreeNode* node = new TreeNode(0);
                    nodes.push_back(node);
                }
            }
            return nodes[0];
        }
    };
    

    我用的是层序遍历

  2. 二叉搜索树的第k个结点

    给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。

    /*
    struct TreeNode {
        int val;
        struct TreeNode *left;
        struct TreeNode *right;
        TreeNode(int x) :
                val(x), left(NULL), right(NULL) {
        }
    };
    */
    class Solution {
    public:
        TreeNode* KthNode(TreeNode* pRoot, int k) {
            TreeNode* kNode = NULL;
            FindKthNode(pRoot, kNode, k);
            return kNode;
        }
        void FindKthNode(TreeNode* now, TreeNode*&kNode, int&k) {
            if (now == NULL || k <= 0) return;
            FindKthNode(now->left, kNode, k);
            if (--k == 0) kNode = now;
            if (k > 0) FindKthNode(now->right, kNode, k);
        }
    };
    

    中序遍历二叉搜索树得到的就是非递减的序列

  3. 数据流中的中位数

    如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

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

    另解:大顶堆和小顶堆,与PAT甲级-Stack类似:https://www.nowcoder.com/discuss/422

  4. 滑动窗口的最大值

    给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个:{[2,3,4],2,6,2,5,1},{2,[3,4,2],6,2,5,1},{2,3,[4,2,6],2,5,1},{2,3,4,[2,6,2],5,1},{2,3,4,2,[6,2,5],1},{2,3,4,2,6,[2,5,1]}。

    一、暴力穷举:

    class Solution {
    public:
        vector maxInWindows(const vector& num, unsigned int size) {
            vector maxs;
            if (!size) return maxs;
            for (int i = 0;i + size <= num.size();++i) {
                int MAX = 0x80000000;
                for (int j = 0;j < size && j + i < num.size();++j) MAX = max(MAX, num[i + j]);
                maxs.push_back(MAX);
            }
            return maxs;
        }
    };
    

    二、multiset:

    class Solution {
    public:
        vector maxInWindows(const vector& num, unsigned int size) {
            vector maxs;
            if (!size || size > num.size()) return maxs;
            multiset> now;
            for (int i = 0;i < size;++i) now.insert(num[i]);
            maxs.push_back(*now.begin());
            for (int i = size;i < num.size();++i) {
                now.erase(now.find(num[i - size]));
                now.insert(num[i]);
                maxs.push_back(*now.begin());
            }
            return maxs;
        }
    };
    
  5. 矩阵中的路径

    请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。例如 a b c e s f c s a d e e 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

    class Solution {
    public:
        bool hasPath(char* matrix, int rows, int cols, char* str) {
            if (*matrix == '\0') return false;
            if (*str == '\0') return true;
            visit.resize(rows, vector(cols, false));
            for (int i = 0;i < rows;++i) {
                for (int j = 0;j < cols;++j) {
                    if (matrix[i * cols + j] == str[0]) {
                        visit[i][j] = true;
                        if (Has(i, j, 1, matrix, rows, cols, str)) return true;
                        visit[i][j] = false;
                    }
                }
            }
            return false;
        }
        bool Has(int i, int j, int now, char* matrix, int rows, int cols, char* str) {
            if (str[now] == '\0') return true;
            const int di[] = {0, 1, 0, -1}, dj[] = {1, 0, -1, 0};
            for (int k = 0;k < 4;++k) {
                int ii = i + di[k], jj = j + dj[k];
                if (ii < 0 || ii >= rows || jj < 0 || jj >= cols) continue;
                if (!visit[ii][jj] && matrix[ii * cols + jj] == str[now]) {
                    visit[ii][jj] = true;
                    if (Has(ii, jj, now + 1, matrix, rows, cols, str)) return true;
                    visit[ii][jj] = false;
                }
            }
            return false;
        }
        vector > visit;
    };
    
  6. 机器人的运动范围

    地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

    class Solution {
    public:
        int movingCount(int threshold, int rows, int cols) {
            vector > visit(rows, vector(cols, false));
            const int di[] = {0, 1}, dj[] = {1, 0}; // 向右,向下
            int MAX = 1;
            threshold >= 0 ? visit[0][0] = true : --MAX;
            for (int i = 0;i < rows;++i) {
                for (int j = 0;j < cols;++j) {
                    if (!visit[i][j]) continue;
                    for (int k = 0;k < 2;++k) {
                        int ii = i + di[k], jj = j + dj[k], sum = 0;
                        if (ii < 0 || ii >= rows || jj < 0 || jj >= cols || visit[ii][jj]) continue;
                        for (int iii = ii;iii && sum <= threshold;iii /= 10) sum += iii % 10;
                        for (int jjj = jj;jjj && sum <= threshold;jjj /= 10) sum += jjj % 10;
                        if (sum <= threshold) {
                            visit[ii][jj] = true;
                            ++MAX;
                        }
                    }
                }
            }
            return MAX;
        }
    };
    

    注意和上一题不同,这题可以重复进入格子

  7. 剪绳子

    给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],…,k[m]。请问k[0]xk[1]x…xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

    输入描述:输入一个数n,意义见题面。(2 <= n <= 60)

    class Solution {
    public:
        int cutRope(int number) {
            int MAX = 1;
            for (int last = 1;last < number;++last) MAX = max(MAX, last * Cut(number - last));
            return MAX;
        }
        int Cut(int number) {
            int MAX = number;
            for (int last = 1;last < number;++last) MAX = max(MAX, last * Cut(number - last));
            return MAX;
        }
    };
    

你可能感兴趣的:(剑指offer)