代码随想录自刷5:双指针

116 阅读3分钟

27. 移除元素

27. 移除元素

思路:双指针,一个指针(right)用来遍历元素:

  1. 当元素不等于要删除元素时,将right指针指向的元素赋值给left指针指向的元素,然后left指针往前移动。
  2. 当元素等于要删除元素时,left指针不动这样下次情况为1时就会把删除元素给覆盖掉了! 遍历完后,left指针所在位置就是新数组长度!
public int removeElement(int[] nums, int val) {
        int left=0;
        for(int right=0;right<nums.length;right++){
            if(nums[right]!=val){
                nums[left]=nums[right];
                left++;
            }
        }
        return left;
    }

344. 反转字符串

344. 反转字符串

思路:双指针,然后使用位运算

public void reverseString(char[] s) {
        int start=0;
        int end=s.length-1;
        while(start<end){
            s[start]^=s[end];
            s[end]^=s[start];
            s[start]^=s[end];
            start++;
            end--;
        }
    }

剑指 Offer 05. 替换空格

剑指 Offer 05. 替换空格

思路:这题除了双指针也可以用StringBuilder会让代码更简洁

//使用StringBuilder
public String replaceSpace(String s) {
        StringBuilder sb=new StringBuilder();
        for(char ch : s.toCharArray()){
            if(ch==' '){
                sb.append("%20");
            }else
                sb.append(ch);
        }
        return sb.toString();
    }
//双指针
public String replaceSpace(String s) {
        if(s.length()==0 || s==null)
            return s;
        StringBuilder sb=new StringBuilder();
        for(int i=0;i<s.length();i++){
            if(s.charAt(i)==' ')
                sb.append("  ");//新增加2个位置
        }
        int left=s.length()-1; //指向原来s的末尾
        s+=sb.toString();
        int right=s.length()-1;//指向新s的末尾
        char[] ch=s.toCharArray();
        //从后往前填充元素,这样才不会覆盖掉其他元素
        while(left>=0){
            if(ch[left]==' '){
                ch[right--]='0';
                ch[right--]='2';
                ch[right]='%';
            }else{
                ch[right]=ch[left];
            }
            left--;
            right--;
        }
        return new String(ch);
    }

151. 反转字符串中的单词

151. 反转字符串中的单词 思路:交换字符串用到双指针。

  • 去除所有多余空格整理出新的句子
  • 先反转整个句子,再逐个反转里面的单词
  • 反转每个单词时,注意遍历的范围判断要用start!!如果用end的话,最后一个单词就会因为超出范围跳出循环从而没进行到交换!所以这个细节很重要
public String reverseWords(String s) {
        StringBuilder sb=removeSpace(s);
        //整个反转
        reverse(sb,0,sb.length()-1);
        //反转每个单词
        reverseString(sb);
        return sb.toString();

    }
    public StringBuilder removeSpace(String s){
        //去除多余空格
        int start=0;
        while(s.charAt(start)==' ')
            start++;
        int end=s.length()-1;
        while(s.charAt(end)==' ')
            end--;
        StringBuilder sb = new StringBuilder();
        while(start<=end){
            char c = s.charAt(start);
            if(c!=' ' || sb.charAt(sb.length()-1)!=' ')
                sb.append(c);
            start++;
        }
        return sb;
    }
    //反转给定范围内的字符串
    public void reverse(StringBuilder sb,int start,int end){
        while(start<end){
            char tmp=sb.charAt(start);
            sb.setCharAt(start,sb.charAt(end));
            sb.setCharAt(end,tmp);
            start++;
            end--;
        }
    }
    //反转每个单词
    public void reverseString(StringBuilder sb){
        int start=0;
        int end=start+1;
        while(start<sb.length()){
            while(end<sb.length() && sb.charAt(end)!=' ')
                end++;
            reverse(sb,start,end-1);
            start=end+1;
            end=start+1;
        }
    }

206. 反转链表

206. 反转链表 思路:建议画图才好理解,画出连接关系(next)。

要断开当前元素next指向关系前,需要记录当前元素next的下一个元素!这样才好接着遍历下去,不会断开。

public ListNode reverseList(ListNode head) {
        ListNode pre=null;
        ListNode cur=head;
        while(cur!=null){
            ListNode tmp=cur.next;
            cur.next=pre;
            pre=cur;    
            cur=tmp;    //移动
        }
        return pre;

19. 删除链表的倒数第 N 个结点

19. 删除链表的倒数第 N 个结点

思路:使用快慢指针,涉及到删除所以用到dummy,注意这里快慢指针都先指向dummy

  • 根据删除倒数第n个节点,快指针先走n+1步(因为一开始指向dummy所以+1)
  • 快慢指针再一起遍历,直到快指针走完链表
  • 此时慢指针来到要删除的节点前一个位置
public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy=new ListNode();
        ListNode fast=dummy;
        ListNode slow=dummy;
        dummy.next=head;
        while(n>=0){
            fast=fast.next;
            n--;
        }
        while(fast!=null){
            fast=fast.next;
            slow=slow.next;
        }
        slow.next=slow.next.next;
        return dummy.next;
    }

面试题 02.07. 链表相交

面试题 02.07. 链表相交

思路:

  • 两个链表先各自遍历计算长度,自己规定链表A永远代表较长的链表(方便后续计算),所以计算完后如果当前链表A较短要做对调操作。
  • 两个长度相减得到差值,让链表A先走这么多步,这样两个链表的起点位置就相同了
  • 开始遍历链表找出相同节点就是相交处
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode nodeA=headA;
        ListNode nodeB=headB;
        int lA=0;
        int lB=0;
        while(nodeA!=null){
            nodeA=nodeA.next;
            lA++;
        }
        while(nodeB!=null){
            nodeB=nodeB.next;
            lB++;
        }
        nodeA=headA;
        nodeB=headB;
        if(lA<lB){
            int tmp=lA;
            lA=lB;
            lB=tmp;
            nodeA=headB;
            nodeB=headA;
        }
        int num=lA-lB;
        while(num>0){
            nodeA=nodeA.next;
            num--;
        }
        while(nodeA!=null){
            if(nodeA==nodeB){
                return nodeA;
            }
            nodeA=nodeA.next;
            nodeB=nodeB.next;
        }
        return null;
    }

142. 环形链表 II

142. 环形链表 II

思路:主要用快慢指针,快指针永远走2步,慢指针走1步。然后分两部分判断:一是否有循环、二循环口在哪里

  • 如果快指针和慢指针相等了,就表示走进循环中

    • (此时选定慢指针,让其从当前点继续每次走1步)
    • 一个指针从头开始(每次走1步)如果和慢指针相等了就表示当前点就是循环口!
  • 没有相等表示没有循环!

具体公式参考代码随想录的推导

 public ListNode detectCycle(ListNode head) {
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null && fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
            if(slow==fast){
                ListNode cur=head;
                while(cur!=fast){
                    cur=cur.next;
                    fast=fast.next;
                }
                return cur;
            }
        }
        return null;
    }

15. 三数之和

15. 三数之和

思路:不能有重复结果,所以要先对数组进行排序,然后要对当前元素进行判断是否跟之前元素一样,如果是则需要跳过。

  • 通过for找到第一个不重复的元素,接下来两个元素分别用两个指针来标识!双指针移动范围:当前元素下一个作为起始,数组末尾元素作为结束。
  • 开始计算三个元素相加结果,并进行相应处理
    • sum>0:表示right大了,要移动--
    • sum<0:表示left小了,要移动++
    • =0:找到一组,加入到结果集合中,并对两指针进行移动找到分别找到下一个不重复的元素继续计算。要注意指针移动不能超出范围!
public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> list=new ArrayList<>();
        if(nums.length<3)
            return list;
        Arrays.sort(nums);
        for(int i=0;i<nums.length;i++){
            if(i>0 && nums[i]==nums[i-1])
                continue;
            int left=i+1;
            int right=nums.length-1;
            while(left<right){
                int sum=nums[i]+nums[left]+nums[right];
                if(sum>0){
                    right--;
                }else if(sum<0){
                    left++;
                }
                else{
                    list.add(new ArrayList<>(Arrays.asList(nums[i],nums[left],nums[right])));
                    while(left<right && nums[left]==nums[left+1])
                        left++;
                    while(left<right && nums[right-1]==nums[right])
                        right--;
                    left++;
                    right--;
                }
            }
        }
        return list;
    }

18. 四数之和

18. 四数之和 思路:和三数之和差不多,只是多了一个元素,在选取出第一个元素后,再用for循环重复步骤选出第二个元素,其他两个元素一样用指针

public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> list=new ArrayList<>();
        Arrays.sort(nums);
        for(int i=0;i<nums.length;i++){
            if(i>0 && nums[i]==nums[i-1])
                continue;
            for(int j=i+1;j<nums.length;j++){
                if(j>i+1 && nums[j]==nums[j-1])
                    continue;
                int left=j+1;
                int right=nums.length-1;
                while(left<right){
                    long sum=(long)nums[i]+nums[j]+nums[left]+nums[right];
                    if(sum>target){
                        right--;
                    }else if(sum<target)
                        left++;
                    else{
                        list.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));
                        while(left<right && nums[left]==nums[left+1])
                            left++;
                        while(left<right && nums[right]==nums[right-1])
                            right--;
                        left++;
                        right--;
                    }
                }
            }
        }
        return list;
    }