双指针法--数组链表篇

“你的时间有限,所以不要浪费时间过别人的生活。”
                                                                                        ----- 乔布斯


前言

通过一周对数组和链表算法题的练习,发现双指针法在其中运用非常广泛且好用,并对TS的应用更加熟悉,故将这周写过的双指针法总结如下。


移除元素

力扣:移除元素

function removeElement(nums: number[], val: number): number {
    let fast:number=0;
    let slow:number=0;
    while(fast<nums.length){
        if(nums[fast]!=val){
            nums[slow]=nums[fast];
            slow++
        }
        fast++
    }
    return slow
};

本题思路:
双指针的应用:这个双指针,快的指针发现目标数字时继续向下移动,而慢指针则指向目标数字后便不再移动等待快指针的覆盖。

易错点:

  1. 数组无法直接删除元素,只能通过覆盖的形式去移除元素,因为 在TS中,数组存储的地址是连续的

有序数组的平方

力扣:有序数组的平方

function sortedSquares(nums: number[]): number[] {
    let left=0;
    let right=nums.length-1;
     let tag=nums.length-1;
    let i=0;
    let arr = new Array(nums.length);
    while(i<nums.length){
        nums[i]=nums[i]*nums[i];
        i++;
    }
    while(left<=right){
        if(nums[left]>nums[right]){
            arr[tag--]=nums[left++];
        }else if(nums[right]>nums[left]){
            arr[tag--]=nums[right--];
        }else{
            arr[tag--]=nums[left++];
            arr[tag--]=nums[right--];
        }
        
    }
    return arr;
};

本题思路:
双指针的应用:两边大中间小,通过两个指针从两边进行遍历,排序到新数组里。

易错点:

  1. 第一次做题目的时候创建新数组arr,直接把nums赋值给了arr,导致出错,以后操作nums也会影响到arr数组,创建新数组语法let arr = new Array(nums.length);

可以尝试用Math.max


长度最小的子数组

力扣:长度最小的子数组

function minSubArrayLen(target: number, nums: number[]): number {
  let left:number = 0,
  length:number=0,
  sum:number=0.,
  res:number=Infinity;
  for(let right=0;right<nums.length;right++){
    sum+=nums[right];
    while(sum>=target){
        length=right-left+1;
        res=Math.min(res,length);
        sum-=nums[left++];
    }
  }
  return res===Infinity?0:res;
};

本题思路:
题目要求求大于目标数字的最小子数组,同样使用双指针法,快的指针往前遍历数组直到里面的加数大于目标数,遍历停止,返回长度,然后开始移动慢指针,往后移,看是否同样满足。满足后返回长度直到它小于目标数快指针再继续遍历。
该方法又叫滑动窗口

在 TypeScript 中,Infinity 是一个特殊的数值,表示正无穷大。它的类型是 number。


删除链表的倒数第N个节点

力扣:删除链表的倒数第N个节点

function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
    const newNode= new ListNode(0,head);
    
    let current=newNode;
    let slow=newNode;
    let count=0;
    if(n<=0){
        return head;
    }
    while(current){
        if(count>n){
            slow=slow.next;
        }
        current=current.next;
        count++;
    }
    if(slow&&slow.next){slow.next=slow.next.next;}
    return newNode.next;
};

本题思路:
第一次写的时候是分两步,第一步先通过一遍遍历找到倒数第N个节点的位置,再进行删除操作。这样稍微有些啰嗦,知道快慢指针法之后尝试的去做了,通过指针之间的逻辑关系,快指针遍历了第n次之后,慢指针开始从头节点移动正好可以找到想要删除节点的前一个节点。

易错点:

  1. 这题最好使用虚拟头节点,这样的好处是不用思考头删的情况,头删在本题非常的麻烦。

环形链表II

力扣:环形链表


function detectCycle(head: ListNode | null): ListNode | null {
    const newNode=new ListNode(0,head);
    let fast=newNode;
    let slow=newNode;
    let index=newNode;
    let now:ListNode|null=null;
    while(slow&&slow.next&&fast&&fast.next&&fast.next.next){
    slow=slow.next;
     fast=fast.next.next;
    if(slow==fast){
      now=slow;
      break;
    }
    }
    while(now&&now.next&&index&&index.next){
    now=now.next;
    index=index.next;
    if(now==index){return now;}
    }
    return now;

};

本题思路:
在这里插入图片描述
本题算是使用了两次双指针,第一次通过快慢指针确定相遇点位置,第二次通过推导出的数学等式利用双指针找到链表的起始节点,但是本题使用的双指针算是最基础的使用方法。


双指针法总结

为什么要使用双指针?

在处理数组,链表,字符串进行删除,反转等操作的时候,往往需要操作两个以上的元素,使用双指针法可以巧妙地解决这些问题。

此外,它还能降低时间复杂度,使代码运行更高效更简洁。

如何使用双指针去解决问题?

通过做过一些题目,首先在写代码之前画出使用双指针所要达到的效果,例如:

删除链表倒数第N个节点中,你需要找到倒数第N个节点并删掉它,换句话说,你需要找到倒数第N个节点的前一个节点,你才能删掉它。
假设只有一个指针,那么它能做的工作是可以遍历整个链表知道它有x个元素,那么第二个指针就可以通过和第一个指针的联系找到x-n个元素,那就是第一个指针走到n时,它再从起点出发。

无论是滑动窗口,快慢指针,左右指针,根据题目要求画出效果,再使用代码去实现即可。

  • 29
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值