1.有序数组的两数之和
解题思路:
这道题和力扣第一题无序的两数之和不同,因为是有序的,所以可以考虑利用双指针,
双指针思想:左指针指向最小的数值,从头向尾遍历,右指针指向最大的数值,从尾向头遍历。
- 如果target==sum,则返回结果
- 如果target>sum;则要将sum变大,移动左指针;
- 如果target<sum,则要将sum变小,移动右指针; 代码如下:
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
vector<int>ans;
int l=0,r=numbers.size()-1;
while(l<r){
if(numbers[l]+numbers[r]==target){
ans.push_back(l+1);
ans.push_back(r+1);
}
if(numbers[l]+numbers[r]<target){
l++;
}else{
r--;
}
}
return ans;
}
};
2.两数平方和
解题思路:方法和上面一样,确定左右边界即可,左边界为0,有边界为根号target;注意这题要利用long long 类型,否则会溢出。
class Solution {
public:
bool judgeSquareSum(int c) {
if(c==0) return true;
long long l=0,r=sqrt(c);
while(l<=r){
long long sum=l*l+r*r;
if(sum==c){
return true;
}else if(sum>c){
r--;
}else{
l++;
}
}
return false;
}
};
3.反转字符串中的元音字符
解题思路:这题先要遍历一边字符串,把所有的元音字符存在一个容器中,可以是哈希表或是vector中,然后利用左右指针交换元音字符,注意元音字符包括大写的。
class Solution {
public:
string reverseVowels(string s) {
vector<int>str;
string ans=s;
for(int i=0;i<s.size();i++){
if(s[i]=='a'||s[i]=='e'||s[i]=='i'||s[i]=='o'||s[i]=='u'||s[i]=='A'||s[i]=='E'||s[i]=='I'||s[i]=='O'||s[i]=='U'){
str.push_back(i);
}
}
int l=0,r=str.size()-1;
while(l<=r){
ans[str[l]]=s[str[r]];
ans[str[r]]=s[str[l]];
l++;
r--;
}
return ans;
}
};
优化方法:上面需要利用额外的空间,可以一边遍历一遍交换,当头尾指针都遇到元音时交换两个字符。可以将全部元音字符存到一个字符串中
注意:npos可以表示string的结束位子,是string::type_size 类型的,也就是find()返回的类型。find函数在找不到指定值得情况下会返回string::npos。
class Solution {
public:
string reverseVowels(string s) {
string v = "aeiouAEIOU";
int i = -1,j = s.size();
while(i<j){
while(i<j && v.find(s[++i])==v.npos);
while(i<j && v.find(s[--j])==v.npos);
swap(s[i],s[j]);
}
return s;
}
};
作者:SnowD
链接:https://leetcode-cn.com/problems/reverse-vowels-of-a-string/solution/shuang-zhi-zhen-jian-ji-dai-ma-by-snow
4.回文字符串
解题思路:本题的关键是处理删除一个字符。在使用双指针遍历字符串时,如果出现两个指针指向的字符不相等的情况,我们就试着删除一个字符,再判断删除完之后的字符串是否是回文字符串。在试着删除字符时,我们既可以删除左指针指向的字符,也可以删除右指针指向的字符。
class Solution {
public:
//构造函数判断删除后的字符是否是回文字符串
bool isPalindrome(string s,int i,int j){
while(i<j){
if(s[i++]!=s[j--]){
return false;
}
}
return true;
}
bool validPalindrome(string s) {
for(int i=0,j=s.size()-1;i<j;i++,j--){
if(s[i]!=s[j]){
//删除左指针或者右指针后在判断
return isPalindrome(s,i,j-1)||isPalindrome(s,i+1,j);
}
}
return true;
}
};
5.判断链表是否存在环
解题思路:设置双指针,一个快指针,一个慢指针,慢指针每次移动一个节点,快指针每次移动两个节点,如果存在环,那么这两个指针一定会相遇。 其中利用哈希表unordered_set也可以unordered_set.count()返回值为0或1,存在x,返回1,反之,返回0
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head==NULL) return false;
//双指针,都从头节点开始,一个快指针一次走两步,一个慢指针走一步,如果存在环,则一定会重合。
ListNode *p1=head,*p2=head;
while(p2!=NULL&&p2->next!=NULL){
p1=p1->next;
p2=p2->next->next;
if(p1==p2){
return true;
}
}
return false;
}
};
//利用哈希表存
unordered_set<ListNode*>us;
while(head){
if(us.count(head)){
return true;
}
us.insert(head);
head=head->next;
}
return false;
链表中环的入口节点
题目描述:
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
解题思路:
设置双指针,一个慢指针,一个快指针,慢指针一次移动一步,快指针一次移动两步,如果链表中存在环,则一定会相遇,当第一次相遇后,将快指针设置为头节点,然后快指针和慢指针每次移动一步,则下次相遇一定是环的入口节点;
代码实现
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if(pHead==nullptr||pHead->next==nullptr) return nullptr;
ListNode *p1=pHead;
ListNode *p2=pHead;
//快指针和快指针->next不为空;
while(p2&&p2->next){
p1=p1->next;
p2=p2->next->next;
if(p1==p2) break;
}
//if(!p2||!p2->next) return nullptr;
p2=pHead;
while(p2!=p1){
p2=p2->next;
p1=p1->next;
}
return p1;
}
};
剑指offer–和为S的连续正序序列
题目描述
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列?输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序
解题思路
解法一:利用三层暴力循环的,第一层左边界,第二层为右边界,第三层从左到右遍历计算和,如果相等则加入结果中。
解法二:利用左右指针,滑动窗口法,i表示左边界,j表示右边界
1.初始化,i=1,j=1, 表示窗口大小为0
2.如果窗口中值的和小于目标值sum, 表示需要扩大窗口,j += 1
3.否则,如果窗口值和大于目标值sum,表示需要缩小窗口,i += 1
4.否则,等于目标值,存结果,缩小窗口,继续进行步骤2,3,4.
代码实现
//三层暴力解法:时间复杂度为O(N^3),空间复杂度为O(1)
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
vector<vector<int>>res;
for(int i=1;i<=sum/2;i++){
for(int j=i+1;j<sum;j++){
int tmp=0;
for(int k=i;k<=j;k++){
tmp+=k;
}
if(sum==tmp){
vector<int>ans;
for(int k=i;k<=j;k++){
ans.push_back(k);
}
res.push_back(ans);
}else if(tmp>sum){
break;
}
}
}
return res;
}
};
//解法二:滑动窗口法,时间复杂度为O(n),空间复杂度为O(1)
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
vector<vector<int >>res;
int tmp=0;
int l=1,r=1;
while(l<=sum/2){
if(tmp<sum){
tmp+=r;
r++;
}else if(tmp>sum){
tmp-=l;
l++;
}else{
vector<int>ans;
for(int i=l;i<r;i++){
ans.push_back(i);
}
tmp-=l;
l++;
res.push_back(ans);
}
}
return res;
}
};
剑指offer—和为S的两个数字
题目描述
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
解题思路
利用双指针,一个指向数组头部,一个指向数组尾部,计算两个指针指向的数字之和,如果小于S则左指针向右移,如果大于S则右指针向左移。由于第一个满足体条件一定是乘积最小的,故找到满足条件的break即可。
代码实现
class Solution {
public:
vector<int> FindNumbersWithSum(vector<int> array,int sum) {
vector<int >res;
int r=array.size()-1;
if(r<0||sum<=array[0]) return res;
int l=0,tmp=0;
while(l<=r){
tmp=array[l]+array[r];
if(tmp<sum){
l++;
}else if(tmp>sum){
r--;
}else{
res.push_back(array[l]);
res.push_back(array[r]);
break;
}
}
return res;
}
};