赞
踩
解题思路:首先我们利用的思路是丑数的递推性质:因为丑数只包含因子2,3,5,所以可以有一个规律就是:“丑数 = 某较小丑数 * 某因子”。假如我们要求的下个丑数为Xn+1,则Xn+1必定出现在Xa * 2,Xb * 3,Xc * 5这三个数中的某一个,也就是较小的一个
因此,我们可以使用动态规划的解题方法。首先我们可以设置指针a,b,c指向首个丑数(即1),利用上述的循环递推公式依次得到下一个丑数,并每轮将对应指针执行+1操作即可。
动态规划解析:
class Solution {
public:
int nthUglyNumber(int n) {
int a = 0, b = 0, c = 0;
int dp[n];
dp[0] = 1;
for(int i = 1; i < n; i++) {
int n2 = dp[a] * 2, n3 = dp[b] * 3, n5 = dp[c] * 5;
dp[i] = min(min(n2, n3), n5);
if(dp[i] == n2) a++;
if(dp[i] == n3) b++;
if(dp[i] == n5) c++;
}
return dp[n - 1];
}
};
二叉树前序遍历的顺序为:
二叉树中序遍历的顺序为:
所以我们可以通过前序遍历先得到根节点,然后在中序遍历中以根节点为界线,我们就可以分别知道左子树和右子树中的节点数目。由于同一颗子树的前序遍历和中序遍历的长度显然是相同的,因此我们就可以对应到前序遍历的结果中,这样以来我们就知道了左子树的前序遍历和中序遍历结果,以及右子树的前序遍历和中序遍历结果,我们就可以递归地构造出左子树和右子树,再将这两颗子树接到根节点的左右
我们可以使用哈希表来帮助我们快速地定位根节点。对于哈希映射中的每个键值对,键表示一个元素(节点的值),值表示其在中序遍历中的出现位置。在构造二叉树的过程之前,我们可以对中序遍历的列表进行一遍扫描,就可以构造出这个哈希映射。在此后构造二叉树的过程中,我们就只需要O(1)的时间对根节点进行定位了。
class Solution {
private:
unordered_map<int, int> index;
public:
TreeNode* myBuildTree(const vector<int>& preorder, const vector<int>& inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
if (preorder_left > preorder_right) {
return nullptr;
}
// 前序遍历中的第一个节点就是根节点
int preorder_root = preorder_left;
// 在中序遍历中定位根节点
int inorder_root = index[preorder[preorder_root]];
// 先把根节点建立出来
TreeNode* root = new TreeNode(preorder[preorder_root]);
// 得到左子树中的节点数目
int size_left_subtree = inorder_root - inorder_left;
// 递归地构造左子树,并连接到根节点
// 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
root->left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
// 递归地构造右子树,并连接到根节点
// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root->right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n = preorder.size();
// 构造哈希映射,帮助我们快速定位根节点
for (int i = 0; i < n; ++i) {
index[inorder[i]] = i;
}
return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
}
};
解题思路:
首先 岛屿总是被水包围,并且每座岛屿只能由水平方向和(或)竖直方向上相邻的陆地连接形成。所以其实很简单就是一个深搜的问题,我们先经过遍历,当遇到第一个为1的元素时,我们就可以认为有一个岛屿了,然后我们进入深搜,搜索四周,注意返回条件,要么就是越界,因为是二维数组所以行和列都有可能越界,除此之外就是当我们遍历到第一个不为1的数字也就说明无法继续连接成岛屿了则直接返回,还需要知道当我们遍历过的地方我们需要给它赋值覆盖掉原本的1,这样是为了提高效率。最后返回岛屿数量就行。
代码如下:
class Solution {
public:
void dfs(vector<vector<char>>& grid, int i, int j) {
if (i < 0 || i >= grid.size() || j < 0 || j >= grid[0].size() || grid[i][j] != '1') {
return;
}
grid[i][j] = '2';
dfs(grid, i + 1, j);
dfs(grid, i - 1, j);
dfs(grid, i, j + 1);
dfs(grid, i, j - 1);
}
int numIslands(vector<vector<char>>& grid) {
int isLands = 0;
for (int i = 0; i < grid.size(); i++) {
for (int j = 0; j < grid[0].size(); j++) {
if (grid[i][j] == '1') {
dfs(grid, i, j);
isLands++;
}
}
}
return isLands;
}
};
解题思路:
本题其实就是一个关于DFS的题目,机器人每次走一格就给计数➕1,所以我们只要搞清楚什么情况下机器人可以走,也就是边界问题以及行坐标和列坐标的数位之和不能大于k并且此格子机器人还没有进入过,既然有这个要求则还需要一个操作就是将每一个进入过的格子做一个标记,最后直接返回就行了
代码如下:
class Solution {
public:
int count = 0;
int movingCount(int m, int n, int k) {
vector<vector<int>> vis(m,vector<int>(n,0));
dfs(vis,m,n,k,0,0);
return count;
}
void dfs(vector<vector<int>> &vis ,int &m, int &n, int &k, int i, int j){
if (i < 0 || j < 0 || i >= m || j >= n || !sumTarget(i,j,k) || vis[i][j] != 0) {
return;
}
if (vis[i][j] == 0) {
count++;
vis[i][j] = 1;
dfs(vis,m,n,k,i + 1,j);
dfs(vis,m,n,k,i,j + 1);
}
}
bool sumTarget(int i, int j, int k) {
int temp = 0;
while (i > 0) {
temp += i % 10;
i /= 10;
}
while(j > 0) {
temp += j % 10;
j /= 10;
}
if (temp > k) {
return false;
}
return true;
}
};
解题思路:首先本题的解题思路还是动态规划,因为每次只能往右或者往下走所以我们来构建这样一个结构,我们的二维动态dp数组中,一定是一个矩形,所以这个矩形的上边肯定是由顶点一直往右得到的,所以我们先构造这个边,同理矩形左边的边也是由顶点一直往下得到的所以本题就很简单了,我们构造完矩形的这两个边就需要往矩形的内部拓展了,所以我们只需要考虑里边的每个点(元素)是由上边的点向下移动价值大还是由左边的点向右移动价值大,然后最后构造完这个二维矩形dp数组,返回右下角的元素即可。
代码如下:
class Solution {
public:
int maxValue(vector<vector<int>>& grid) {
int n = grid.size();
int m = grid[0].size();
int dp[n][m];
dp[0][0] = grid[0][0];
for (int i = 1; i < n; i++) {
dp[i][0] = dp[i - 1][0] + grid[i][0];
}
for (int j = 1; j < m; j++) {
dp[0][j] = dp[0][j - 1] + grid[0][j];
}
for (int i = 1; i < n; i++) {
for (int j = 1; j < m; j++) {
dp[i][j] = max(dp[i][j - 1] + grid[i][j],dp[i - 1][j] + grid[i][j]);
}
}
return dp[n - 1][m - 1];
}
};
解题思路
本题其实就是一个dfs的过程,首先就是先从顶点出发然后往四周扩散,但是我们要知道几个临界条件(不能超出范围,如果遇到了无法匹配的情况就退出)等等,然后如果匹配成功我们就返回true就行了。
代码如下:
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
rows = board.size();
cols = board[0].size();
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if(dfs(board,word,i,j,0)) {
return true;
}
}
}
return false;
}
private:
int rows,cols;
bool dfs(vector<vector<char>>& board, string word, int i, int j, int k) {
if (i >= rows || i < 0 || j >= cols || j < 0 || board[i][j] != word[k]) {
return false;
}
if (k == word.size() - 1) {
return true;
}
board[i][j] = '\0';
bool res = dfs(board,word,i+1,j,k+1) || dfs(board,word,i-1,j,k+1) || dfs(board,word,i,j+1,k+1) ||dfs(board,word,i,j-1,k+1);
board[i][j] = word[k];
return res;
}
};
我们使用两个指针i和j分别指向两个字符串的开头,然后向后遍历,当遇到小数点’.‘时停下来,并将每个小数点’.'分隔开的修订号解析成数字进行比较,越靠近前边,修订号的优先级越大。根据修订号大小关系,返回相应的数值。
具体过程如下:
代码如下
class Solution {
public:
int compareVersion(string version1, string version2) {
int n = version1.length();
int m = version2.length();
int i = 0, j = 0;
while(i < n || j < m) {
long int num1 = 0, num2 = 0;
while(i < n && version1[i] != '.') {
num1 = num1 * 10 + version1[i++] - '0';
}
while(j < m && version2[j] != '.') {
num2 = num2 * 10 + version2[j++] - '0';
}
if (num1 > num2) {
return 1;
} else if (num1 < num2) {
return -1;
}
i++,j++;
}
return 0;
}
};
解题思路:
这里的方法不需要记录已经走过的路径,所以执行用时和内存消耗都相对较小
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector<int> res;
if (matrix.empty()) return res;
int u = 0;
int d = matrix.size() - 1;
int l = 0;
int r = matrix[0].size() - 1;
while (true) {
for (int i = l; i <= r; i++) {
res.push_back(matrix[u][i]);
}
if (++u > d) break;
for (int i = u; i <= d; i++) {
res.push_back(matrix[i][r]);
}
if (--r < l) break;
for (int i = r; i >= l; i--) {
res.push_back(matrix[d][i]);
}
if (--d < u) break;
for (int i = d; i>= u; i--) {
res.push_back(matrix[i][l]);
}
if (++l > r) break;
}
return res;
}
};
解题思路
运用到的是基于快速排序的选择排序,也叫快速选择。快速排序的原理就是可以一次把一个元素放到数组它对应的位置上,利用这个特性我们可以进行左右边界的更迭达到类似二分的效果,即一次甩掉一半的元素可以不必再纠结它们的顺序和大小。具体是否可以完成二分效果,取决于 base 元素的选择,如果 base 选择较差,那么会使算法退化到 O(N^2) 的时间复杂度;反之可以把问题规模每次都缩小一半。具体流程如下:
//快速选择方法-迭代
class Solution {
public:
void quickPartition(vector<int>& nums, int start, int end, int target) {
// 随机取一个数作为基准
srand(time(nullptr));
int random = rand() % (end - start + 1) + start;
int base = nums[random];
// 将该数放到待快排区间开头第一个元素
swap(nums[start], nums[random]);
int index = start;
// 从待快排区间的第二个元素开始,依次与base比较,如果大于等于base则将该元素
// 交换到index + 1位置,index++,使得最终index前面的元素都比base大。
for (int i = start + 1; i <= end; ++i) {
if (nums[i] >= base) {
swap(nums[index + 1], nums[i]);
index++;
}
}
// base存放在区间开头,现在需要把它交换到index位置,这就是它在整个有序数组中的位置。
swap(nums[index], nums[start]);
// 如果index小于target,需要在右边区间继续快排查找,否则到在边区间查找,
// 如果等于已经找到目标值不需要递归,这里这么做优化了传统快排的复杂度。
if (index < target) {
quickPartition(nums, index + 1, end, target);
}
else if (index > target) {
quickPartition(nums, start, index - 1, target);
}
}
int findKthLargest(vector<int>& nums, int k) {
// 方法1. 快速排序的分区思想,快排的思想是一次找出一个数的正确位置,
// 并使得该数左边的元素都比它小,该数右边的元素都比它大,要找出第k
// 大的元素,只需要在快排的时候采用降序排序,找到下标为k-1的元素即可。
quickPartition(nums, 0, nums.size() - 1, k - 1);
return nums[k - 1];
}
};
解题思路
我们找出以每一个字符为第一个字符的无重复字符的子串,然后每一次通过比较替换最后直接返回就行了。如果我们依次递增地枚举子串的起始位置,那么子串的结束位置也是递增的。这里的原因在于,假设我们选择字符串中的第k个字符作为起始位置,并且得到了不包含重复字符的最长子串的结束位置为rk。那么当我们选择第k+1个字符作为起始位置时,首先从k+1到rk的字符显然是不重复的,并且由于少了原本的第k个字符,我们可以尝试继续增加rk,直到右侧出现了重复字符位置。
其实就是定义一个滑动窗口:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
// 哈希集合,记录每个字符是否出现过
unordered_set<char> occ;
int n = s.size();
// 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
int rk = -1, ans = 0;
// 枚举左指针的位置,初始值隐性地表示为 -1
for (int i = 0; i < n; ++i) {
if (i != 0) {
// 左指针向右移动一格,移除一个字符
occ.erase(s[i - 1]);
}
while (rk + 1 < n && !occ.count(s[rk + 1])) {
// 不断地移动右指针
occ.insert(s[rk + 1]);
++rk;
}
// 第 i 到 rk 个字符是一个极长的无重复字符子串
ans = max(ans, rk - i + 1);
}
return ans;
}
};
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。