算法学习
title: 算法学习 author: zsw
算法学习动态规划剑指offer47 礼物的最大价值https://leetcode-cn.com/problems/li-wu-de-zui-da-jie-zhi-lcof/solution/mian-shi-ti-47-li-wu-de-zui-da-jie-zhi-dong-tai-gu/卡特兰数 打家劫舍 1—3打家劫舍1打家劫舍2打家劫舍3找数字规律剑指43 1~n整数中1出现的次数剑指44 数字序列中某一位的数字剑指45. 把数组排成最小的数(自定义排序)二分查找
算法学习
动态规划
剑指offer47 礼物的最大价值
在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
不同的二叉搜索树1
卡特兰数
假设n个节点存在二叉排序树的个数是G(n),令f(i)为以i为根的二叉搜索树的个数,则 G(n) = f(1) + f(2) + f(3) + f(4) + ... + f(n)G(n)=f(1)+f(2)+f(3)+f(4)+...+f(n)
当i为根节点时,其左子树节点个数为i-1个,右子树节点为n-i,则 f(i) = G(i-1)*G(n-i)f(i)=G(i−1)∗G(n−i)
综合两个公式可以得到 卡特兰数 公式 G(n) = G(0)G(n-1)+G(1)G(n-2)+...+G(n-1)*G(0)G(n)=G(0)∗G(n−1)+G(1)∗(n−2)+...+G(n−1)∗G(0)
打家劫舍 1—3
打家劫舍1
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
dp 方程 dp[i] = max(dp[i-2]+nums[i], dp[i-1])
int rob(vector<int>& nums) { if (nums.empty()) { return 0; } int size = nums.size(); if (size == 1) { return nums[0]; } vector<int> dp = vector<int>(size, 0); dp[0] = nums[0]; dp[1] = max(nums[0], nums[1]); for (int i = 2; i < size; i++) { dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]); } return dp[size - 1]; }
打家劫舍2
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
把环拆成两个队列,一个是从0到n-1,另一个是从1到n,然后返回两个结果最大的。
int rob(vector<int>& nums) { if(nums.size()==0) return 0; if(nums.size()==1) return nums[0]; vector<int> dp1(nums.size()); vector<int> dp2(nums.size()); dp1[0] = nums[0]; //dp1偷第一间 dp2不偷第一间 dp1[1] = max(nums[1],dp1[0]); dp2[0] = 0; dp2[1] = nums[1]; for(int i=2;i<nums.size();++i) { dp1[i] = max(dp1[i-1],nums[i]+dp1[i-2]); dp2[i] = max(dp2[i-1],nums[i]+dp2[i-2]); } return max(dp1[nums.size()-2],dp2[nums.size()-1]); }
打家劫舍3
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
-
对于一个以 node 为根节点的二叉树而言,如果尝试偷取 node 节点,那么势必不能偷取其左右子节点,然后继续尝试偷取其左右子节点的左右子节点。
-
如果不偷取该节点,那么只能尝试偷取其左右子节点。
-
比较两种方式的结果,谁大取谁。
class Solution { unordered_map<TreeNode *,int> sums; public: int rob(TreeNode* root) { return tryrob(root); } int tryrob(TreeNode *root) { if(!root) return 0; if(sums.count(root)) return sums[root]; int res1 = 0; //偷取当前节点,就只能偷取当前节点的子节点的子节点。 if(root->left) { res1+=tryrob(root->left->left)+tryrob(root->left->right); } if(root->right) { res1+=tryrob(root->right->left)+tryrob(root->right->right); } res1+=root->val; //不偷取当前节点 就只能偷取当前节点的子节点 int res2=0; res2 = tryrob(root->left)+tryrob(root->right); sums[root] = max(res1,res2); return max(res1,res2); } };
不同的二叉搜索树2
递归
for (int i = start; i <= end; i++) { // 获得所有可行的左子树集合 vector<TreeNode*> leftTrees = generateTrees(start, i - 1); // 获得所有可行的右子树集合 vector<TreeNode*> rightTrees = generateTrees(i + 1, end); // 从左子树集合中选出一棵左子树,从右子树集合中选出一棵右子树,拼接到根节点上 for (auto& left : leftTrees) { for (auto& right : rightTrees) { TreeNode* currTree = new TreeNode(i); currTree->left = left; currTree->right = right; allTrees.emplace_back(currTree); } } }
找数字规律
剑指43 1~n整数中1出现的次数
输入一个整数 n
,求1~n这n个整数的十进制表示中1出现的次数。
剑指44 数字序列中某一位的数字
数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。
请写一个函数,求任意第n位对应的数字。
剑指45. 把数组排成最小的数(自定义排序)
输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
二分查找
剑指offer11 旋转数组的最小值
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。