题目描述:
将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 “LEETCODEISHIRING” 行数为 3 时,排列如下:
L C I R
E T O E S I I G
E D H N
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“LCIRETOESIIGEDHN”。
解题思路:
当空字符或者只有一个字符的时候直接输出字符;
按如图所示的颜色将字符分组
每一组字符长度:num1=numRows*2-2,
分组个数: num=s.size()/num1或者num=s.size()/num1+1
代码实现:
string convert(string s, int numRows)
{
string res;
int groupl = numRows * 2 - 2;//groupl表示每个分组的长度
if (s.size() < 2 || groupl == 0)
{
res = s;
return res;
}
int num = s.size() / groupl + 1;//num表示分组个数
if (s.size() < 2 || groupl == 0)
{
res = s;
return res;
}
if (s.size() % groupl == 0)
{
num = s.size() / groupl;
}
// 首先进行分组,并将分组结果保存
vector<string> vec(num);
int k = 0;
int count = 0;
for (int i = 0; i < s.size(); ++i)
{
if (count == groupl)//表示一个组已经分完
{
count = 0;
k++;
}
vec[k].push_back(s[i]);
count++;
}
//将每个分组的首元素添加到结果中
for (int i = 0; i < num; ++i)
{
res.push_back(vec[i][0]);
}
//处理其他元素
for (int m = 1; m < numRows; ++m)//m:处理每一行
{
for (int n = 0; n < num; ++n)//n:处理每一组
{
if (m < vec[n].size())
{
res.push_back(vec[n][m]);
}
if ((groupl - m) < vec[n].size() && (groupl - m) != m)
{
res.push_back(vec[n][groupl - m]);
}
}
}
return res;
}
问题描述:
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
输入:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
输出: 2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右
解题思路:
动态规划
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
代码实现:
int uniquePathsWithObstacles(vector<vector<int>>& grid)
{
int row = grid.size();
if (row == 0)
{
return -1;
}
int col = grid[0].size();
//dp[i][j]表示从左上角开始到位置(i,j)的最短的路径
vector<vector<long long>> dp(row, vector<long long>(col));
if (grid[0][0] == 1)
{
dp[0][0] = 0;
return 0;
}
dp[0][0] = 1;
for (int i = 1; i < col; ++i)//处理dp的第一行
{
if (grid[0][i] != 1 && dp[0][i - 1] == 1)
{
dp[0][i] = 1;
}
else
{
dp[0][i] = 0;
}
}
for (int j = 1; j < row; ++j)//处理dp的第一列
{
if (grid[j][0] != 1 && dp[j-1][0] == 1)
{
dp[j][0] = 1;
}
else
{
dp[j][0] = 0;
}
}
for (int i = 1; i < row; ++i)
{
for (int j = 1; j < col; ++j)
{
if (grid[i][j] != 1)
{
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
else
{
dp[i][j] = 0;
}
}
}
return dp[row - 1][col - 1];
}
问题描述:
所有 DNA 由一系列缩写为 A,C,G 和 T 的核苷酸组成,例如:“ACGAATTCCG”。在研究 DNA 时,识别 DNA 中的重复序列有时会对研究非常有帮助。
编写一个函数来查找 DNA 分子中所有出现超多一次的10个字母长的序列(子串)。
示例:
输入: s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT"
输出: ["AAAAACCCCC", "CCCCCAAAAA"]
解题思路:
将所有长度为10的子串存放进map中,可根据map中的子串出现的次数查询出我们需要的子串。
代码实现:
vector<string> findRepeatedDnaSequences(string s)
{
int len = s.size();
vector<string> res;
unordered_map<string, int> mymap;
if (len<10)
{
return res;
}
for (int i = 0; i < len; ++i)
{
string word = s.substr(i, 10);
mymap[word]++;
}
auto it = mymap.begin();
while (it != mymap.end())
{
if (it->second > 1)
{
res.push_back(it->first);
}
it++;
}
return res;
}
题目描述:
判断一个 9 × 9 9×9 9×9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。(数独部分空格内已填入了数字,空白格用 ‘.’ 表示。)
示例 1:
输入:
[
["5","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]
]
输出: true
示例 2:
输入:
[
["8","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]
]
输出: false
解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。
但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的
解题思路:
假设:i = 0;c = 8;那么rawFlag[i][c] = true的含义为:在第0行中,出现过数字8。
假设:i = 8; c = 1;那么rawFlag[i][c] = true的含义为:在第8行中,出现过数字1。
假设:i = 0;c = 8;那么colFlag[i][c] = true的含义为:在第0列中,出现过数字8。
假设:i = 8; c = 1;那么colFlag[i][c] = true的含义为:在第8列中,出现过数字1。
假设:i = 0;c = 8;那么cellFlag[i][c] = true的含义为:在第0个方阵中,出现过数字8。
假设:i = 8; c = 1;那么cellFlag[i][c] = true的含义为:在第8个方阵中,出现过数字1。
代码实现
bool isValidSudoku(vector<vector<char> > &board) {
if (board.empty() || board[0].empty()) return false;
int m = board.size(), n = board[0].size();
vector<vector<bool> > rowFlag(m, vector<bool>(n, false));//表示某行是否出现过该数字
vector<vector<bool> > colFlag(m, vector<bool>(n, false));//表示某列是否出现过该数字
vector<vector<bool> > cellFlag(m, vector<bool>(n, false));//表示某个3x3的矩阵中是否出现过该数字
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (board[i][j] >= '1' && board[i][j] <= '9') {
int c = board[i][j] - '1';
if (rowFlag[i][c] || colFlag[c][j] || cellFlag[3 * (i / 3) + j / 3][c])
{
//rowFlag[i][c]:表示i行是否出现过c
//colFlag[c][j]:表示j列是否出现过c
//cellFlag[i][c]:表示在第i个方阵中是否出现过数字c,其中该数字属于第几个方阵可通过其行列坐标计算得到
return false;
}
rowFlag[i][c] = true;
colFlag[c][j] = true;
cellFlag[3 * (i / 3) + j / 3][c] = true;
}
}
}
return true;
}
题目描述:
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] )。
编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。
示例 1:
输入: nums = [2,5,6,0,0,1,2], target = 0
输出: true
示例 2:
输入: nums = [2,5,6,0,0,1,2], target = 3
输出: false
解题思路:
这道是之前那道 Search in Rotated Sorted Array 在旋转有序数组中搜索的延伸,现在数组中允许出现重复数字,这个也会影响我们选择哪半边继续搜索,由于之前那道题不存在相同值,我们在比较中间值和最右值时就完全符合之前所说的规律:如果中间的数小于最右边的数,则右半段是有序的,若中间数大于最右边数,则左半段是有序的。
而如果可以有重复值,就会出现来面两种情况,[3 1 1] 和 [1 1 3 1],对于这两种情况中间值等于最右值时,目标值3既可以在左边又可以在右边,那怎么办,对于这种情况其实处理非常简单,只要把最右值向左一位即可继续循环,如果还相同则继续移,直到移到不同值为止,然后其他部分还采用 Search in Rotated Sorted Array 在旋转有序数组中搜索中的方法。
代码实现:
bool search(vector<int>& nums, int& target) {
int n = nums.size();
if (n == 0) return false;
int left = 0, right = n - 1;
while (left <= right) {//二分法,左右指针
int mid = (left + right) / 2;
if (nums[mid] == target) return true;
else if (nums[mid] < nums[right]) {//中间值小于右边的值,则右半段是有序的
if (nums[mid] < target && nums[right] >= target) left = mid + 1;
else right = mid - 1;
} else if (nums[mid] > nums[right]){//中间值大于左边的值,则左半段是有序的(同一升序空间)
if (nums[left] <= target && nums[mid] > target) right = mid - 1;
else left = mid + 1;
} else --right;//中间值等于最右边的值时,左移右指针即可
}
return false;//没找到
}
题目描述:
给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
例如,给定三角形:
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
解题思路:
动态规划
代码实现:
int minimumTotal(vector<vector<int>>& triangle)
{
int len= triangle.size();
vector<vector<int>> dp = triangle;
for (int i = 1; i < len; ++i)
{
for (int j = 0; j <=i; ++j)
{
if (j == 0)//左边界
{
dp[i][j] = dp[i - 1][j] + triangle[i][j];
}
else if (j == i)//右边界
{
dp[i][j] = dp[i - 1][j - 1] + triangle[i][j];
}
else//中间部分
{
dp[i][j] = min(dp[i - 1][j - 1] + triangle[i][j], dp[i - 1][j] + triangle[i][j]);
}
}
}
return *min_element(dp[len - 1].begin(), dp[len - 1].end());
}
题目描述:
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
示例:
输入: ["eat", "tea", "tan", "ate", "nat", "bat"],
输出:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
解题思路:
代码实现:
vector<vector<string>> groupAnagrams(vector<string>& strs)
{
vector<vector<string>> res;//保存返回的结果
unordered_map<string, vector<string>> mymap;//映射表
int size = strs.size();
if (size == 0)
{
return res;
}
for (string str : strs)
{
string t = str;//先保存原始数据,防止排序后异位词的改变
sort(t.begin(), t.end());//对每个异位词进行排序
mymap[t].push_back(str);//以排序后的异位词作为键
}
auto it = mymap.begin();
while (it != mymap.end())
{
res.push_back(it->second);
it++;
}
return res;
}
问题描述:
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
示例 1:
输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4.
示例 2:
输入: n = 13
输出: 2
解释: 13 = 4 + 9.
解题思路:
d p [ n ] dp[n] dp[n] 表示以 n n n为和的最少平方的和的个数(所求)。
d p dp dp 数组所有下标已经为完全平方数的数(如 1 , 4 , 9... 1,4,9... 1,4,9...)置为 1 1 1。
动态方程的意思是:对于每个 i i i ,比 i i i 小一个完全平方数的那些数中最小的个数 + 1 +1 +1就是所求,也就是 d p [ i − j ∗ j ] + 1 dp [ i - j * j ] + 1 dp[i−j∗j]+1
代码实现:
int numSquares(int n)
{
vector<int> dp(n + 1, INT_MAX);
for (int i = 1; i*i <= n; ++i)
{
if (i*i <= n)
{
dp[i*i] = 1;//每个完全平方数自身必然是由自身一个数组成
}
}
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j*j <i ; ++j)//j*j为比i小的的完全平方数
{
dp[i] = min(dp[i], dp[i - j * j] + 1);
if (dp[i] == 1)
{
break;
}
}
}
return dp[n];
}
题目描述:
假设有从 1 到 N 的 N 个整数,如果从这 N 个数字中成功构造出一个数组,使得数组的第 i 位 (1 <= i <= N) 满足如下两个条件中的一个,我们就称这个数组为一个优美的排列。
条件:
第 i 位的数字能被 i 整除
i 能被第 i 位上的数字整除
现在给定一个整数 N,请问可以构造多少个优美的排列?
示例1:
输入: 2
输出: 2
解释:
第 1 个优美的排列是 [1, 2]:
第 1 个位置(i=1)上的数字是1,1能被 i(i=1)整除
第 2 个位置(i=2)上的数字是2,2能被 i(i=2)整除
第 2 个优美的排列是 [2, 1]:
第 1 个位置(i=1)上的数字是2,2能被 i(i=1)整除
第 2 个位置(i=2)上的数字是1,i(i=2)能被 1 整除
解题思路:
一号位可以放置:1 2 3 4 5.
二号位可以放置:1 2 4
三号位只能放置:3
四号位可以放置: 1 2 4
代码实现:
int countArrangement(int N) {
int result = 0;
vector<int> isIn(N + 1, 0);
getAns(N, 1, result, isIn);//从一号位置开始
return result;
}
void getAns(int N, int pos, int& result, vector<int>& isIn)
{
if(pos > N)
{
result ++;
return;
}
else
{
for(int i = 1; i <= N; i ++)//对每一个数字进行判断
{
if(isIn[i] == 0 && (i % pos == 0 || pos % i == 0))
{
isIn[i] = 1;//标记i这个数字被访问过
getAns(N, pos + 1, result, isIn);//调用下一个位置的递归函数
isIn[i] = 0;//恢复初始状态
}
}
}
}
题目描述:
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3 a 3a 3a 或 2 [ 4 ] 2[4] 2[4] 的输入。
示例:
s = "3[a]2[bc]", 返回 "aaabcbc".
s = "3[a2[c]]", 返回 "accaccacc".
s = "2[abc]3[cd]ef", 返回 "abcabccdcdcdef".
解题思路:
代码实现:
string decodeString(string s)
{
//用两个栈,一个数字栈,一个字符串栈
stack<int> numstack;
stack<string> strstack;
string cur = "";//保存每个中括号里的字符串
int num = 0;
for (int i = 0; i < s.size(); ++i)
{
if (s[i] >= '0'&&s[i] <= '9')
{
num = num * 10 + s[i] - '0';
}
if (s[i] == '[')//遇到左括号就将数字,字符入栈
{
numstack.push(num);
strstack.push(cur);//首先将已经处理过的字符串push进字符串栈中
cur.clear();//以便处理下一个未被处理过的字符串
num = 0;
}
else if (s[i] >= 'a'&&s[i] <= 'z' || s[i] >= 'A'&&s[i] <= 'Z')
{
cur += s[i];
}
else if (s[i] == ']')
{
//取出数字栈中的栈顶元素,作为重复的次数
int k = numstack.top();
numstack.pop();
for (int i = 0; i < k; ++i)
{
strstack.top() += cur;
}
cur = strstack.top();//更新每次的cur为当前已经处理过的字符串
strstack.pop();
}
}
return cur;
}
题目描述:
给定一个包含 n n n 个整数的数组 n u m s nums nums,判断 n u m s nums nums 中是否存在三个元素 a a a, b b b, c c c ,使得 a + b + c = 0 ? a + b + c = 0 ? a+b+c=0?找出所有满足条件且不重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4]
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
解题思路:
代码实现:
vector<vector<int>> threeSum(vector<int>& nums)
{
vector<vector<int>> vec;//存放最终结果的数组
vector<int> result;//每次存放一组符合条件的元素
if(nums.size()<3)
{
return vec;
}
sort(nums.begin(),nums.end());//首先对数组进行排序
int sum;
for(int i=0;i<nums.size()-2;++i)
{
int start=i+1;
int end=nums.size()-1;
int k=-nums[i];//nums[i]为我们在外层循环确定好的一个数
while(start<end)
{
sum=nums[start]+nums[end];
if(sum==k)
{
result.push_back(nums[i]);
result.push_back(nums[start]);
result.push_back(nums[end]);
vec.push_back(result);
result.clear();
start++;
end--;
//去除start的重复
while(start<end&&nums[start]==nums[start-1])
{
start++;
}
//去除end的重复
while(start<end&&nums[end]==nums[end+1])
{
end--;
}
}
else if(sum<k)
{
start++;
}
else
{
end--;
}
}
//去除i的重复
while(i<nums.size()-2&&nums[i]==nums[i+1])
{
i++;
}
}
return vec;
}
题目描述:
给定一个包括 n n n 个整数的数组 n u m s nums nums 和 一个目标值 t a r g e t target target。找出 n u m s nums nums 中的三个整数,使得它们的和与 t a r g e t target target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.
与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).
解题思路:
排序和双指针
如果 sum > target 则 end--
如果 sum < target 则 start++
如果 sum == target 则说明距离为 0 直接返回结果
整个遍历过程,固定值为 n 次,双指针为 n 次,时间复杂度为 O ( n 2 ) O(n^2) O(n2)
总时间复杂度: O ( n 2 ) O(n^2) O(n2)
代码实现:
int threeSumClosest(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());//对数组进行排序
int sum;
int start;
int end;
int result = nums[0]+nums[1]+nums[2];
for(int i=0;i<nums.size()-2;++i)
{
start=i+1;
end=nums.size()-1;
while(start<end)
{
sum=nums[i]+nums[start]+nums[end];
if(abs(target-sum)<abs(target-result))
{
//该结果更接近,则更新result为最接近的结果
result=sum;
}
if(target>sum)
{
start++ ;
}
else
{
end--;
}
}
}
return result;
}
题目描述:
给定一个包含 n n n 个整数的数组 n u m s nums nums 和一个目标值 t a r g e t target target,判断 n u m s nums nums 中是否存在四个元素 a a a, b b b, c c c 和 d d d ,使得 a + b + c + d a + b + c + d a+b+c+d的值与 t a r g e t target target 相等?找出所有满足条件且不重复的四元组。
示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
解决思路:
代码实现:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> res;
// 从小到大对数组进行排序
sort(nums.begin(), nums.end());
int size = nums.size();
int curSum;
int start, end;
vector<int> save;
for (int i = 0; i < size - 3; i++) {
for (int j = i + 1; j < size - 2; j++) {
curSum = nums[i] + nums[j];
start = j + 1, end = size - 1;
// 双指针扫描
while (start < end) {
// 找到一个解
if (nums[start] + nums[end] == target - curSum) {
save.clear();
save.push_back(nums[i]);
save.push_back(nums[j]);
save.push_back(nums[start]);
save.push_back(nums[end]);
res.push_back(save);
start++;
end--;
// 去 start 重复
while (start < end && nums[start] == nums[start-1]) {
start++;
}
// 去 end 重复
while (start < end && nums[end] == nums[end+1]) {
end--;
}
} else if (nums[start] + nums[end] > target - curSum) {
end--;
} else {
start++;
}
}
// 去 j 重复
while (j < size - 2 && nums[j] == nums[j+1]) {
j++;
}
}
// 去 i 重复
while (i < size - 3 && nums[i] == nums[i+1]) {
i++;
}
}
return res;
}