LeetCode刷题总结之双指针法
Leetcode刷题总结
目前已经刷了50道题,从零开始刷题学到了很多精妙的解法和深刻的思想,因此想按方法对写过的题做一个总结
双指针法
双指针法有时也叫快慢指针,在数组里是用两个整型值代表下标,在链表里是两个指针,一般能实现O(n)的时间解决问题,两个指针的位置一般在第一个元素和第二个元素或者第一个元素和最后一个元素,快指针在前“探路”,当符合某种条件时慢指针向前挪
- 盛最多水的容器
这道题其实是求最大面积,最大面积取决于较小值。初始时两指针分别位于第一和最后一个元素处,那么明确指针应该向什么方向移动是解题的关键。既然最大面积取决于较小值,那么指针应向较大值方向移动:当指针移动的时候,底在减小,那么假如向较小值方向移,那么由于底变小,高小于等于前一次的高,此时面积肯定小于之前的面积,每一次移动更新一次面积值。
时间:O(n)
空间:O(1)
代码如下:
- int maxArea(vector<int>& height) {
- int i=,j=height.size()-;
- int maxA=;
- while(j-i>=)
- {
- maxA=max(maxA,(min(height[i],height[j]))*(j-i));
- if(height[i]<=height[j])
- i++;
- else
- j--;
- }
- return maxA;
- }
2. 三数之和
此题的子步骤是两数之和,固定一个数,寻找target=-nums[i]的两个数,采用二分查找的方法(O(logn)),二分法的基础是有序,因此需要先对其进行排序操作
时间:O(nlogn)+O(nlogn)
空间:O(1)结果数组不算
代码如下:
- vector<vector<int>> threeSum(vector<int>& nums) {
- int n=nums.size();
- sort(nums.begin(),nums.end());
- if(n<||nums[]>||nums[n-]<)
- return {};
- vector<vector<int>> res;
- for(int i=;i<n-;i++)
- {
- if(nums[i]>)
- break;
- if(i>&&nums[i]==nums[i-])
- continue;
- int l=i+,r=n-;
- while(l<r)
- {
- if(nums[l]+nums[r]==-nums[i])
- {
- vector<int> t;
- t.push_back(nums[i]);
- t.push_back(nums[l]);
- t.push_back(nums[r]);
- res.push_back(t);
- l++;
- r--;
- while(l<r&&nums[l]==nums[l-])
- l++;
- while(l<r&&nums[r]==nums[r+])
- r--;
- }
- else if(nums[l]+nums[r]<-nums[i])
- l++;
- else
- r--;
- }
- }
- return res;
- }
3. 四数之和
、
此题的子步骤是三数之和,三数之和的子步骤是两数之和,因此要定两个数,寻找剩下的两个
代码如下:
- vector<vector<int>> fourSum(vector<int>& nums, int target) {
- vector<vector<int>> res;
- set<vector<int>> a;
- int n=nums.size();
- sort(nums.begin(),nums.end());
- if(n<)
- return {};
- for(int i=;i<n-;i++)
- {
- for(int j=i+;j<n;j++)
- {
- int l=j+,r=n-;
- while(l<r)
- {
- if(nums[i]+nums[j]+nums[l]+nums[r]==target)
- {
- a.insert(vector<int>{nums[i],nums[j],nums[l],nums[r],});
- l++;
- r--;
- }
- else if(nums[i]+nums[j]+nums[l]+nums[r]>target)
- r--;
- else
- l++;
- }
- }
- }
- for(auto c:a)
- {
- res.push_back(c);
- }
- return res;
- }
4. 最接近三数之和
- int threeSumClosest(vector<int>& nums, int target) {
- int n=nums.size();
- sort(nums.begin(),nums.end());
- int res;
- int min=INT_MAX;
- for(int i=;i<n-;i++)
- {
- int l=i+,r=n-;
- while(l<r)
- {
- if(abs(target-nums[i]-nums[l]-nums[r])<min)
- {
- min=abs(target-nums[i]-nums[l]-nums[r]);
- res=nums[i]+nums[l]+nums[r];
- }
- if(nums[i]+nums[l]+nums[r]>target)
- r--;
- else
- l++;
- }
- }
- return res;
- }
以上是关于数组的一些典型题目,下面是关于链表的一些比较好的例子
5. 删除链表的倒数第N个节点
这道题有姊妹题:获取数组的倒数第N个元素,获取链表的倒数第N个元素,这些题当然可以先遍历一遍获得长度,然后再遍历一遍,但是此时时间是O(2n),而双指针法则可以到达O(n)
初始时将慢指针置于head,快指针置于第n+1个元素处,然后快慢指针通过循环同时向后遍历,直到快指针为NULL,删除慢指针此时指向的节点
为什么是第n+1个呢?因为是要删除倒数第N个元素,需要获得要删除的节点的前一个节点才可以实现删除后仍然连接
时间:O(n)
空间:O(1)
代码如下:
- ListNode* removeNthFromEnd(ListNode* head, int n) {
- if(n==)
- return head;
- if(head==NULL)
- return head;
- ListNode* v=head;
- ListNode* u=head;
- for(int i=;i<n;i++)
- v=v->next;
- if(v==NULL)
- {
- head=head->next;
- return head;
- }
- while(v->next!=NULL)
- {
- u=u->next;
- v=v->next;
- }
- u->next=u->next->next;
- return head;
- }
6. 相交链表
这道题方法有多种,第一种暴力法,第二种利用哈希表,先将A的所有节点插入哈希表种,然后遍历B找到重复的节点,时间是O(m+n),但是空间是O(m)或O(n)
双指针法做法如下:
- 创建两个指针 pApA 和 pBpB,分别初始化为链表
A
和B
的头结点。然后让它们向后逐结点遍历。 当 pApA 到达链表的尾部时,将它重定位到链表 B 的头结点 (你没看错,就是链表 B); 类似的,当 pBpB 到达链表的尾部时,将它重定位到链表 A 的头结点。
- 若在某一时刻 pApA 和 pBpB 相遇,则 pApA/pBpB 为相交结点。
- 如果两个链表相交,那么尾部必然相同
分析:如上图,假如两链表交点之前的长度一样,那么两个指针依次向后遍历,相等时则为交点。上述方法就是让两个指针从同一位置出发,经过相同步数之后同时到达交点
时间:O(m+n)
空间:O(1)
代码如下:
- ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
- if(!headA||!headB) return NULL;
- int m=,n=;
- ListNode *p=headA;
- ListNode *q=headB;
- while(p){
- m++;
- p=p->next;
- }
- while(q){
- n++;
- q=q->next;
- }
- p=headA;
- q=headB;
- if(m>n){
- for(int i=;i<m-n;i++)
- p=p->next;
- }
- if(m<n){
- for(int i=;i<n-m;i++)
- q=q->next;
- }
- while(p&&q&&p!=q){
- p=p->next;
- q=q->next;
- }
- if(!p) return NULL;
- else return p;
- }
7. 环形链表
此题常规方法是哈希表,时间是O(n),空间也是O(n)
双指针法如下:快指针相当于环形跑道上领先的人,慢指针则是落后的人,如果存在环,那么快指针总会追上慢指针而相遇。快指针每次走两步,慢指针每次走一步,快指针相对于慢指针每次走一步。
代码如下:
- bool hasCycle(ListNode *head) {
- if(head==NULL||head->next==NULL)
- return ;
- ListNode* s=head;
- ListNode* f=head->next;
- while(s!=f)
- {
- if(f==NULL||f->next==NULL)
- return ;
- s=s->next;
- f=f->next->next;
- }
- return ;
- }
8. 回文链表
此题思想很简单:找到中点,将后半部分翻转,然后与前半部分比较
子步骤是链表的翻转,这个也是leetcode上的一道题(206 翻转链表),快指针每次走两步,慢指针每次走一步,快相对于慢每次走一步,那么当快指针到了尾部的时候,慢指针在中点,然后将以慢指针为头指针的链表进行翻转,再进行比较
时间:O(n)
空间:O(1)
代码如下:
- bool isPalindrome(ListNode* head) {
- if(head==NULL||head->next==NULL)
- return ;
- if(head->next!=NULL&&head->next->next==NULL)
- {
- if(head->val==head->next->val)
- return ;
- else
- return ;
- }
- ListNode* fast=head;
- ListNode* slow=head;
- for(;fast&&fast->next;slow=slow->next,fast=fast->next->next)
- ;
- ListNode* back=reverseList(slow);
- for(ListNode* front=head;front&&back;front=front->next,back=back->next)
- if(front->val!=back->val)
- return ;
- return ;
- }
- ListNode* reverseList(ListNode* head)
- {
- if(head==NULL||head->next==NULL)
- return head;
- ListNode* pre=NULL;
- ListNode* cur=head;
- ListNode* next=head;
- ListNode* res;
- while(cur!=NULL)
- {
- if(next==NULL)
- res=cur;
- else
- next=next->next;
- cur->next=pre;
- pre=cur;
- cur=next;
- }
- return res;
- }
LeetCode刷题总结之双指针法的更多相关文章
- LeetCode刷题专栏第一篇--思维导图&时间安排
昨天是元宵节,过完元宵节相当于这个年正式过完了.不知道大家有没有投入继续投入紧张的学习工作中.年前我想开一个Leetcode刷题专栏,于是发了一个投票想了解大家的需求征集意见.投票于2019年2月1日 ...
- leetcode 刷题进展
最近没发什么博客了 凑个数 我的leetcode刷题进展 https://gitee.com/def/leetcode_practice 个人以为 刷题在透不在多 前200的吃透了 足以应付非算法岗 ...
- LeetCode刷题指南(字符串)
作者:CYC2018 文章链接:https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Leetcode+%E9%A2%98%E8%A7% ...
- leetcode刷题记录--js
leetcode刷题记录 两数之和 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但 ...
- Leetcode刷题记录(python3)
Leetcode刷题记录(python3) 顺序刷题 1~5 ---1.两数之和 ---2.两数相加 ---3. 无重复字符的最长子串 ---4.寻找两个有序数组的中位数 ---5.最长回文子串 6- ...
- LeetCode刷题总结-数组篇(上)
数组是算法中最常用的一种数据结构,也是面试中最常考的考点.在LeetCode题库中,标记为数组类型的习题到目前为止,已累计到了202题.然而,这202道习题并不是每道题只标记为数组一个考点,大部分习题 ...
- LeetCode刷题总结-数组篇(中)
本文接着上一篇文章<LeetCode刷题总结-数组篇(上)>,继续讲第二个常考问题:矩阵问题. 矩阵也可以称为二维数组.在LeetCode相关习题中,作者总结发现主要考点有:矩阵元素的遍历 ...
- LeetCode刷题总结-数组篇(下)
本期讲O(n)类型问题,共14题.3道简单题,9道中等题,2道困难题.数组篇共归纳总结了50题,本篇是数组篇的最后一篇.其他三个篇章可参考: LeetCode刷题总结-数组篇(上),子数组问题(共17 ...
- LeetCode刷题总结-树篇(下)
本文讲解有关树的习题中子树问题和新概念定义问题,也是有关树习题的最后一篇总结.前两篇请参考: LeetCode刷题总结-树篇(上) LeetCode刷题总结-树篇(中) 本文共收录9道题,7道中等题, ...
随机推荐
- HDU 4812:D Tree(树上点分治+逆元)
题目链接 题意 给一棵树,每个点上有一个权值,问是否存在一条路径(不能是单个点)上的所有点相乘并对1e6+3取模等于k,输出路径的两个端点.如果存在多组答案,输出字典序小的点对. 思路 首先,(a * ...
- Socket编程(C语言实现):socket()函数英文翻译
最近开始研究使用Socket API来网络编程,想着把自己的感想.感悟写下来.我发现在编程之外还有不少概念性的东西要学习.我觉得应该有以下几点吧: 1.得了解下计算机网络的基本概念,如OSI的7层模型 ...
- MyBatis 接口多参数的处理方法
From<MyBatis从入门到精通> 1.接口类中增加的方法: /* 2.7 多个接口参数的用法 多个参数时,可以选取的方案有:使用Map类型或者使用@Param注解 使用Map类型作为 ...
- 列表 元组 range
2019 年 7 月 9 日 列表---list------容器 列表:存储数据,支持多个数据类型,比如 :字符串 数字 布尔值 列表 集合 元组 特点 : 有序 可变 支持索引 (定义一个列表不 ...
- list模板题
题面: 设计一个int类型的动态链表L,L中有一个代表当前位置的光标,支持下列操作: insert(x): 在光标前面插入元素x,插入后光标指向新插入的元素x move(d): 如果d为正数,则光标向 ...
- NameNode和SecondaryNameNode的工作机制
NameNode&Secondary NameNode 工作机制 NameNode: 1.启动时,加载编辑日志和镜像文件到内存 2.当客户端对元数据进行增删改,请求NameNode 3.Nam ...
- [leetcode] 300. Longest Increasing Subsequence (Medium)
题意: 求最长增长的子序列的长度. 思路: 利用DP存取以i作为最大点的子序列长度. Runtime: 20 ms, faster than 35.21% of C++ online submissi ...
- c实现生产者消费者问题。 windows下。
#include <stdio.h>#include <windows.h> #define P(S) WaitForSingleObject(S,INFINITE)//定义W ...
- UTF-16 -- 顶级程序员也会忽略的系统编码问题,JDK 错了十年!
Unicode(统一码.万国码.单一码)是计算机科学领域里的一项业界标准,包括字符集.编码方案等.Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一 ...
- 原生 js基础常用的判断和循环
原生 js基础常用的判断和循环 以下部分是个人实践及和搜集的资料: 最常用的if判断语句: if (/* 条件表达式 */){ // 成立执行语句 } else { // 否则执行语句 } 原生js的 ...