《剑指Offer》C++实现-Week4

46. 二叉搜索树的后序遍历序列

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。

如果是则返回true,否则返回false。

假设输入的数组的任意两个数字都互不相同。

样例
输入:[4, 8, 6, 12, 16, 14, 10]

输出:true

class Solution {
public:
    
    vector<int> seq;

    bool verifySequenceOfBST(vector<int> sequence) {
        seq = sequence;
        return dfs(0, seq.size() - 1);
    }
    
    bool dfs(int l, int r)
    {
        if(l >= r) return true;
        int root = seq[r];
        int k = l;
        while(k < r && seq[k] < root) k ++;
        for(int i = k; i < r; i ++)
            if(seq[i] < root)
                return false;
        return dfs(l, k - 1) && dfs(k, r - 1);        
    }
};

47. 二叉树中和为某一值的路径

输入一棵二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。

从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。

样例
给出二叉树如下所示,并给出num=22。

      5
     / \
    4   6
   /   / \
  12  13  6
 /  \    / \
9    1  5   1

输出:[[5,4,12,1],[5,6,6,5]]

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:

    vector<vector<int>> ans;
    vector<int> path;

    vector<vector<int>> findPath(TreeNode* root, int sum) {
        dfs(root, sum);
        return ans;
    }
    
    void dfs(TreeNode *root, int sum)
    {
        if(!root) return;
        path.push_back(root->val);
        sum -= root->val;
        if(!root->left && !root->right && !sum) ans.push_back(path);
        dfs(root->left, sum);
        dfs(root->right, sum);
        path.pop_back();
    }
};

48. 复杂链表的复刻

请实现一个函数可以复制一个复杂链表。

在复杂链表中,每个结点除了有一个指针指向下一个结点外,还有一个额外的指针指向链表中的任意结点或者null。

注意:

函数结束后原链表要与输入时保持一致。

/**
 * Definition for singly-linked list with a random pointer.
 * struct ListNode {
 *     int val;
 *     ListNode *next, *random;
 *     ListNode(int x) : val(x), next(NULL), random(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *copyRandomList(ListNode *head) {
        //先复制一遍,接在原节点后面
        for(auto p = head; p;)
        {
            auto np = new ListNode(p->val);
            auto next = p->next;
            p->next = np;
            np->next = next;
            p = next;
        }
        
        for(auto p = head; p; p = p->next->next)
        {
            if(p->random) p->next->random = p->random->next;
        }
        
        auto dummy = new ListNode(-1);
        auto cur = dummy;
        for(auto p = head; p; p = p->next)
        {
            cur->next = p->next;
            cur = cur->next;
            p->next = p->next->next;
        }
        return dummy->next;
    }
};

49. 二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。

要求不能创建任何新的结点,只能调整树中结点指针的指向。

注意:

需要返回双向链表最左侧的节点。
例如,输入下图中左边的二叉搜索树,则输出右边的排序双向链表。

图----------------------------------

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* convert(TreeNode* root) {
        if(!root) return NULL;
        auto sides = dfs(root);
        return sides.first; //返回pair first 即左子树最左节点 second 左子树最右节点
    }
    
    pair<TreeNode*, TreeNode*> dfs(TreeNode *root)
    {
        if(!root->left && !root->right) return {root, root};//只剩一个叶子节点
        if(root->left && root->right)
        {
            auto lsides = dfs(root->left), rsides = dfs(root->right);
            lsides.second->right = root, root->left = lsides.second;
            rsides.first->left = root, root->right = rsides.first;
            return {lsides.first, rsides.second};
        }
        if(root->left)
        {
            auto lsides = dfs(root->left);
            lsides.second->right = root, root->left = lsides.second;
            return {lsides.first, root};
        }
        if(root->right)
        {
            auto rsides = dfs(root->right);
            rsides.first->left = root, root->right = rsides.first;
            return {root, rsides.second};
        }
    }
    
};

50. 序列化二叉树

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

您需要确保二叉树可以序列化为字符串,并且可以将此字符串反序列化为原始树结构。

样例
你可以序列化如下的二叉树

    8
   / \
  12  2
     / \
    6   4

为:"[8, 12, 2, null, null, 6, 4, null, null, null, null]"
注意:

以上的格式是AcWing序列化二叉树的方式,你不必一定按照此格式,所以可以设计出一些新的构造方式。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:

    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        string res;
        dfs_s(root, res);
        //cout << res << endl;
        return res;
    }
    
    void dfs_s(TreeNode *root, string &res)
    {
        if(!root)
        {
            res += "null ";
            return;
        }
        res += to_string(root->val) + ' ';
        dfs_s(root->left, res);
        dfs_s(root->right, res);
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        int u = 0;
        return dfs_d(data, u);
    }
    
    TreeNode* dfs_d(string data, int &u)
    {
        if(u == data.size()) return NULL;
        int k = u;
        while(data[k] != ' ') k ++;
        if(data[u] == 'n')
        {
            u = k + 1;
            return NULL;
        }
        int val = 0, flag = 0; //增加对负数的判断
        for(int i = u; i < k; i++)
        {
            if(data[i] == '-') //负号做个标记
            {
                flag = 1;
                continue;
            }
            val = val * 10 + data[i] - '0';
            
        } 
        u = k + 1;
        if(flag) val = -val;
        auto root = new TreeNode(val);
        root->left = dfs_d(data, u);
        root->right = dfs_d(data, u);
        return root;
    }
    
};

51. 数字排列

输入一组数字(可能包含重复数字),输出其所有的排列方式。

样例
输入:[1,2,3]

输出:

      [
        [1,2,3],
        [1,3,2],
        [2,1,3],
        [2,3,1],
        [3,1,2],
        [3,2,1]
      ]
class Solution {
public:

    vector<vector<int>> ans;
    vector<int> path;

    vector<vector<int>> permutation(vector<int>& nums) {
        path.resize(nums.size());
        sort(nums.begin(), nums.end());
        dfs(nums, 0, 0, 0);
        return ans;
    } 
    
    void dfs(vector<int> &nums, int u, int start, int state)
    {
        if(u == nums.size())
        {
            ans.push_back(path);
            return;
        }
        if(!u || nums[u] != nums[u - 1]) start = 0;
        for(int i = start; i < nums.size(); i ++)
        {
            if(!(state >> i & 1)) //如果第 i 位不是1
            {
                path[i] = nums[u];
                dfs(nums, u + 1, i + 1, state + (1 << i));
            }
        }
    }
    
};

52. 数组中出现次数超过一半的数字

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

假设数组非空,并且一定存在满足条件的数字。

思考题:

假设要求只能使用 O(n) 的时间和额外 O(1) 的空间,该怎么做呢?
样例
输入:[1,2,1,1,3]

输出:1

class Solution {
public:
    int moreThanHalfNum_Solution(vector<int>& nums) {
        int cnt = 0, val = -1;
        for(auto x : nums)
        {
            if(!cnt) val = x, cnt = 1;
            else
            {
                if(x == val) cnt ++;
                else cnt --;
            }
        }
        return val;
    }
};

53. 最小的k个数

输入n个整数,找出其中最小的k个数。

注意:

数据保证k一定小于等于输入数组的长度;
输出数组内元素请按从小到大顺序排序;
样例
输入:[1,2,3,4,5,6,7,8] , k=4

输出:[1,2,3,4]

class Solution {
public:
    vector<int> getLeastNumbers_Solution(vector<int> input, int k) {
        priority_queue<int> heap; //维护一个大根堆
        for(auto x : input)
        {
            heap.push(x);
            if(heap.size() > k) heap.pop();
        }
        
        vector<int> res;
        while(heap.size()) res.push_back(heap.top()), heap.pop();
        
        reverse(res.begin(), res.end());
        return res;
    }
};

54. 数据流中的中位数

如何得到一个数据流中的中位数?

如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。

如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

样例
输入:1, 2, 3, 4

输出:1,1.5,2,2.5

解释:每当数据流读入一个数据,就进行一次判断并输出当前的中位数。

class Solution {
public:
    
    priority_queue<int> max_heap; //大根堆
    priority_queue<int, vector<int>, greater<int>> min_heap; //小根堆

    void insert(int num){
        max_heap.push(num);
        if(min_heap.size() && max_heap.top() > min_heap.top())
        {
            auto maxv = max_heap.top(), minv = min_heap.top();
            max_heap.pop(), min_heap.pop();
            max_heap.push(minv), min_heap.push(maxv);
        }
        
        if(max_heap.size() > min_heap.size() + 1)
        {
            min_heap.push(max_heap.top());
            max_heap.pop();
        }
        
    }

    double getMedian(){
        if(max_heap.size() + min_heap.size() & 1) return max_heap.top();
        return (max_heap.top() + min_heap.top()) / 2.0;
    }
};

55. 连续子数组的最大和

输入一个 非空 整型数组,数组里的数可能为正,也可能为负。

数组中一个或连续的多个整数组成一个子数组。

求所有子数组的和的最大值。

要求时间复杂度为O(n)。

样例
输入:[1, -2, 3, 10, -4, 7, 2, -5]

输出:18

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int sum = 0, res = INT_MIN;
        for(auto x : nums)
        {
            if(sum > 0) sum += x;
            else sum = x;
            res = max(res, sum);
        }
        return res;
    }
};

56. 从1到n整数中1出现的次数

输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。

例如输入12,从1到12这些整数中包含“1”的数字有1,10,11和12,其中“1”一共出现了5次。

样例
输入: 12
输出: 5

class Solution {
public:
    int numberOf1Between1AndN_Solution(int n) {
        if(!n) return 0;
        vector<int> number;//取出每一位数字
        while(n) number.push_back(n % 10), n /= 10;
        int res = 0;
        for(int i = number.size() - 1; i >= 0; i --) //从最高位开始枚举
        {
            auto left = 0, right = 0, t = 1; //取出左边数字和右边数字
            for(int j = number.size() - 1; j > i; j --) left = left * 10 + number[j];
            for(int j = i - 1; j >= 0; j --) right = right * 10 + number[j], t *= 10;
            
            res += left * t;
            if(number[i] == 1) res += right + 1;
            else if(number[i] > 1) res += t;
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值