• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
acmer4302
博客园    首页    新随笔    联系   管理    订阅  订阅
LeetCode 633,167,345,680,88,141,524 #双指针专题

#双指针专题


LeetCode 633 平方数之和#双指针专题

  • 1.分析题意:对于题目本身的描述,一些边界条件或者非法输入,提出自己的问题。像面试官表明你对问题已经有了一定的思考

    • 给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得 a2+b2=c

    • 0 <= c <= 2^31 - 1

    • 首先需要注意a^2超int的问题 可以使用double

    • 判断一个数是否为可开方数

  • 2.描述一个大体思路:通常我们可以先给出一个最基本的暴力解法,再进一步去思考优化方法;

    • 暴力解法

      1. 从前往后枚举++i,j=i*i<=c

      2. 判断sqrt(c-j)是否是可开方数 sqrt(c-j)==(int)(sqrt(c-j))

    • 优化解法-双指针

      • i,j同时操作 时间复杂度<sqrt(n)
  • 3.写代码:如果第二步做好了,那么这里其实就是把伪代码或者框图填充完;

    class Solution {
    public:
        bool judgeSquareSum(int c) {
            for(double i=0,j=0;j<=c;j=i*i)
            {
                //cout<<" "<<j<<" "<<(int)(sqrt(c-j))*(int)(sqrt(c-j))<<endl;
                if(sqrt(c-j)==(int)sqrt(c-j))
                {
                    return true;
                }
                i=i+1;
            }
            return false;
        }
    };
    //双指针解法
    class Solution {
    public:
        bool judgeSquareSum(int c) {
            unsigned int i=0,j=(int)sqrt(c);//注意sqrt以后是浮点型数据 必须强转
            for(;i<=j;)
            {
                if(i*i+j*j==c)//这里可能超int 2 31-1
                    return true;
                else if(i*i+j*j<c)
                    i++;
                else
                    j--;
            }
            return false;
            /*
            for(double i=0,j=0;j<=c;j=i*i)
            {
                //cout<<" "<<j<<" "<<(int)(sqrt(c-j))*(int)(sqrt(c-j))<<endl;
                if(sqrt(c-j)==(int)sqrt(c-j))
                {
                    return true;
                }
                i=i+1;
            }
            return false;
            */
        }
    };
    
  • 测试:通过自己给的测试用例发现代码的潜在问题,也是面试考察中的一环。通常我们给出一个边界用例,再结合一个常规用例,一步一步地通过语言描述你的算法做了什么,并且可以在白板旁边标注当前的程序运行状态,来检验最后的答案是否正确;

    • 普通的样例走一遍流程 11
    • 边界值测试是否存在细节错误 2,147,483,648
  • 简单分析一下算法的时间和空间复杂度。

    • 暴力解法

      • 时间:o(sqrt(n))
      • 空间:o(1)
    • 双指针

      • 时间:o(sqrt(n))
      • 空间:o(1)

LeetCode 167 两数之和 II - 输入有序数组#双指针专题

  • 分析题意:

    • 给定一个已按照*升序排列* 的有序数组,找到两个数使得它们相加之和等于目标数。

      函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。

      说明:

      • 返回的下标值(index1 和 index2)不是从零开始的。//从1开始
      • 你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。//不可以重复使用一个元素

      示例:

        输入: numbers = [2, 7, 11, 15], target = 9
        输出: [1,2]
        解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
    
  • 解法:

    • 暴力解法

      • o(n2)找一遍
    • 技巧解法

      • 先标记一遍整个数组,记录值为其下标,然后从头往后找 target-a[i]是否存在标记,存在即可返回记录下标
      • 复杂度o(n)
    • 双指针解法

  • 代码:

    • //技巧解法
      class Solution {
      public:
          vector<int> twoSum(vector<int>& numbers, int target) {
              map<int,int>mp;
              vector<int>ans;
              for(int i=0;i<numbers.size();i++)
              {
                  mp[numbers[i]]=i+1;
              }
              for(int i=0;i<numbers.size();i++)
              {
                  if(mp[target-numbers[i]]&&mp[target-numbers[i]]!=i)
                  {
                      ans.push_back(i+1);
                      ans.push_back(mp[target-numbers[i]]);
                      break;
                  }
              }
              return ans;
          }
      };
      //双指针解法
      class Solution {
      public:
          vector<int> twoSum(vector<int>& numbers, int target) {
              int i=0,j=numbers.size()-1;
              
              for(;i<=j;)
              {
                  if(numbers[i]+numbers[j]==target)
                      break;
                  else if(numbers[i]+numbers[j]<target)
                      i++;
                  else
                      j--;
              }
              vector<int>temp;
              temp.push_back(i+1);
              temp.push_back(j+1);
              return temp;
          }
      };
      
      

LeetCode 345 反转字符串中的元音字母 #双指针专题

  • 题目描述

    • 编写一个函数,以字符串作为输入,反转该字符串中的元音字母。注意大写和小写都要包括

      示例 1:

      输入:"hello"
      输出:"holle"
      

      示例 2:

      输入:"leetcode"
      输出:"leotcede"
      

      提示:

      • 元音字母不包含字母 "y" 。
  • 解题思路

    • 双指针

      • 但是一定要注意分为4种情况

        • i为元音 j为元音

          • 替换之后 i++ j++
        • i为元音 j不为元音

          • j++
        • i不为元音 j为元音

          • i++
        • i j 都不为元音

          • i++,j--
  • 代码

    • class Solution {
      public:
          string reverseVowels(string s) {
              map<char,bool>mp;
              char t;
              int i=0,j=s.length()-1;
              mp['a']=mp['e']=mp['i']=mp['o']=mp['u']=mp['A']=mp['E']=mp['I']=mp['O']=mp['U']=true;
              //字母记得大小写的坑点
              //if else if 条件判断充分了
              for(;i<=j;)
              {
                  if(mp[s[i]]&&mp[s[j]])
                  {
                      //cout<<i<<" "<<j<<endl;
                      t=s[i];//string的单个字符可以和char进行交换 大胆使用
                      s[i]=s[j];
                      s[j]=t;
                      i++,j--;
                      continue;
                  }
                  else if(mp[s[i]]&&!mp[s[j]])//这种的以后大胆的使用就行了
                      j--;
                  else if(mp[s[j]]&&!mp[s[i]])
                      i++;
                  else
                      i++,j--;
              }
              return s;
          }
      };
      
  • 时间复杂度

    • o(n)

LeetCode 680 验证回文字符串 Ⅱ #双指针专题

  • 题目描述:

    • 给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。

      示例 1:

      输入: "aba"
      输出: True
      

      示例 2:

      输入: "abca"
      输出: True
      解释: 你可以删除c字符。
      

      注意:

      1. 字符串只包含从 a-z 的小写字母。字符串的最大长度是50000。
  • 思路

    • 双指针方法
      • i=0 j=length-1
      • 从前往后筛选 只能换一次 使用flag标记次数
      • 如果不能匹配
        • 首先查看flag是否标记
          • 未标记
            • 左+1 和右 左和右-1都可
              • 需要判断谁的下一个移动还满足条件
              • 注意 都不满足 也有有一个操作 来跳出循环
            • 左+1可
            • 右-1可
            • 左右都不可
          • 标记了
      • 能匹配
        • 左+1 右-1
  • 代码:

    • //换几次 第一个坑
      //怎么换 第二个坑
      //第三个坑 没有枚举全 因为第一中两种都可以的情况可以单独考虑成一种成立即可 if(s[i+2]==s[j-1])i++ else if(s[j-2]==s[i+1])   j--; else i++;
      //第四个坑 i+1 i+2 j-1 j-2 没考虑越界的情况
      //总结:记住各个条件的组合  想全了
      class Solution {
      public:
          bool validPalindrome(string s) {
              int i=0,j=s.length()-1;
              bool flag=false;//避免第一个坑
              if(j==1||j==0)
                  return true;
              for(;i<=j;)
              {
                  if(s[i]==s[j])
                      i++,j--;
                  else
                  {
                      if(!flag)
                      {
                          flag=true;//避免第二个坑
                          if(s[i+1]==s[j]&&s[j-1]==s[i])
                          {
                              if(s[i+2]==s[j-1])
                                  i++;
                              else if(s[j-2]==s[i+1])
                                  j--;
                              else
                                  i++;
                          }
                          else if(s[i+1]==s[j])
                              i++;
                          else if(s[j-1]==s[i])
                              j--;
                          else
                              return false;
                      }
                      else
                          return false;
                  }
              }
              return true;
          }
      };
      
  • 时间复杂度

    • o(n)

LeetCode 88 合并两个有序数组 #双指针专题

  • 题目描述

    给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。

    初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。你可以假设 nums1 有足够的空间(空间大小等于 m + n)来保存 nums2 中的元素。

    示例 1:

    输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
    输出:[1,2,2,3,5,6]
    

    示例 2:

    输入:nums1 = [1], m = 1, nums2 = [], n = 0
    输出:[1]
    

    提示:

    • 0 <= m, n <= 200
    • 1 <= m + n <= 200
    • nums1.length == m + n
    • nums2.length == n
    • -109 <= nums1[i], nums2[i] <= 109
  • 思路分析

    • 直观排序

      直接先把nums1的m后的数pop_back()掉,然后把nums2 push_back()进去

      最后把nums1进行排序

    • 双指针

      1. 开始思考的从前往后找出后一个数组的各位数位置,但是一直卡在这个交换覆盖的位置,需要如何记录,往第二个数组里存 还不知道如何去放,实际上思考到是死路一条,就应该去换一个思路
      2. 找出两个数组中最小的放在第一个数组的前方 需要三个指针 但存在覆盖问题
      3. 找出两个数组中最大的 就是指针从最后往前扫描 放在第一个数组后方 正确思路
  • 代码

    //暴力200 nlogn 排序可以
    //LeetCode不需要我们来输出
    //双指针 没想起来思路 
    //找最小值 往nums1中放 会覆盖数据
    //找最大值 谁大谁放  然后往前移动
    //谁移动完  对方移动 直接往里放
    
    class Solution {
    public:
        void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
            //双指针解法
            int i=m-1,j=n-1,k=m+n-1;
            for(;i>=0||j>=0;)//i,j的下标先行判断 且应该为有一个条件满足就可以
            {
                if(i<0)
                    nums1[k--]=nums2[j--];
                else if(j<0)
                    nums1[k--]=nums1[i--];
                else if(nums1[i]>=nums2[j])
                    nums1[k--]=nums1[i--];
                else if(nums2[j]>nums1[i])
                    nums1[k--]=nums2[j--];
            }
            /*暴力解法
            for(int i=0;i<n;i++)
                nums1.pop_back();
            for(int i=0;i<n;i++)
            {
                nums1.push_back(nums2[i]);
            }
            sort(nums1.begin(),nums1.end());
            */
        }
    };
    
  • 时间复杂度

    • 双指针 o(n)
    • 暴力 o(nlogn)

LeetCode 141 环形链表 #双指针专题

  • 题目描述

    • 给定一个链表,判断链表中是否有环。

      如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

      如果链表中存在环,则返回 true 。 否则,返回 false 。

      //注意实例里的输入 pos 只是为了我的输入方便

      进阶:

      你能用 O(1)(即,常量)内存解决此问题吗?

      示例 1:

      img

      输入:head = [3,2,0,-4], pos = 1
      输出:true
      解释:链表中有一个环,其尾部连接到第二个节点。
      

      示例 2:

      img

      输入:head = [1,2], pos = 0
      输出:true
      解释:链表中有一个环,其尾部连接到第一个节点。
      

      示例 3:

      img

      输入:head = [1], pos = -1
      输出:false
      解释:链表中没有环。
      

      提示:

      • 链表中节点的数目范围是 [0, 104]
      • -105 <= Node.val <= 105
      • pos 为 -1 或者链表中的一个 有效索引 。
  • 方法解析

    • 开始暴力思路 是死循环 考虑不周 从某点开始
    • 双指针思路
      • i每次走一步 j每次走两步 这样存在环的话 他们一定会相遇 证明省略 这个是参考github上的思路
  • 代码展示

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
     //一开始的暴力思路>n^2 有时候还是死循环
     //双指针思路  i每次走一步 j每次走两步 这样存在环的话 他们一定会相遇 证明省略
    class Solution {
    public:
        bool hasCycle(ListNode *head) {
            if(head==NULL)//i不能指向空最好
                return false;
            ListNode *i=head,*j=head->next;//避免一开始 他们俩就假相等
            while(i!=NULL&&j!=NULL&&j->next!=NULL)
            {
                if(i==j)
                    return true;
                else
                    i=i->next,j=j->next->next;
            }
            return false;
        }
    };
    
  • 时间复杂度


LeetCode 524 通过删除字母匹配到字典里最长单词 #双指针专题

  • 题目描述

    • 给定一个字符串和一个字符串字典,找到字典里面最长的字符串,该字符串可以通过删除给定字符串的某些字符来得到。如果答案不止一个,返回长度最长且字典顺序最小的字符串。如果答案不存在,则返回空字符串。

      示例 1:

      输入:
      s = "abpcplea", d = ["ale","apple","monkey","plea"]
      
      输出: 
      "apple"
      

      示例 2:

      输入:
      s = "abpcplea", d = ["a","b","c"]
      
      输出: 
      "a"
      

      说明:

      1. 所有输入的字符串只包含小写字母。
      2. 字典的大小不会超过 1000。
      3. 所有输入的字符串长度不会超过 1000。
  • 方法解析

    • 排序+剪支

      • 排序完了以后 进行子串匹配 第一个匹配的就是结果
    • 不排序

      • 拿target记录被匹配的结果

      • 后续的匹配项先和他进行比较进行剪枝

  • 代码展示

    class Solution {
    public:
        static bool cmp(string s1,string s2)
        {
            if(s1.length()!=s2.length())
            {
                return s1.length()>s2.length();
            }
            else
                return s1<s2;
        }
        string findLongestWord(string s, vector<string>& d) {
            /*不排序 + 剪枝
            string target="";
            for(int ii=0;ii<d.size();ii++)
            {
                if(target.length()>d[ii].length()||(target.length()==d[ii].length()&&target<d[ii]))
                    continue;
                int i=0,j=0;
                while(i<s.size()&&j<d[ii].size())
                {
                    if(s[i]==d[ii][j])
                        i++,j++;
                    else
                        i++;
                }
                if(j==d[ii].size())
                    target=d[ii];
            }
            return target;*/
            //排序剪纸 方法   效率不太行   
            sort(d.begin(),d.end(),cmp);
            //我的暴力代码 时间复杂度是o(n^2)
            //此题可以不排序 也可以 但是需要对比完所有的可能结果
            //剪枝是通用方法来优化
            for(int ii=0;ii<d.size();ii++)
            {
                if(s.length()<d[ii].length()||(s.length()==d[ii].length()&&(s<d[ii]||s>d[ii])))
                    continue;
                int i=0,j=0;
                for(;i<s.length()&&j<d[ii].length();)
                {
                    if(s[i]==d[ii][j])
                        i++,j++;
                    else
                        i++;
                }
                if(j==d[ii].size())
                    return d[ii];
            }
            return "";
            
        }
    };
    
  • 时间复杂度

    • 排序+剪支
      • o(n^2)
    • 双指针解法
      • o(n^2)
posted on 2021-01-13 21:30  wjs_ouc  阅读(139)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3