字符串和数组---总结
本博客的题均为leetcode上的摘录,都是我的刷题答案,仅做参考,若有错误,欢迎指正
直接上题目:
一、旋转数组
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入:[1,2,3,4,5,6,7]
和 k = 3 输出:[5,6,7,1,2,3,4]
解释: 向右旋转 1 步:[7,1,2,3,4,5,6]
向右旋转 2 步:[6,7,1,2,3,4,5]
向右旋转 3 步:[5,6,7,1,2,3,4]
示例 2:
输入: [-1,-100,3,99]
和 k = 2
输出: [3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
说明:
- 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
- 要求使用空间复杂度为 O(1) 的 原地 算法。
//C++实现
class Solution {
public:
void rotate(vector<int>& nums, int k) {
//方法一
//时间复杂度仅为O(n+k)
if(nums.size()<2)
return ;
if(k>nums.size()) //如果k大于nums的长度,只需求余数即可
k=k%nums.size();
//k=k%nums.size(); //可以不用判断,全部求余即可
stack<int> s;
for(int i=0;i<k;i++)
s.push(nums[nums.size()-1-i]);
for(int i=nums.size()-1;i>=k;i--)
nums[i]=nums[i-k];
for(int i=0;i<k;i++)
{
nums[i]=s.top();
s.pop();
}
//方法二
//还可以使用三个reverse()进行操作,很巧的方法
/*
*方法三
*此方法会超时,时间复杂度太大了
int i=0;
while(i<k)
{
int temp;
temp=*(nums.end()-1);
//temp=nums.top();
for(int i=nums.size()-1;i>0;i--)
nums[i]=nums[i-1];
nums[0]=temp;
i++;
}
*/
}
};
二、杨辉三角 II
给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。
在杨辉三角中,每个数是它左上方和右上方的数的和。
示例:
输入: 3
输出: [1,3,3,1]
进阶:
你可以优化你的算法到 O(k) 空间复杂度吗?
//C++实现
class Solution {
public:
vector<int> getRow(int rowIndex) {
vector<vector<int>> ret(rowIndex+1); //输入的数,即为杨辉三角的行数
for(int i=0;i<ret.size();i++)
{
ret[i].resize(i+1); //杨辉三角每一行的列数即为行数的值,第一行为一列,第二行为两列。。。
for(int j=0;j<i+1;j++)
{
if(j==0 || j==i) //每行的第一个和最后一个元素为1
ret[i][j]=1;
else //其他元素为上一行的同一列和前一列的元素和
{
ret[i][j]=ret[i-1][j-1]+ret[i-1][j];
}
}
}
return ret[rowIndex];
}
};
三、翻转字符串里的单词
给定一个字符串,逐个翻转字符串中的每个单词。
示例 1:
输入: "the sky is blue
" 输出: "blue is sky the
"
示例 2:
输入: " hello world! "
输出: "world! hello"
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
示例 3:
输入: "a good example"
输出: "example good a"
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
说明:
- 无空格字符构成一个单词。
- 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
- 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
//C++实现
class Solution {
public:
string reverseWords(string s) {
//find_first_not_of(" "):找到第一个不是“ ”的字符,返回index
//find_first_of(" "):找到第一个“ ”字符,返回index
//substr()函数
//如果字符串为空,返回s
if(s.empty())
return s;
//处理字符串前后的空格
s.erase(0,s.find_first_not_of(" ")); //去除字符串前面所有的空格
//s.erase(s.find_last_not_of(" ")+1); //此语句是删除字符串末尾的所有空格
s+=" "; //保持字符串末尾至少有一个空格,作为循环判断条件
vector<string> ret; //定义一个vector存储分离出的字符串单词
while(!s.empty()) //以s非空作为循环条件
{
size_t index=s.find_first_of(" "); //获取第一个“ ”的索引,下面用substr截取第一个单词
ret.push_back(s.substr(0,index)); //根据第一个空格的位置,截取第一个单词,放入ret当中
s.erase(0,index); //提取出第一个单词之后,将他从s字符串删除
s.erase(0,s.find_first_not_of(" "));//每提取出一个单词之后,将字符串前面空格清空,方便下一次循环操作
}
//循环将ret存储的单词逐个加空格,倒序返回
s=s+*(ret.end()-1);
for(int i=ret.size()-2;i>=0;i--)
s=s+" "+ret[i];
return s;
}
};
这个题目让我很好的理解和使用C++里面的字符快速查找函数,在进行字符串的操作过程中使用很方便,这里主要是四个函数:
find_first_of(string &str):找到字符串中的第一个str字符
find_first_not_of(string &str):找到字符串中的第一个不是str的字符
find_last_of(string &str):找到字符串中最后一个str字符
find_last_not_of(string &str):找到字符串中最后一个非str字符
除此之外还有字符串的截取函数:substr(),能够根据索引截取出字符串中的指定片段,再结合string的erase()函数,就能够很自如的对字符串进行剪切,截取以及删减。
四、反转字符串中的单词 III
给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
示例 1:
输入: "Let's take LeetCode contest"
输出: "s'teL ekat edoCteeL tsetnoc"
注意:在字符串中,每个单词由单个空格分隔,并且字符串中不会有任何额外的空格。
//C++实现
class Solution {
public:
string reverseWords(string s) {
//s.erase(0,s.find_first_not_of(" "));
//s.erase(s.find_last_not_of(" "));
if(s.empty()) //处理字符串为空情况
return s;
vector<string> ret;
while(!s.empty())
{
size_t index=s.find_first_of(" "); //获取第一个空格的index
if(index==string::npos) //如果检索不到空格,返回string::npos,表示到达最后一个单词,直接反转后存入即可
{
reverse(s.begin(),s.end());
ret.push_back(s);
break;
}
string t = s.substr(0,index); //提取单词
reverse(t.begin(),t.end()); //反转单词
ret.push_back(t); //存储单词
s.erase(0,index); //将单词从字符串删除
s.erase(0,s.find_first_not_of(" ")); //清除字符串前面的空格
}
s.clear(); //清空字符串
s+=ret[0];
for(int i=1;i<ret.size();i++)
{
s=s+" "+ret[i];
}
return s;
}
};
五、删除排序数组中的重复项
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0,1,2,3,4。
你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
//C++实现
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
//题目隐含条件:排序数组,都已经排好序了
//双指针的做法,很巧
/*
if(nums.size()<2) //vector为空或者只有一个元素的都可以忽略,直接返回size()即可
return nums.size();
int i=0,j=1; //定义两个索引值,表示两个指针,分别进行操作
while(j<nums.size())
{
if(nums[i]==nums[j]) //如果碰到连续相等的字符,j++,j往后移动,直到不是相等的数
j++;
else //如果前后不相等,直接跳过,并且将j指向的那个元素移到i+1处,即将不相等的数连续的放在一起
{
i++;
nums[i]=nums[j];
j++;
}
}
return i+1; //最后不重复的数只是i+1,后面的都是重复后剩下的数
*/
//初次做法:时间超时
if(nums.size()<2)
return nums.size();
int count=0;
for(int i=0;i<nums.size()-1;i++)
{
int temp=i+1;
while(temp<nums.size()-1 && nums[temp] == nums[i])
{
temp++;
count++;
}
if(temp==i+1)
break;
else
{
int len=temp;
for(int j=i+1;j<len;j++,temp++)
nums[j]=nums[temp];
}
}
return nums.size()-count;
}
};
此题的双指针解法充分体现了双指针的内涵,值得学习和深究。
六、移动零
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入:[0,1,0,3,12]
输出:[1,3,12,0,0]
说明:
- 必须在原数组上操作,不能拷贝额外的数组。
- 尽量减少操作次数。
//C++实现
class Solution {
public:
void moveZeroes(vector<int>& nums) {
/*冒泡法*/
for(int i=0;i<nums.size()-1;i++)
{
for(int j=0;j<nums.size()-i-1;j++)
{
if(nums[j]==0)
{
int temp=nums[j];
nums[j]=nums[j+1];
nums[j+1]=temp;
}
}
}
}
};