力扣K神图解算法数据结构解析10

力扣K神图解算法数据结构点这里

十、分治算法

  1. 剑指07,重建二叉树

    //时间O(n),空间O(n)
    //自己一直觉得这道题很难,没想到还是能够拿下,其实理论也清楚,前序遍历和中序遍历
    //关键如下
    //1.recur递归参数的确定,根节点在前序遍历中的索引,子树在中序遍历中的左边界和右边界
    //2.关于中序遍历哈希表的建立,空间换时间,使得查询为O(1)
    //3.常规递归基本操作,先写出整个程序框架,然后慢慢补充
    class Solution {
    public:
        TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) 
        {
            for(size_t i=0;i<inorder.size();++i)
            {
                mm[inorder[i]] = i;
            }
            return recur(preorder,inorder,0,0,inorder.size()-1);
        }
    private:
        unordered_map<int,int> mm;
        
        TreeNode* recur(vector<int>& preorder, vector<int>& inorder,
        int prenode,int inleft,int inright)
        {
            if(inleft > inright) return nullptr;
            TreeNode* root = new TreeNode(preorder[prenode]);
            int temp = mm[preorder[prenode]];
            root->left = recur(preorder,inorder,prenode+1,inleft,temp-1);
            root->right = recur(preorder,inorder,prenode+1+temp-inleft,temp+1,inright);
            return root;
        }
    };
    
  2. 剑指16,数值的整数次方

    //时间O(logn),空间O(1)
    //快速幂求余时间复杂度为O(logn),循环求余时间复杂度为O(n)
    //关键点就是学会快速幂求余,没啥好说的,整个剑指里面一共有两道快速幂,解法完全一样
    class Solution {
    public:
        double myPow(double x, int n) 
        {
            if(!n || x == 1) return 1;
            int a = abs(n);
            double res = 1;
            while(a > 0)
            {
                if(a % 2) res *= x;
                x = x*x; 
                a /= 2;
            }
            if(n > 0) return res;
            else if(n < 0) return 1.0/res;
            return -1;
        }
    };
    
  3. 剑指17,打印从1到最大的n位数

    //时间O(10^n),空间O(1)
    //此题进阶部分需要考虑大数越界问题,当然此部分没有考虑
    class Solution {
    public:
        vector<int> printNumbers(int n) 
        {
            vector<int> res;
            for(int i=1;i<pow(10,n);++i)
            {
                res.push_back(i);
            }
            return res;
        }
    };
    
  4. 剑指33,二叉搜索树的后序遍历序列

    //时间O(n^2),空间O(n)
    //关键点如下
    //1.后序遍历,最后一个值是根节点,且整个序列按左,右,根排序
    //2.二叉搜索树,左子树均小于根节点,右子树均大于根节点
    //3.确定好递归参数,整个过程按照递归标准流程来就好,可以先写框架
    class Solution {
    public:
        bool verifyPostorder(vector<int>& postorder) 
        {
            return recur(postorder,0,postorder.size()-1);
        }
    private:
        bool recur(vector<int>& postorder,int left,int right)
        {
            if(left >= right) return true;
            int mid;
            //从左往右找右子树的第一个索引,同时也证明左子树其值均小于根节点
            for(int i=left;i<right;++i)
            {
                if(postorder[i] > postorder[right])
                {
                    mid = i;
                    break;
                }
            }
            //验证右子树其值均大于根节点,否则返回false
            for(int i=mid;i<right;++i)
            {
                if(postorder[i] < postorder[right]) return false;
            }
            //递归调用
            return recur(postorder,left,mid-1) && recur(postorder,mid,right-1);
        }
    };
    
    //单调栈方法没看懂,理论上其比递归快,但递归容易想到,且通用
    //时间O(n),空间O(n)
    //略
    
  5. 剑指51,数组中的逆序对

    //时间O(logn),空间O(n)
    //此题本质上是归并排序,逆序对只是在最后“治”的过程中统计了一下而已
    //标准递归,算是二叉搜索树中的后序遍历
    //关键点
    //1.分,二分法,分到单个元素停止
    //2.治,先将范围内的元素拷贝一份,原容器用于容纳排序后元素,然后三指针合并
    //  同时统计逆序对,注意等于不算逆序对
    class Solution {
    public:
        int reversePairs(vector<int>& nums) 
        {
            vector<int> tmp(nums.size());
            msort(nums,tmp,0,nums.size()-1);
            return cnt;
        }
    private:
        int cnt = 0;
        void msort(vector<int>& nums, vector<int>& tmp, int left, int right)
        {
            //分
            if (left >= right) return;
            int mid = left + (right - left) / 2;
            msort(nums, tmp, left, mid);
            msort(nums, tmp, mid+1, right);
            //拷贝
            for (int i = left; i <= right; ++i)
            {
                tmp[i] = nums[i];
            }
           	//治
            int i = left;
            int j = mid + 1;
            int k = left;
            while (k <= right)
            {
                if (i >= mid + 1)	nums[k++] = tmp[j++];
                else if (j >= right + 1) nums[k++] = tmp[i++];
                else if(tmp[i] <= tmp[j]) nums[k++] = tmp[i++];
                else 
                {
                    cnt += mid - i + 1;
                    nums[k++] = tmp[j++];
                }
            }
        }
    };
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值