leetcode-双指针

leetcode-双指针

  • 双指针
    • 1、有序数组-题号167两数之和
    • 2、两数平方和-题号633
    • 3、反转字符串中的元音字符-题号345
    • 4、验证回文字符串-题号680
    • 5、合并两个有序数组-题号88
    • 6、环形链表-题目141
    • 7、最长子序列
    • 参考

双指针

最近开始刷leetcode,并通过分模块练习。这个模块是双指针,所有题目在力扣中的题号我也会标注出来。当然很多解题思路都是借鉴大佬的,这里主要是记录一下自己的想法。

1、有序数组-题号167两数之和

(解法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;
    }
};

2、两数平方和-题号633

特别要注意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;  
    }
};

3、反转字符串中的元音字符-题号345

关于字符串反转,首先要判断左右元素是否都是元音字符。如果左边元素不是,左指针向右移;右边元素不是,右指针向左移。左右指针指向的元素同时是元音字符,交换元素,并同时移动左右指针。

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';
	}	
    
};

4、验证回文字符串-题号680

注意题干是最多删除一个元素,就存在不删除和删除一个元素的两种情况。如果原本不满足回文字符串,就判断删除一个元素后的字符串是否是字符串。注意删除的这个元素可以是左指针指向的元素,也可以是右指针指向的元素。

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 
    }
};

5、合并两个有序数组-题号88

难点是两个指针的移动问题。

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];
		}
    }
};

6、环形链表-题目141

判断是否有环,用到龟兔赛跑算法,如果跑直线乌龟肯定追不上兔子。但是如果有圈,在某一时刻,兔子和乌龟就可能在同一位置。

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;
    }
};

7、最长子序列

首先判断容器中的一个字符串是否是给定字符串的子字符串。然后比较子字符串的长度,如果长度相同再比较字典中的顺序(字符串特有的函数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

你可能感兴趣的:(leetcode,c++)