1、两数之和
题目描述:给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
//C---暴力解法
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* twoSum(int* nums, int numsSize, int target, int* returnSize){
//暴力解法 扫描整个数组 找到和为target的两个元素
int i=0,j=0;
*returnSize=2;//返回returnSize数组的长度为2
for(i=0;i<numsSize-1;i++)//i最多移动至倒数第二个元素
for(j=i+1;j<numsSize;j++)//j最多移动至最后一个元素
{
if(nums[i]+nums[j]==target)
{
returnSize=(int* )malloc(sizeof(int)*2);
returnSize[0]=i;
returnSize[1]=j;
}
}
return returnSize;
}
//C++---map使用
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
//哈希解法
map<int,int>myMap;//第一个参数代表键 第二个参数代表值
vector<int> ans(2,-1);//存放答案 初始化两个值为-1的元素
for(int i=0;i<nums.size();i++)
{//扫描整个vector
if(myMap.count(target-nums[i])>0)//说明target-nums[i]存在 即与当前扫描到的元素和为target的另一个元素在myMap中已经存在
{
ans[0]=myMap[target-nums[i]];
ans[1]=i;
break;
}
//否则将当前扫到的元素插入
myMap[nums[i]]=i;
}
return ans;
}
};
2、两数相加
题目描述:
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode *head=nullptr;
ListNode *tail=nullptr;
int carry=0;//存储进位位
while(l1||l2)
{
//巧妙的地方在于取加数的时候 如果链表不空 则取其值 否则取0
int n1=l1?l1->val:0;//第一个加数
int n2=l2?l2->val:0;//第二个加数
int sum=n1+n2+carry;//两个加数相加与进位位求和
if(!head)
{
head=tail=new ListNode(sum%10);//头结点 存储和对10取余
}else
{
tail->next=new ListNode(sum%10);//使用尾结点维护存储顺序
tail=tail->next;//更新尾结点
}
carry=sum/10; //进位是和除以10
if(l1) l1=l1->next;//l1不空 则向后
if(l2) l2=l2->next;//l2不空 则向后
}
if(carry>0) tail->next=new ListNode(carry);//若最终进位位还有 则新创一个节点
//并让尾结点指向它
return head;//返回头结点
}
};
3、无重复的最长子串
题目描述:给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
//c++版
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_set<char> uset;//无序set容器 容器内存储的各个元素的值互不相等且不对对容器内部的数据进行排序
if(s.size()==0) return 0;//所给字符串为空 没有最长子串
int MaxStr=0;//初始化最长子串为0
int left=0;//工作指针 初始化为0
//扫描所给字符串
for(int i=0;i<s.size();i++ )
{
//注意 不使用count因为count只会返回0或1 不会返回出现字符个数
while(uset.find(s[i])!=uset.end())
{//说明当前字符存在重复
uset.erase(s[left]);//删除最左边字符
left++;//工作指针向右移动 直到不出现重复
}
MaxStr=max(MaxStr,i-left+1);//更新当前最长子串长度
uset.insert(s[i]);//将当前扫描到的元素加入序列
}
return MaxStr;
}
};
//C语言-双指针算法
int lengthOfLongestSubstring(char * s){
//哈希思想
int locate[127];//字符串的ASCII码值最多不超过127
memset(locate,0,sizeof(locate));//赋初值0
int left=0,right=0;//左、右两个工作指针
int ans=0;//最长子串长度 初始为0
while(s[right])//未扫描到字符串尾
{
if(locate[s[right]]&&left<locate[s[right]])
{//如果扫描到已经出现过的字符 则移动左指针维护窗口
left=locate[s[right]];
}
//每扫描一个字符 记录该字符的位置
locate[s[right]]=right+1;//因为是位置 所以得+1
ans=ans>(right-left+1)?ans:(right-left+1);//更新ans
right++;
}
return ans;
}
4. 寻找两个正序数组的中位数
给定两个大小分别为 m
和 n
的正序(从小到大)数组 nums1
和 nums2
。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n))
class Solution {
public:
double getKth(vector<int> nums1,int start1,int end1,vector<int> nums2,int start2,int end2,int k)
{
int len1=end1-start1+1;//求第一个数组长度
int len2=end2-start2+1;//求第二个数组长度
//始终让nums1数组的长度更小
if(len1>len2) return getKth(nums2,start2,end2,nums1,start1,end1,k);
if(len1==0) return nums2[start2+k-1];//第一个数组为空 返回第二个数组的第k个数即可 下标为start2+k-1
if(k==1) return min(nums1[start1],nums2[start2]);//返回两个数组中更小的那个数
//比较两个数组的第k/2个数
int i=start1+min(k/2,len1)-1;//有可能出现k/2>len1的情况 所以选取较小值
int j=start2+k/2-1;
if(nums1[i]>nums2[j])//舍去第二个数组的前k/2的数
{
return getKth(nums1,start1,end1,nums2,j+1,end2,k-(j-start2+1));//k应减少j-start2+1
}else
{
return getKth(nums1,i+1,end1,nums2,start2,end2,k-(i-start1+1));//与上面类似
}
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int n=nums1.size();
int m=nums2.size();
int left=(n+m+1)/2,right=(n+m+2)/2;//将合并后数组长度为奇数和偶数的情况统一了
//奇数会求两次 如:n=4,m=5则求第5小的数 n=4,m=6则求第5小和第6小的数的均值
return (getKth(nums1,0,n-1,nums2,0,m-1,left)+getKth(nums1,0,n-1,nums2,0,m-1,right))*0.5;
}
};
5、给你一个字符串 s
,找到 s
中最长的回文子串
class Solution {
public:
string longestPalindrome(string s) {
//中心扩展法
if(s.size()==0)//所给子串为空
return "";
int left=0,right=0;//向左右两侧扩展的指针
int len=1;//回文串长度最小为1
int maxLen=0;//记录最长回文串长度 初始为0
int maxLeft=0,maxRight=0;//分别记录最长回文串的最左端、最右端的位置
for(int mid=0;mid<s.size();mid++)
{//从每一个mid出发 扩散
left=mid-1;
right=mid+1;
//向左扩散
while(left>=0&&s[left]==s[mid])
{//当超出左边界 或者左扩散至与当前字符不等时 跳出while
left--;
len++;
}
//向右扩散
while(right<=s.size()-1&&s[mid]==s[right])
{//当超出右边界 或者右扩散至与当前字符不等时 跳出while
right++;
len++;
}
//向两边扩散
while(left>=0&&right<=s.size()-1&&s[left]==s[right])
{//当超过两边边界 或者扩散两端字符不等时跳出while
left--;
right++;
len+=2;//满足向两端扩散条件时 回文串长度+2
}
//更新最长回文串
if(len>maxLen)
{
maxLeft=left;
maxRight=right;
maxLen=len;
}
//每次循环 重新开始 筛选最长回文串时 都应该将len置1
len=1;
}
return s.substr(maxLeft+1,maxLen);//由于left-- 所以应从maxLeft+1处开始拷贝
//s.substr(pos,len)返回s中从pos开始的len个字符 的拷贝
}
};
//解法二
class Solution {
public:
string longestPalindrome(string s) {
int res=0;
int maxLeft=-1,maxRight=s.size();
int len=1;
for(int i=0;i<s.size();i++)
{
int left=i-1,right=i+1;
//当回文串数目为偶数时
while(left>=0&&s[left]==s[i])
{
left--;
len++;
}
//当回文串数目是奇数时
while(left>=0&&right<s.size()&&s[left]==s[right])
{
left--,right++;
len+=2;
}
if(len>res)
{
res=len;
maxLeft=left+1;
maxRight=right-1;
}
len=1;//每次循环 将len置为1
}
return s.substr(maxLeft,res);
}
};
6. N 字形变换
将一个给定字符串 s
根据给定的行数 numRows
,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "PAYPALISHIRING"
行数为 3
时,排列如下:
P A H N A P L S I I G Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"
。
请你实现这个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
*首先 选择什么数据结构来存储?
这里 由于每行是一个字符串 所以可以选择vector<string> 即一个元素为string类型的动态数组
来存储
*
//解法:
class Solution {
public:
string convert(string s, int numRows) {
//特判 当只有一行时 返回原字符串
if(numRows<2) return s;
vector<string> res;
int i=0,flag=-1;//flag来决定是向下走还是向上走
for(auto &c:s)
{
res[i]+=c;
if(i==0||i=numRows-1) flag=-flag;//只有当第一行和最后一行才变向
i+=flag;
}
string str;
for(int i=0;i<numRows;i++)
{
str+=res[i];
}
return str;
}
};
7. 整数反转
给你一个 32 位的有符号整数 x
,返回将 x
中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1]
,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)
*思路:
将数字转换
res=0;
while(n)
{
res=res*10+n%10;
n/=10;
}
*
//解法一
class Solution {
public:
int reverse(int x) {
int res=0;
while(x!=0)
{
int t=x%10;
if(res>214748364||res==214748364&&t>7) return 0;
if(res<-214748364||res==-214748364&&t<-8) return 0;
res=res*10+t;
x/=10;
}
return res;
}
};
//解法二
//当上一次的数与这次的数除以10的结果不等 则溢出
class Solution {
public:
int reverse(int x) {
int res=0;
while(x!=0)
{
int temp=x%10;
int last=res;
res=res*10+temp;
if(last!=res/10)
{
return 0;
}
x/=10;
}
return res;
}
};
8. 字符串转换整数 (atoi)
请你来实现一个 myAtoi(string s)
函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi
函数)。
函数 myAtoi(string s)
的算法如下:
- 读入字符串并丢弃无用的前导空格
- 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
- 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
- 将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为
0
。必要时更改符号(从步骤 2 开始)。 - 如果整数数超过 32 位有符号整数范围
[−231, 231 − 1]
,需要截断这个整数,使其保持在这个范围内。具体来说,小于−231
的整数应该被固定为−231
,大于231 − 1
的整数应该被固定为231 − 1
。 - 返回整数作为最终结果。
注意:
- 本题中的空白字符只包括空格字符
' '
。 - 除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。
class Solution {
public:
int myAtoi(string s) {
int i=0;
while(s[i]==' ') i++;
if(i==s.size()) return 0;
int sign=1;//判断正负
if(s[i]=='-')
{
sign=-1;
i++;
}else if(s[i]=='+') i++;
int res=0;
while(i<s.size())
{
//若不是数字字符 提前判断
if(s[i]<'0'||s[i]>'9')
break;
//判断溢出
if(res>214748364||res==214748364&&(s[i]-'0')>7) return 2147483647;
if(res<-214748364||res==-214748364&&(s[i]-'0')>8) return -2147483648;
res=res*10+sign*(s[i]-'0');
i++;
}
return res;
}
};
9. 回文数
给你一个整数 x
,如果 x
是一个回文整数,返回 true
;否则,返回 false
。
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
- 例如,
121
是回文,而123
不是。
class Solution {
public:
bool isPalindrome(int x) {
//负数不是回文数 同时若x最后一位是0时 只有当x==0才是回文数
if(x<0||(x%10==0&&x!=0)) return false;
int revertedNum=0;
//当x<=revertedNum时 表示回文数反转到了一半
while(x>revertedNum)
{
revertedNum=revertedNum*10+x%10;
x/=10;
}
//当有奇数位时 判断x==revertedNum/10 偶数位 判断x==revertedNum
return x==revertedNum||x==revertedNum/10;
}
};
//解法二
class Solution {
public:
bool isPalindrome(int x) {
//负数不是回文数 同时若x最后一位是0时 只有当x==0才是回文数
if(x<0||(x%10==0&&x!=0)) return false;
int div=1;
while(x/div>=10) div*=10;
while(x)
{
int left=x/div;//首位
int right=x%10;//末位
if(left!=right) return false;
x=(x%div)/10;//x更新
div/=100;//由于舍去了两位 所以div/100
}
return true;
}
}
10. 正则表达式匹配
给你一个字符串 s
和一个字符规律 p
,请你来实现一个支持 '.'
和 '*'
的正则表达式匹配。
'.'
匹配任意单个字符'*'
匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s
的,而不是部分字符串。
*
动态规划:
f[i][j]表示s的前i个字符可以与p的前j个字符匹配
p的第j个字符是一般字符时:
如果s的第i个字符能与之匹配 则f[i][j]=f[i-1][j-1]
p的第j个字符是'*'时:
如果s的第i个字符能与p的第j-1个字符匹配 则f[i][j]=f[i-1][j]或f[i][j-2](匹配0次)
如果s的第i个字符不能与p的第j-1个字符匹配 则f[i][j]=f[i][j-2] 越过后两个字符与第j-2个字符看是否匹配
*
class Solution {
public:
bool isMatch(string s, string p) {
int m=s.size(),n=p.size();
vector<vector<int>> f(m+1,vector<int>(n+1));//返回f[n][m]
//f[i][j]表示s的前i个字符能与p的前j个字符匹配
f[0][0]=true;//两个空字符一定可以匹配
for(int i=0;i<=m;i++)
for(int j=1;j<=n;j++)
{
if(p[j-1]=='*'&&j>=2)//第j个字符是‘*’
{
f[i][j]=f[i][j-2];
if(match(s,p,i,j-1)) f[i][j]=f[i][j]||f[i-1][j];
}else//第j个字符是一般字符
{
if(match(s,p,i,j)) f[i][j]=f[i-1][j-1];
}
}
return f[m][n];
}
bool match(string s,string p,int i,int j)
{
if(i==0) return false;//边界情况
if(p[j-1]=='.') return true;//.可以匹配任意字符
return s[i-1]==p[j-1];
}
};
11. 盛最多水的容器
给定一个长度为 n
的整数数组 height
。有 n
条垂线,第 i
条线的两个端点是 (i, 0)
和 (i, height[i])
。
找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
//思路:
*规定两个指针,一个指针指向最左边i 一个指针指向最右边j
由于最大体积等于 底(j-i)*高(height[i]和height[j]中的最小值)
扫描一遍整个height数组 直至两指针相遇
*
class Solution {
public:
int maxArea(vector<int>& height) {
int res=-1;
int i=0,j=height.size()-1;
while(i<j)
{
res=height[i]<height[j]?max(res,(j-i)*height[i++]):max(res,(j-i)*height[j--]);
}
return res;
}
};