leetcode记录-双指针类型

在家真的闲出个鸟,难得自己刷刷题吧。
从CSNOTES推荐的开始刷,每天刷几道,做个记录。
在这里我先不管优化的事情,先只关心能否A过。

一:双指针类型题目

167. Two Sum II - Input array is sorted (Easy)
这里是对于一个已经排好序的数组找出两个数等于指定的target,返回索引。
思想很简单,利用已经排好序,一个指针放最前面,一个指针放最后面,判断和为多少然后移动指针。发现对于vector还是不熟悉= =
vector的大小应该用vec.size()而不是sizeof(vec)
自己的方法如下,能A不过内存消耗太大。

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
    int p=0;
    int q=numbers.size()-1;
    
    while(p<numbers.size()-1&&q>0)
    {
        int sum=numbers[p]+numbers[q];
      if(sum==target)
        {
           break;
         }
         if(sum<target)
         {
             p++;
         }
         if(sum>target)
         {
             q--;
         }
    }
     vector<int> index={p+1,q+1};
            return index;
    }
};

168. Sum of Square Numbers (Easy)
给出一个正整数,判断是否是两个数的平方的和。例如5=1平方+2平方,5就是平方数。
这里我先设想一下,给出一个整数C,则在1到根号C下取整的数组里面利用双指针平方和找到target©.(后来发现跟参考解法思想一致)
一些常识:
使用floor函数。floor(x)返回的是小于或等于x的最大整数。
如: floor(10.5) == 10 floor(-10.5) == -11
使用ceil函数。ceil(x)返回的是大于x的最小整数。
如: ceil(10.5) == 11 ceil(-10.5) ==-10
平方函数:pow(x,2)就是x平方 sqrt()开根号
方法确定之后遇到的问题却非常多:
一:应考虑0的问题
二:由于我非常死板,硬是多定义了一个vector存储那些数字,其实那些数字是连续的,没有必要,后续也引起了一系列内存控制的问题,并且效率极低。
最终修改之后:

  bool judgeSquareSum(int c) {
        int number=floor(sqrt(c));
       int p=0;int q=number;
        while(p<=q)
        {
            if (pow(p,2)+pow(q,2)==c)
            {
                return true;
            }
            if(pow(p,2)+pow(q,2)<c)
            {
                p++;
            }
            if(pow(p,2)+pow(q,2)>c)
            {
                q--;
            }
        }
        return false;
    }

345. Reverse Vowels of a String (Easy)
这个问题是将一个字符串的元音字母翻转,思路简单:前后两个指针,首先判断是否是元音字母,两个指针都是元音则交换后继续前进。
元音字母有:a e i o u A E I O U(记得大写)
不熟悉对于字符串的操作= =
1.对于string的索引操作,判断size不熟悉,尝试先转成char也错误了
2.对于是否元音的判断由于比较复杂,可以单独写个函数或者一个哈希表辅助
3.使用while进行循环判断的时候里层的while可能会两个指针交叉!!!只有外层控制p<q是不够的!!!!特别注意!!
自己写的臃肿丑陋的解法:

   string reverseVowels(string s) {
       
       int size=s.size(); 
       int p=0,q=size-1;
       while(p<q)
       {
           while(s[p]!='a'&&s[p]!='e'&&s[p]!='i'&&s[p]!='o'&&s[p]!='u'&&p<q&&s[p]!='A'&&s[p]!='E'&&s[p]!='I'&&s[p]!='O'&&s[p]!='U')//不是元音时指针移动,加上p<q!!!!
           {
               p++;
           }
              while(s[q]!='a'&&s[q]!='e'&&s[q]!='i'&&s[q]!='o'&&s[q]!='u'&&p<q&&s[q]!='A'&&s[q]!='E'&&s[q]!='I'&&s[q]!='O'&&s[q]!='U')
           {
               q--;
           }
           //此时都是元音字母
           swap(s[p++],s[q--]);
         
       }
       return s;
    }

优美的解法,思路一致:
while不如用if+continue
continue一次就结束一次循环,例如下面每次循环都只是i++一次或者j–一次(前面的判断都用了continue结束循环),一直到都满足元音条件发生交换。

class Solution {
private:
    bool isOrigin(char c){
        if(c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' || 
            c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U')
            return true;
        else
            return false;
    }
public:
    string reverseVowels(string s) {
        int i = 0,j = s.size();
        while(i < j){
            if(!isOrigin(s[i])){
                i++;
                continue;
            }
            if(!isOrigin(s[j])){
                j--;
                continue;
            }
            swap(s[i++],s[j--]);
        }
        return s;
    }
};

680. Valid Palindrome II (Easy)
回文字符串,判断一个字符串是否在至多删掉一个字符后是回文字符串。
想法是:两边的指针同时开始向中间靠,如果不一样则跳出循环,判断这个字符是否在删去一个字符之后仍然是回文字符串,分为删前面和删后面两种情况,如果直接按思路写很麻烦(判断条件),这里要写一个判断是否是严格回文字符串的函数更为简便。

别人的写法:

class Solution 
{
public:
    bool is(string s,int i,int j)  //判断是不是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 is(s,i+1,j) || is(s,i,j-1);
            }
        }
        //跳出循环说明始终相等,即为回文字符串
        return true;

    }
};

tips:重点学习别人的代码风格和实现。

88. Merge Sorted Array (Easy)
合并两个有序数组,比较烦的是后面的0,以及特殊情况的考虑(m=0,n=0),最后采用的是比较笨的把第二个数组的数字插到第一个后面然后用冒泡排序重排,效率很低。

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
       int j=0;
       if(n==0)
       {
           return;
       }
       if(m==0)
       {
           for(int j=0;j<n;j++)
           {
               nums1[j]=nums2[j];
           }
       }
       for(int i=m;i<m+n;i++)
       {  
           if(nums1[i]==0)
           {
               nums1[i]=nums2[j];
               j++;
           }
       }
       for(int i=0;i<m+n-1;i++)//采用大数下沉的方式
       {
           for(int j=0;j<m+n-i-1;j++)
           {
              if(nums1[j]>nums1[j+1])
              {   int temp;
                  temp=nums1[j+1];
                  nums1[j+1]=nums1[j];
                  nums1[j]=temp;//交换位置
              }
           }
       }
    }
};

推荐解法:
官方有一种很吊的解法写在这记录下,双(三)指针在两个数组从大的(末尾)开始比较,p1指向nums1最末尾,p2指向nums2最末尾,p1p2比较之后填到p的格子,移动p以及p1或者p2较大的那一个,p指针指向nums1即将要填写的那个格子,p2<=0时结束算法

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int tail=m+n-1,tail1=m-1,tail2=n-1;
        while(tail!=tail1)
        {
            if(tail1>=0 && nums1[tail1]>nums2[tail2]) nums1[tail--]=nums1[tail1--];
            else nums1[tail--]=nums2[tail2--];
        }
    }
};

524. Longest Word in Dictionary through Deleting (Medium)
这个算是码起来最顺的,不过奇怪的是有个测试用例跟题目意思不太一样?
题目描述:删除 s 中的一些字符,使得它构成字符串列表 d 中的一个字符串,找出能构成的最长字符串。如果有多个相同长度的结果,返回字典序的最小字符串。
用例:
s:“bab”
d:[“ba”,“ab”,“a”,“b”]

预期:“ab”
按道理来说最长的是前两个,字符串s都有,ba在前面,应当返回ba啊?

俺最开始写的做法:

class Solution {
public:
//判断这个字符A是否包含字符B
bool exist(string A, string B)
{
  int p=0,q=0;
 while(q<B.size()&&p<A.size())
 {
     if(A[p]==B[q])
     {
         p++;
         q++;
     }
     else
     {
         p++;
     }
 }
 if(q==B.size())
 {
     return true;
 }
 else
 {
     return false;
 }
}
    string findLongestWord(string s, vector<string>& d) {
        int  length=0;
        int  xuhao=-1;
     for(int i=0;i<d.size();i++)
     {
         if(exist(s,d[i])&&d[i].size()>length)
         {   length=d[i].size();
             xuhao=i;
         }
     }
     if(xuhao>=0)
    {
     return d[xuhao];
    }
    else
    {
        return "";
    }
    }
};

在题解区发现一个几乎一样的解法:

class Solution 
{
private:
    bool isZichuan(string target, string s)
    {   
        //分别从左端开始索引,检测是否为子列
        int i = 0, j = 0;
        while(i < target.size() && j < s.size())
        {
            if(target[i] == s[j])
                i++;
            j++;  
        }
        return i == target.size();
    }

public:

    string findLongestWord(string s, vector<string>& d) 
    {
        string str = "";
        for(int i = 0; i < d.size(); i++)
        {
            int tag = str.length();
            int leng = d[i].length();
            //若字符串更短或者一样长且字母顺序较大的直接舍去
            if(tag > leng || (tag == leng && str.compare(d[i]) < 0))
                continue;
            
            if(isZichuan(d[i], s))
            {
                str = d[i];
            }
        }
        return str;
    }
};

不同之处就在于这个compare函数
int compare(const basic_string& __str)
和str按字典序比较大小,若小于str返回小于0的值,若等于str返回0,若大于str返回大于0的值

compare 是逐字符比较的 从第一位开始 若相同则比较下一字符 若不同 就马上出结果了 如"dog”与"cat dog cat"相比的话,第一字符d大于c则 "dog”大于"cat dog cat。
字典排序:
先比较第一个字符
i 和 b
b<i b是第2个 ,i是第9个 2 < 9
于是 baray < ilove
如果第一位相同,就比较第二位,
例如:
abcdd<abcde
aaaay<aaaaz
如果其中之一是另一个的前缀,则短的那个排前面:
aaa < aaab
所以略微参考他修改了下就过了

class Solution {
public:
//判断这个字符A是否包含字符B
bool exist(string A, string B)
{
  int p=0,q=0;
 while(q<B.size()&&p<A.size())
 {
     if(A[p]==B[q])
     {
         p++;
         q++;
     }
     else
     {
         p++;
     }
 }
 if(q==B.size())
 {
     return true;
 }
 else
 {
     return false;
 }
}
    string findLongestWord(string s, vector<string>& d) {
        int  length=0;
        int  xuhao=-1;
        string str="";
     for(int i=0;i<d.size();i++)
     {
         if((exist(s,d[i])&&d[i].size()>length)||(exist(s,d[i])&&d[i].size()==length&&str.compare(d[i])>0 ))
         {   
             str=d[i];
             length=d[i].size();
             xuhao=i;
         }
     }
     if(xuhao>=0)
    {
     return d[xuhao];
    }
    else
    {
        return "";
    }
    }
};

141. Linked List Cycle (Easy)
给定一个链表,判断链表中是否有环
对链表的操作一点都不熟悉= =题目也不是太懂,直接看答案改天再做一次

解法:使用双指针,一个指针每次移动一个节点,一个指针每次移动两个节点,如果存在环,那么这两个指针一定会相遇。

/**
 * 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) 
{
	//两个运动员位于同意起点head
	ListNode* faster{ head };  //快的运动员
	ListNode* slower{ head };  //慢的运动员
	//ListNode* slow = head;
    //ListNode* fast = head;//也可以这么写

	if (head == NULL)  //输入链表为空,必然不是循环链表
		return false;

	while (faster != NULL && faster->next != NULL)
	{
		faster = faster->next->next;  //快的运动员每次跑两步
		slower = slower->next;  //慢的运动员每次跑一步
		if (faster == slower)  //他们在比赛中相遇了
			return true;  //可以断定是环形道,直道不可能相遇
	}
	return false;  //快的运动员到终点了,那就是直道,绕圈跑不会有终点
}
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值