十、分治算法
-
剑指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; } };
-
剑指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; } };
-
剑指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; } };
-
剑指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) //略
-
剑指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++]; } } } };