最近开始刷leetcode,并通过分模块练习。这个模块是双指针,所有题目在力扣中的题号我也会标注出来。当然很多解题思路都是借鉴大佬的,这里主要是记录一下自己的想法。
(解法1:双指针)主要是理解这个数组numbers是个有序的非递减排列的数组,一定要注意是有序的,所以双指针是个比较好的解决方法。定义两个指针left,right。left最开始指向数组的首部,right指向尾部。如果numbers[left] 加上numbers[right]大于target,right向左移位,小于target,left就向右边移位。循环执行该过程,循环执行的条件是left < right。
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int left = 0,right = numbers.size() - 1;
vector<int>arr;
while(left < right){
if(numbers[left] + numbers[right] == target){
arr.push_back(left + 1);
arr.push_back(right + 1);
return arr;
}else if(numbers[left] + numbers[right] > target){
--right;
}else{
++left;
}
}
arr.push_back(-1);
arr.push_back(-1);
return arr;
}
};
(解法2:二分查找)固定一个位置,求另一个值。
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
vector<int>arr;//定义一个用于存储返回位置的vector容器
for(int i = 0;i < numbers.size();i++)
{//固定的位置为i,寻找target - numbers[i]这个数的下标
int low = i + 1,high = numbers.size() - 1; //定义高位和低位
while(low < high)//每一次二分查找的条件为低位小于高位
{
int mid = (high - low)/2 + low;//每次获取中位数
if(numbers[mid] == target - numbers[i]){
//如果中位数满足条件,返回i+1和mid+1
arr.push_back(i + 1);
arr.push_back(mid + 1);
return arr;
}else if(numbers[mid] > target - numbers[i]){
//如果中位数偏大高位变为mid - 1
high = mid -1;
}else{
//如果中位数偏小低位变为mid + 1
low = mid + 1;
}
}
}
arr.push_back(-1);
arr.push_back(-1);
return arr;
}
};
特别要注意int的范围,long型表示的数据更大。
class Solution {
public:
bool judgeSquareSum(int c) {
//这里注意两点:1、high*high + low*low可能大于pow(2,31),所以要定义为long型
//2、sqrt(x)是对x进行开根号,返回值是一个double类型,所以要强转为long型
long low = 0,high = (long)sqrt(c);//低位从0开始,高位从sqrt(c)开始
while(low <= high)//注意这里可以=,因为可能是同一个数的平方和,如1*1 + 1*1 = 2
{
if(low * low + high*high == c){
//如果满足条件返回true
return true;
}else if(low * low + high*high > c){
//结果偏大,高位就向减一
--high;
}else{
//结果偏小,低位就向加一
++low;
}
}
return false;
}
};
关于字符串反转,首先要判断左右元素是否都是元音字符。如果左边元素不是,左指针向右移;右边元素不是,右指针向左移。左右指针指向的元素同时是元音字符,交换元素,并同时移动左右指针。
class Solution {
public:
string reverseVowels(string s) {
int left = 0,right = s.length() - 1;//定义左指针为0,右指针为s.length()-1
while(left < right)//循环结束条件
{
if(isVowel(s[left])&&isVowel(s[right]))
{
//如果目前左右指针指向的元素都是元音字符,交换左右指针指向的元素
char temp = s[left];
s[left] = s[right];
s[right] = temp;
//交换结束后,left指针向右移,right指针向左移
left++;
right--;
}
if(!isVowel(s[left])) left++;//如果左指针指向的元素不是元音字符,左指针向右移
if(!isVowel(s[right])) right--;//如果右指针指向的元素不是元音字符,右指针向左移
}
return s;
}
bool isVowel(char c){//判断是否是元音字符
return c == 'a' || c == 'A'||
c == 'e' || c == 'E'||
c == 'i' || c == 'I'||
c == 'o' || c == 'O'||
c == 'u' || c == 'U';
}
};
注意题干是最多删除一个元素,就存在不删除和删除一个元素的两种情况。如果原本不满足回文字符串,就判断删除一个元素后的字符串是否是字符串。注意删除的这个元素可以是左指针指向的元素,也可以是右指针指向的元素。
class Solution {
public:
bool checkPalindrome(int left,int right,const string& s){
while(left < right){
if(s[left] == s[right]){
//判断完当前数据后,一定要记得移位
left++;
right--;
}else{
return false;
}
}
return true;
}
bool validPalindrome(string s) {
int left = 0,right = s.length() - 1;
while(left < right){
if(s[left] == s[right]){
left++;
right--;
}else{
//如果当高低位对应元素不相等就删除一个元素,删除左元素就是left+1,删除右元素就是right+1
//再利用checkPalindrome判断删除元素后的字符串是否是回文字符串
return (checkPalindrome(left+1,right,s)||checkPalindrome(left,right-1,s));//为什么使用left++,和right--不行
}
}
return true;//如果该字符串本身就是一个回文字符串,就可以直接返回true
}
};
难点是两个指针的移动问题。
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int arr[m + n ];//定义一个数组用于按照顺序的存储元素
int p1 = 0,p2 = 0;//定义p1指向nums1当前元素,p2指向nums2当前元素
int cur;//用于存储当前较小的数,或者一个数组遍历后,另一个数组的剩余元素
while(p1 < m || p2 <n){
if(p1 == m){
//如果nums1的元素已经遍历完,就直接将当前p2指向的值传给cur
cur = nums2[p2++];//cur = nums[p2],p2++;
}else if(p2 == n){
//如果nums2的元素已经遍历完,就直接将当前p1指向的值传给cur
cur = nums1[p1++];
}else if(nums1[p1] < nums2[p2]){
//如果当前p1指向的值小于p2指向的值,将p1指向的值赋给cur
cur = nums1[p1++];
}else{
//如果当前p2指向的值小于p1指向的值,将p2指向的值赋给cur
cur = nums2[p2++];
}
arr[p1 + p2 -1] = cur;//将cur值存储到arr中,arr便成为了一个非递减的顺序数组
}
for(int i = 0;i < nums1.size();i++){
//将arr赋值给nums1
nums1[i] = arr[i];
}
}
};
判断是否有环,用到龟兔赛跑算法,如果跑直线乌龟肯定追不上兔子。但是如果有圈,在某一时刻,兔子和乌龟就可能在同一位置。
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head == NULL || head->next == NULL){
//如果定义头节点为空或者头节点的下一个节点为空,则不可能有环
return false;
}
ListNode*slow = head; //定义慢指针
ListNode*fast = head->next; //定义快指针
while(fast != slow){//循环条件是快慢指针不相等
if(fast == NULL || fast->next==NULL){
//如果快指针为空,或者快指针的下一步为空,则不存在环
return false;
}
slow = slow->next;//慢指针每一次走一步
fast = fast->next->next;//快指针每一次走两步
}
return true;
}
};
首先判断容器中的一个字符串是否是给定字符串的子字符串。然后比较子字符串的长度,如果长度相同再比较字典中的顺序(字符串特有的函数compare)。
class Solution {
public:
string findLongestWord(string s, vector<string>& dictionary) {
vector<string>substr;//定义一个容器存储子字符串
string min;
for(int i = 0; i < dictionary.size();i++){
if(isSubstr(s,dictionary[i])){
substr.push_back(dictionary[i]);
}
}
if(substr.size() == 0){
return " ";//如果没有子串就返回空字符
}else{
min = substr[0];
for(int i = 1;i < substr.size();i++){
if(substr[i].length() > min.length()){
//比较字符的长度
min = substr[i];
}else if (substr[i].length() == min.length()){
//比较字符再字典中的顺序
if(substr[i].compare(min) < 0){
min = substr[i];
}
}
}
}
return min;
}
bool isSubstr(string s,string s2){
//判断是否是字串的函数
int i = 0,j = 0;
while(i<s.length()&&j<s2.length()){
if(s[i] == s2[j]){
//如果当前指向s的元素和指向s2的元素相同,两个指针同时右移
++i;
++j;
}else{
//如果当前指向s的元素和指向s2的元素不相同,指向s的指针右移
i++;
}
}
if(j == s2.length()){
//如果s2已经遍历完,说明s2是s1的子串
return true;
}else{
return false;
}
}
};
博客园 leetcode刷题分类
leetcode