Leetcode刷题总结

目前已经刷了50道题,从零开始刷题学到了很多精妙的解法和深刻的思想,因此想按方法对写过的题做一个总结

双指针法

双指针法有时也叫快慢指针,在数组里是用两个整型值代表下标,在链表里是两个指针,一般能实现O(n)的时间解决问题,两个指针的位置一般在第一个元素和第二个元素或者第一个元素和最后一个元素,快指针在前“探路”,当符合某种条件时慢指针向前挪

  1. 盛最多水的容器

这道题其实是求最大面积,最大面积取决于较小值。初始时两指针分别位于第一和最后一个元素处,那么明确指针应该向什么方向移动是解题的关键。既然最大面积取决于较小值,那么指针应向较大值方向移动:当指针移动的时候,底在减小,那么假如向较小值方向移,那么由于底变小,高小于等于前一次的高,此时面积肯定小于之前的面积,每一次移动更新一次面积值。

时间: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刷题总结之双指针法的更多相关文章

  1. LeetCode刷题专栏第一篇--思维导图&时间安排

    昨天是元宵节,过完元宵节相当于这个年正式过完了.不知道大家有没有投入继续投入紧张的学习工作中.年前我想开一个Leetcode刷题专栏,于是发了一个投票想了解大家的需求征集意见.投票于2019年2月1日 ...

  2. leetcode 刷题进展

    最近没发什么博客了 凑个数 我的leetcode刷题进展 https://gitee.com/def/leetcode_practice 个人以为 刷题在透不在多  前200的吃透了 足以应付非算法岗 ...

  3. LeetCode刷题指南(字符串)

    作者:CYC2018 文章链接:https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Leetcode+%E9%A2%98%E8%A7% ...

  4. leetcode刷题记录--js

    leetcode刷题记录 两数之和 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但 ...

  5. Leetcode刷题记录(python3)

    Leetcode刷题记录(python3) 顺序刷题 1~5 ---1.两数之和 ---2.两数相加 ---3. 无重复字符的最长子串 ---4.寻找两个有序数组的中位数 ---5.最长回文子串 6- ...

  6. LeetCode刷题总结-数组篇(上)

    数组是算法中最常用的一种数据结构,也是面试中最常考的考点.在LeetCode题库中,标记为数组类型的习题到目前为止,已累计到了202题.然而,这202道习题并不是每道题只标记为数组一个考点,大部分习题 ...

  7. LeetCode刷题总结-数组篇(中)

    本文接着上一篇文章<LeetCode刷题总结-数组篇(上)>,继续讲第二个常考问题:矩阵问题. 矩阵也可以称为二维数组.在LeetCode相关习题中,作者总结发现主要考点有:矩阵元素的遍历 ...

  8. LeetCode刷题总结-数组篇(下)

    本期讲O(n)类型问题,共14题.3道简单题,9道中等题,2道困难题.数组篇共归纳总结了50题,本篇是数组篇的最后一篇.其他三个篇章可参考: LeetCode刷题总结-数组篇(上),子数组问题(共17 ...

  9. LeetCode刷题总结-树篇(下)

    本文讲解有关树的习题中子树问题和新概念定义问题,也是有关树习题的最后一篇总结.前两篇请参考: LeetCode刷题总结-树篇(上) LeetCode刷题总结-树篇(中) 本文共收录9道题,7道中等题, ...

随机推荐

  1. 并发编程-concurrent指南-Lock-可重入锁(ReentrantLock)

    可重入和不可重入的概念是这样的:当一个线程获得了当前实例的锁,并进入方法A,这个线程在没有释放这把锁的时候,能否再次进入方法A呢? 可重入锁:可以再次进入方法A,就是说在释放锁前此线程可以再次进入方法 ...

  2. 数据结构与算法---树结构(Tree structure)

    为什么需要树这种数据结构 数组存储方式的分析 优点:通过下标方式访问元素,速度快.对于有序数组,还可使用二分查找提高检索速度. 缺点:如果要检索具体某个值,或者插入值(按一定顺序)会整体移动,效率较低 ...

  3. HDU 2298:Toxophily(推公式)

    http://acm.hdu.edu.cn/showproblem.php?pid=2298 题意:给出一个x,y,v,问从(0,0)以v为初速度射箭,能否射到(x,y)这个点,如果能,输出最小的射出 ...

  4. 上传文件不落地转Base64字符串

    1. 问题描述 因需调用第三方公司的图像识别接口,入参是:证件类型.图像类型.图片base64字符串,采用http+json格式调用. 本来采用的方式是:前端对图片做base64处理,后端组装下直接调 ...

  5. markdown的博客

    测试一下markdown写博客 function firstProgram() { console.log("This is my first markdown blog"); }

  6. 在 Microsoft.VisualStudio.Setup.Engine.Install(Product product, String destination, CancellationToken token)无法在相同位置或现有实例“20cc4971”的子目录上安装指定实例“ebc82a8e”的解决方案

    在所在的安装目录根目录下搜索实例 如 20cc4971 将文件夹全部删除. 一般默认安装在C盘,所以在C盘搜索实例文件夹,将其全部删除.

  7. 根据数据库帮助类采用事务插入图片到sql server数据库中

    我们定义数据库为image类型,然后读取图片为字符流,再保存到数据库中,首先我们定义一个读取图片的公共类,此公共类以后会用到,所以可以建立相应的帮助类 public static byte[] Rea ...

  8. 说说WPF的依赖属性

    首先,我们先来大概了解一下依赖属性 什么是依赖属性:依赖属性自己没有值,通过依赖别人(如Binding)来获得值. 依赖属性为什么会出现:控件常用字段有限,包装太多属性会占用过高内存,造成浪费.所以用 ...

  9. Git更改提交

    提交记录我们的工作历史记录,提交自身是一成不变的.Git提供了几个工具和命令,抓门用来帮助修改完善版本库中的提交. 实际工作中存在很多情况需要我们去修改或返工某个提交或者整个提交序列: 1,可以在某个 ...

  10. [leetcode] 650. 2 Keys Keyboard (Medium)

    解法一: 暴力DFS搜索,对每一步进行复制还是粘贴的状态进行遍历. 注意剪枝的地方: 1.当前A数量大于目标数量,停止搜索 2.当前剪贴板数字大于等于A数量时,只搜索下一步为粘贴的状态. Runtim ...