数组和字符串
数组简介
寻找数组的中心索引
给你一个整数数组 nums ,请计算数组的 中心下标 。
数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。
如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。
如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/array-and-string/yf47s/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
int pivotIndex(vector<int>& nums) {
//先算出数组所有元素的总和
int sum=0;
for(int i=0;i<nums.size();i++)
{
sum=sum+nums[i];
}
//如果第0个元素右边所有元素的和是0的话,那就说明中心元素是第0个元素
if(sum-nums[0]==0)
return 0;
//用数组的减去当前阶段的总和以及当前坐标元素的值就可以知道剩下部分的总和,如果这两个和相等,就说明前半部分的和等于后半部分的和
//0-i index j-size()-1 只要0-i的和与j-size()-1的和相等就是所求坐标
// curSum为0-i的和, sum-curSum-nums[i+1]为j-size()-1的和
//这两个值相等说明index就是中间坐标
int curSum=0;
for(int i=0;i<nums.size();i++)
{
curSum=curSum+nums[i];
if((sum-curSum-nums[i+1])==curSum)
{
return i+1;
}
}
return -1;
}
};
搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
解法一:暴力法
- 从头到尾遍历找目标值,当找到的下标对应的值比目标值还要大的时候,把目标值插入到那个位置上
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
for(int i=0;i<nums.size();i++)
{
if(nums[i]==target)
{
return i;
}
if(nums[i]>target)
{
return i;
}
}
return nums.size();
}
};
解法二:二分法
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int size=nums.size();
//如果target比最后一位都大,直接插入到最后一位
if(target>nums[size-1])
return size;
int mid=size/2;
int left=0;
int right=size-1;
//二分查找,如果能找到,就返回mid的位置
while((left<right)&&(left>-1)&&(right<size))
{
if(target==nums[mid])
{
return mid;
}
if(target<nums[mid])
{
right=mid-1;
mid=(left+mid-1)/2;
}
if(target>nums[mid])
{
left=mid+1;
mid=(mid+1+right)/2;
}
}
//判断target和最后找到位置的关系,比最后位置大则返回最后位置+1
if(target>nums[left])
return left+1;
//否则返回最后位置
return left;
}
};
合并区间
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/array-and-string/c5tv3/
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
//如果只有一个区间,返回这个区间
if(intervals.size()==1)
return intervals;
//先把二维数组按每个区间的左下标排序
sort(intervals.begin(),intervals.end());
//定义返回结果的数组
vector<vector<int>>res;
res.push_back(intervals[0]);
//如果当前区间的左下标小于等于当前结果数组的右下标,说明有重叠的部分可以合并
//j记录结果数组的下标
int j=0;
for(int i=j+1;i<intervals.size();i++)
{
if(intervals[i][0]<=res[j][1])
{
//合并区间的右下标取结果数组的区间与目前遍历到的区间右下标更大的值
res[j][1]=max(intervals[i][1],res[j][1]);
}
else{
//如果没有合并的地方,那就直接把当前的区间加进结果数组里
res.push_back(intervals[i]);
//结果数组坐标+1
j++;
}
}
return res;
}
};
二维数组简介
旋转矩阵
给你一幅由 N × N
矩阵表示的图像,其中每个像素的大小为 4 字节。请你设计一种算法,将图像旋转 90 度。
不占用额外内存空间能否做到?
给定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
原地旋转输入矩阵,使其变为:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/array-and-string/clpgd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
- 根据数字找规律可以发现只需要沿次对角线翻转再沿水平方向中线对折即可
class Solution {
public:
//交换两个数的值
static void swap(int &num1,int &num2)
{
int temp=num1;
num1=num2;
num2=temp;
}
//交换两个数组的值
static void swap(vector<int> &num1,vector<int> &num2)
{
vector<int> temp=num1;
num1=num2;
num2=temp;
}
void rotate(vector<vector<int>>& matrix) {
int size=matrix.size();
//先沿次对角线两两交换位置
for(int i=0;i<size-1;i++)
{
for(int j=0;j<size-i-1;j++)
{
swap(matrix[i][j],matrix[size-j-1][size-i-1]);
}
}
//再沿中间的线两两交换位置
for(int i=0;i<size/2;i++)
{
swap(matrix[i],matrix[size-i-1]);
}
}
};
零矩阵
编写一种算法,若M × N矩阵中某个元素为0,则将其所在的行与列清零。
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
//记录多少行
int LineSize=matrix.size();
//记录多少列
int RowSize=matrix[0].size();
//记录列为0的元素
vector<int> row;
//记录行为0的元素
vector<int> line;
for(int i=0;i<LineSize;i++)
{
for(int j=0;j<RowSize;j++)
{
if(matrix[i][j]==0)
{
//如果列为零,把当前的列坐标值放入row数组
row.push_back(j);
//如果行为零,把当前的行坐标值放入line数组
line.push_back(i);
}
}
}
//依次清空行坐标
for(int i=0;i<line.size();i++)
{
for(int j=0;j<RowSize;j++)
{
matrix[line[i]][j]=0;
}
}
//依次清空列坐标
for(int j=0;j<row.size();j++)
{
for(int i=0;i<LineSize;i++)
{
matrix[i][row[j]]=0;
}
}
}
};
对角线遍历
给定一个含有 M x N 个元素的矩阵(M 行,N 列),请以对角线遍历的顺序返回这个矩阵中的所有元素,对角线遍历如下图所示
https://leetcode-cn.com/leetbook/read/array-and-string/cuxq3/
- 实在是没想到可以这么模拟步数,写了一个下午,看到这题解的时候哭出声来
class Solution {
public:
vector<int> findDiagonalOrder(vector<vector<int>>& mat) {
vector<int> res;
//定义四个方向,横着走,斜下走,往下走,斜上走
int dx[]={0,1,1,-1};
int dy[]={1,-1,0,1};
int line=mat.size();
int row=mat[0].size();
//定义一个数组判断当前的点有没有走过
vector<vector<bool>>judge(line,vector<bool>(row,0));
//记录当前的点的坐标和方向
int x=0,y=0,d=0;
for(int step=0;step<line*row;step++)
{
res.push_back(mat[x][y]);
judge[x][y]=true;
//如果是最后一个点直接返回
if(step==line*row-1)
return res;
//确定当前的点的下一步
int a=x+dx[d],b=y+dy[d];
while(a<0||a>=line||b<0||b>=row||judge[a][b]==true)
{
//如果d增加到超过3,那就取余,防止数组越界
d=(d+1)%4;
a=x+dx[d];
b=y+dy[d];
}
//找到了下一步之后改变xy的值
x=x+dx[d];
y=y+dy[d];
//如果是横着走或者竖着走的话,要先变方向,不然会一直横着走
if(d==0||d==2)
{
//如果d增加到超过3,那就取余,防止数组越界
d=(d+1)%4;
}
}
return res;
}
};
字符串简介
最长公共子串
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""
。
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
//记录最长子串
string res;
int len=0;
//记录时第几个字符串
int i=0;
//当超过其中一个字符串的长度时,子串长度达到最大值,退出循环
while(len<strs[i].size())
{
//从头到尾依次遍历字符串数组,依次看每一个字符串的第i个值是不是相等,不相等就返回当前子串
for(i=0;i<strs.size()-1;i++)
{
if(strs[i][len]!=strs[i+1][len])
{
return res;
}
}
//如果遍历完了发现这一len对应的坐标对应的字符相等,子串的长度就加1,然后把子串复制给res
len++;
res=strs[i].substr(0,len);
}
return res;
}
};
最长回文串
给你一个字符串 s
,找到 s
中最长的回文子串。
class Solution {
public:
string longestPalindrome(string s) {
//如果字符串字符数小于等于1
int size=s.size();
if(s.size()<=1)
return s;
//要找到最长的回文子串,只需要依次求以每个字符为中心的最大子串即可
//记录子串左下标
int left=0;
//记录子串的右下标
int right=0;
//记录最大子串的长度和下标,只要字符串不为空,最长回文串长度至少为1
int maxlen=1;
int pos=0;
//从第一个下标开始依次找各个字符的最大子串
for(int i=0;i<size;i++)
{
//记录当前字符的回文串
int len=0;
//回文串是奇数
left=i-1;
right=i+1;
while(left>=0&&right<size&&s[left]==s[right])
{
len=right-left+1;
if(len>maxlen)
{
maxlen=len;
pos=left;
}
left--;
right++;
}
//回文串是偶数
left=i;
right=i+1;
while(left>=0&&right<size&&s[left]==s[right])
{
len=right-left+1;
if(len>maxlen)
{
maxlen=len;
pos=left;
}
left--;
right++;
}
}
return s.substr(pos,maxlen);
}
};
翻转字符串里的单词
class Solution {
public:
string reverseWords(string s) {
//可以用栈来解决,读到空格就压栈
int left=0,right=s.size()-1;
//先去掉左边的空格
while(s[left]==' ')left++;
//去掉右边空格
while(s[right]==' ')right--;
//记录当前单词
string word;
//栈倒叙保存单词
stack<string> words;
while(left<=right)
{
//如果不是空的,当前单词的字母加1
if(s[left]!=' ')
{
word=word+s[left];
}
//如果是空值而且左边是有字母的,整个单词压栈
//是最后一个单词,压栈
if((s[left]==' '&&s[left-1]!=' ')||left==right){
words.push(word);
word="";
}
//如果左边和当前字符都是空的话,直接left++
left++;
}
//遍历栈输出结果字符
string res;
while(!words.empty())
{
res=res+words.top()+" ";
words.pop();
}
return res.substr(0,res.size()-1);
}
};
实现strStr()
解法一:暴力法
- 79/80个样例超时了
class Solution {
public:
int strStr(string haystack, string needle) {
if(needle.size()==0)
return 0;
int i=0,j=0;
while(i<haystack.size()&&j<needle.size())
{
if(haystack[i+j]==needle[j])
{
j++;
continue;
}
if(haystack[i+j]!=needle[j])
{
i++;
j=0;
continue;
}
}
if(j==needle.size())
{
return i;
}
return -1;
}
};
解法二:改进版暴力法
- 直接截取每一个坐标下后子串个长度的字符串比较是不是相等
class Solution {
public:
int strStr(string haystack, string needle) {
int haySize=haystack.size();
int needleSize=needle.size();
if(needleSize==0)
return 0;
if(haystack==needle)
return 0;
int i=0,j=0;
//i的值最大只需要到两个字符串长度差值
while(i<haySize-needleSize+1&&j<needleSize)
{
if(haystack[i+j]==needle[j])
{
j++;
continue;
}
if(haystack[i+j]!=needle[j])
{
i++;
j=0;
continue;
}
}
if(j==needle.size())
{
return i;
}
return -1;
}
};
解法三:直接调库
class Solution {
public:
int strStr(string haystack, string needle) {
return haystack.find(needle);
}
};
双指针技巧
反转字符串
双指针法
class Solution {
public:
void reverseString(vector<char>& s) {
int left=0,right=s.size()-1;
while(left<right)
{
char temp=s[left];
s[left++]=s[right];
s[right--]=temp;
}
}
};
数组拆分
给定长度为 2n 的整数数组 nums ,你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), …, (an, bn) ,使得从 1 到 n 的 min(ai, bi) 总和最大。
返回该 最大总和 。
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/array-and-string/c24he/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
- 两个数最大的时候,他的最小值也最大,依次取最大的两个数即可
class Solution {
public:
int arrayPairSum(vector<int>& nums) {
sort(nums.begin(),nums.end());
int res=0;
for(int i=nums.size()-2;i>=0;i=i-2)
{
res=res+nums[i];
}
return res;
}
};
两数之和
给定一个已按照 升序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。
函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length 。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/array-and-string/cnkjg/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
解法一:哈希表
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
//如果两个数的和是target,那target-其中一个数等于另一个数
map<int,int>mp;
for(int i=0;i<numbers.size();i++)
{
//如果当前的数还没找到配对的数,把他放到map里,对应的值是下标
//为了区分原始的值和下标0,把下标+1
if(mp[target-numbers[i]]==0)
{
mp[numbers[i]]=i+1;
}
else{
return {mp[target-numbers[i]],i+1};
}
}
return {};
}
};
解法二:双指针
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int left=0;
int right=numbers.size()-1;
while(left<right)
{
if(numbers[left]+numbers[right]==target)
{
return{left+1,right+1};
}
else if(numbers[left]+numbers[right]<target)
{
left++;
}
else{
right--;
}
}
return {};
}
};
移动元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/array-and-string/cwuyj/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int fast=0;
int slow=0;
int size=nums.size();
while(fast<size)
{
//如果找到了和val相等的元素,那么令慢指针指向它
if(nums[fast]==val)
{
slow=fast;
size--;
//然后从慢指针开始,从后往前依次移动一位
for(;slow<size;slow++)
{
nums[slow]=nums[slow+1];
}
}
//否则就继续看下一个元素
else{
fast++;
}
}
return size;
}
};
最大连续1的个数
给定一个二进制数组, 计算其中最大连续 1 的个数。
class Solution {
public:
int findMaxConsecutiveOnes(vector<int>& nums) {
int fast=0;
//为了防止一开始的连续1是最大的,先将慢指针置为-1
int slow=-1;
int res=0;
int curRes=0;
while(fast<nums.size())
{
//快指针指向1时,快指针减去指向0时的坐标就是当前连续1的长度
if(nums[fast]==1)
{
curRes=fast-slow;
res=res>curRes?res:curRes;
curRes=0;
}
if(nums[fast]==0)
{
//如果快指针指向的数字是0,慢指针指向当前下标
slow=fast;
}
fast++;
}
return res;
}
};
长度最小的子数组
解法一:暴力法
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int res=1000000;
int curlen=0;
int sum=0;
int size=nums.size();
for(int i=0;i<size;i++)
{
sum=0;
for(int j=i;j<size;j++)
{
sum=nums[j]+sum;
if(sum>=target)
{
curlen=j-i+1;
res=res<curlen?res:curlen;
}
}
}
return res==1000000?0:res;
}
};
小结
杨辉三角
给定一个非负整数 *numRows,*生成杨辉三角的前 numRows 行。
在杨辉三角中,每个数是它左上方和右上方的数的和。
示例:
输入: 5
输出:
[
[1],
[1,1],
[1,2,1],
[1,3,3,1],
[1,4,6,4,1]
]
class Solution {
public:
vector<vector<int>> generate(int numRows) {
vector<vector<int>>res;
//遍历从第一行到第五行
for(int i=0;i<numRows;i++)
{
//遍历往每一行中添加元素,第i行有i+1个元素
vector<int> line(i+1,0);
for(int j=0;j<i+1;j++)
{
//如果是第一个或者最后一个数字直接压入1
if(j==0||j==i)
{
line[j]=1;
}
else{
line[j]=res[i-1][j]+res[i-1][j-1];
}
}
res.push_back(line);
}
return res;
}
};
杨辉三角Ⅱ
给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。
在杨辉三角中,每个数是它左上方和右上方的数的和。
- 笨办法,把整个杨辉三角都遍历一遍
class Solution {
public:
vector<int> getRow(int rowIndex) {
vector<vector<int>>res;
//遍历从第一行到第33行
for(int i=0;i<=33;i++)
{
//遍历往每一行中添加元素,第i行有i+1个元素
vector<int> line(i+1,0);
for(int j=0;j<i+1;j++)
{
//如果是第一个或者最后一个数字直接压入1
if(j==0||j==i)
{
line[j]=1;
}
else{
line[j]=res[i-1][j]+res[i-1][j-1];
}
}
res.push_back(line);
}
return res[rowIndex];
}
};
反转字符串的单词Ⅲ
给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
- 快慢指针来反转
class Solution {
public:
string reverseWords(string s) {
int fast=0;
int slow=0;
while(fast<s.size())
{
//快指针直到空格的时候,快慢指针之间是一个单词,把这个单词反转
//反转之后,慢指针指向下一个字母
if(s[fast]==' ')
{
int left=slow;
int right=fast-1;
while((left<right)&&(left>=slow)&&(right<=fast-1))
{
char temp=s[left];
s[left]=s[right];
s[right]=temp;
left++;
right--;
}
fast++;
slow=fast;
}
//如果快指针没有指向空格,那么快指针继续往后移动
else{
fast++;
}
}
//单独处理最后一个单词,他最后没有空格所以在前面不会反转
int left=slow;
int right=fast-1;
while(left<right)
{
char temp=s[left];
s[left]=s[right];
s[right]=temp;
left++;
right--;
}
return s;
}
};
寻找旋转排序数组的最小值
已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:
若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]
注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。
给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/array-and-string/c3ki5/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
int findMin(vector<int>& nums) {
int temp=nums[0];
//只需要找到一个比第一个元素小的元素说明就是最小的,因为数组是升序的
for(int i=0;i<nums.size();i++)
{
if(nums[i]<temp)
return nums[i];
}
return temp;
}
};
删除排序数组的重复项
给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
作者:力扣 (LeetCode)
链接:https://leetcode-cn.com/leetbook/read/array-and-string/cq376/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
- 快慢指针
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int size=nums.size();
if(size==0)
return 0;
int fast=1;
int slow=0;
//从头到尾遍历,每次找到一个不重复的数就把慢指针指向的下一个数字改了
for(fast=0;fast<nums.size();fast++)
{
if(nums[fast]!=nums[slow])
{
slow++;
nums[slow]=nums[fast];
}
}
//长度为下标+1
return slow+1;
}
};
移动零
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
class Solution {
public:
void moveZeroes(vector<int>& nums) {
//遇到非零就把零和非零换位置
int slow=0;
int fast=0;
for(;fast<nums.size();fast++)
{
//fast指向0的时候slow不会增加,也就是slow始终指向0
//所以交换slow和fast的元素,就可以把0移到后面
if(nums[fast]!=0)
{
int temp=nums[fast];
nums[fast]=nums[slow];
nums[slow]=temp;
slow++;
}
}
}
};