代码随想录-双指针

代码随想录-双指针

1.移除元素(力扣27)
1.1 题目

在这里插入图片描述

1.2 代码
class Solution {
    public int removeElement(int[] nums, int val) {
        if(nums==null || nums.length==0){
            return 0;
        }

        //用来指向非val元素
        int l=0;        
        //遍历
        for(int i=0;i<nums.length;i++){
            if(nums[i]!=val){
                nums[l++]=nums[i];
            }
        }
        return l;
    }
}
1.3 思路
1.声明一个指向非val元素的指针l
2.将非val元素赋值到l的位置上,nums[l++]=nums[i];
3.最终的l就是非val值的数量
2.反转字符串(力扣344)
2.1 题目

在这里插入图片描述

2.2 代码
class Solution {
    public void reverseString(char[] s) {

        //分别定义左、右指针,用来移动相对应的字符
        int l=0;
        int r=s.length-1;
        while(l<=r){
            char tem=s[r];
            s[r]=s[l];
            s[l]=tem;
            l++;
            r--;
        }              
    }
}
2.3 思路
1.定义左、右两个指针,使其分别从左、右出发,进行交换
3.替换空格(剑指offer 05)
3.1 题目

在这里插入图片描述

3.2 代码
class Solution {
    public String replaceSpace(String s) {
        if(s==null || s.length()==1){
            return s;
        }

        //java中的方法真好用
        // return s.replaceAll(" ","%20");

        StringBuilder sb=new StringBuilder();
        for(int i=0;i<s.length();i++){
            if(String.valueOf(s.charAt(i)).equals(" ")){
                sb.append("%20");
            }else{
                sb.append(s.charAt(i));
            }
        }
        return sb.toString();
    }
}
3.3 思路
使用StringBuilder,遇见空格则拼接"%20"
4.颠倒字符串中的单词(力扣151)
4.1 题目

在这里插入图片描述

4.2 代码
class Solution {
    public String reverseWords(String s) {

        //首先对s字符串前后的空格进行消除
        s=s.trim();
        //使用StringBuilder进行拼接
        StringBuilder sb=new StringBuilder();
        //使用空格对字符串进行切割
        String[] strs=s.split(" ");
        for(int i=strs.length-1;i>=0;i--){
            //注意:一定要加上这个
            if(strs[i].equals("")) continue;
            sb.append(strs[i].replaceAll(" ",""));
            if(i!=0){
                sb.append(" ");
            }            
        }
        return sb.toString();
    }
}
4.3 思路
要知道String中的以下方法:
trim():清除字符串前后的空格
split():将字符串按照某一个字符切割,返回String数组
replaceAll():将字符串中的某一个字符替换成另外一个字符
5.反转链表(力扣206)
5.1 题目

在这里插入图片描述

5.2 代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
       if(head==null || head.next==null){
           return head;
        }

        //设置一个始终指向左边节点变量
        ListNode leftNode=head;
        //head始终指向右边的节点
        head=head.next;
        //将第一个节点的指向为null
        leftNode.next=null;
        while(head!=null && head.next!=null){
            //设置一个head后继节点的临时变量
            ListNode node=head.next;
            //head指向前驱节点,即leftNode
            head.next=leftNode;
            //leftNode指向head的位置
            leftNode=head;
            //head往后走
            head=node;
        }

        //执行到这,说明head已经指向最后一个元素了,但是并没有将最后一个节点和其前驱节点连接
        head.next=leftNode;
        return head;

    }
}
5.3 思路
1.设置一个始终指向左边结点的指针,leftNode
2.head始终指向leftNode的下一个结点
3.在while中定义一个临时变量,让node=head.next
6.删除链表的倒数第N个结点(力扣19)
6.1 题目

在这里插入图片描述

6.2 代码
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {

        //判空
        if(head==null || head.next==null){
            return null;
        }

        //定义快慢指针,让快指针先走n步,然后慢指针和快指针一起走
        ListNode fast=head;
        ListNode slow=head;
        for(int i=0;i<n;i++){
            fast=fast.next;
        }

        //让慢指针和快指针一起走,直到快指针的下一个结点为null
        while(fast!=null && fast.next!=null){
            fast=fast.next;
            slow=slow.next;
        }

        if(fast==null){
            return head.next;
        }

        slow.next=slow.next.next;
        return head;            
    }
}
6.3 思路
1.定义快慢指针slow和fast
2.先让fast走n步
	2.1 当删除第一个结点时,fast为null
	2.2 当删除的不是第一个结点时,fast不为null
3.让slow和fast一块走,当fast.next!=null为界限
4.若fast==null 返回head.next
  若fast!=null  slow.next=slow.next.next,返回head
7.链表相交(面试题 02.07)
7.1 题目

在这里插入图片描述

7.2 代码(简便)
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        //判空
        if(headA==null || headB==null){
            return null;
        }

        //定义两个指针,分别遍历headA和headB,遍历完之后继续遍历相反的链表
        ListNode m=headA;
        ListNode n=headB;
        while(m!=n){
            m=(m==null)?headB:m.next;
            n=(n==null)?headA:n.next;
        }
        return m;       
    }
}
7.3 代码(复杂)
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        //判空
        if(headA==null || headB==null){
            return null;
        }

        //定义两个指针,分别指向headA和headB,让它们去遍历链表
        //直到其中一个指针的下一个结点为空,说明两个链表的位置一样
        ListNode m=headA;
        ListNode n=headB;
        while(m.next!=null && n.next!=null){
            m=m.next;
            n=n.next;            
        }

        if(m.next==null){
            //当m.next==null时,说明headA的长度<=headB的长度
            //让headB指向相同的位置
            while(n.next!=null){
                headB=headB.next;
                n=n.next;
            }
            //headA和headB现在指向了同一个位置,遍历他们向后走
            while(headA!=headB){
                headA=headA.next;
                headB=headB.next;
            }
        }else{
            //当n.next==null时,说明headA的长度>headB的长度
            //让它们向后走
            while(m.next!=null){
                headA=headA.next;
                m=m.next;
            }
            //一起走
            while(headA!=headB){
                headA=headA.next;
                headB=headB.next;
            }
        }
        return headA;
    }
}
8.环形链表Ⅱ(力扣142)
8.1 题目

在这里插入图片描述

8.2 代码(set)
public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head==null || head.next==null){
            return null;
        }

        //使用set,不可重复集合
        HashSet<ListNode> set=new HashSet<>();
        while(head!=null){
            if(!set.contains(head)){
                set.add(head);
                head=head.next;
            }else{
                break;
            }
        }
        return head;      
    }
}
8.3 代码(快慢指针)
public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head==null || head.next==null){
            return null;
        }

        //使用快慢指针
        ListNode slow=head.next;
        ListNode fast=head.next.next;
        //先让fast向后走,直到fast==null或fast.next==null,这表示无环
        while(fast!=null && fast.next!=null){
            //当slow==fast时,说明有环
            if(slow==fast){
                break;
            }
            //慢指针一次走一步
            slow=slow.next;
            //快指针一次走两步
            fast=fast.next.next;
        }
        //判断fast和fast.next是否为空,若为空,则返回null
        if(fast==null || fast.next==null){
            return null;
        }
        //待两指针相遇,则使其中一个指针从头继续走
        slow=head;
        while(slow!=fast){
            slow=slow.next;
            fast=fast.next;
        }
        return slow;        
    }
}
8.4 思路
思路一:使用set集合
	1. 当set中存在当前结点时,直接返回,说明有环
	2. 当遍历完链表set中的元素都不重复,则返回null,说明无环
思路二:使用快慢指针
	1. 让慢指针slow一次走一格,快指针fast一次走两格
		1.1 若快指针fast==null 或 fast.next==null,则无环,直接返回null
		1.2 若1.1不满足,则说明有环,执行以下程序
	2.执行到这,说明有环,使慢指针slow从头开始走,快指针从当前位置走,两者都一次走一步,相遇点即为入环		
9.三数之和(力扣15)
9.1 题目

在这里插入图片描述

9.2 代码
class Solution {    
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res=new ArrayList<>();
        //判空
        if(nums==null || nums.length<3){
            return res;
        }
        //对数组进行排序
        Arrays.sort(nums);

        //遍历数组
        for(int i=0;i<nums.length-1;i++){
            //判断相邻两个值是否相同,相同则跳过
            if(i!=0 && nums[i]==nums[i-1]){
                continue;
            }
            //i就是一个指针
            //l指向i的右边
            int l=i+1;
            //r指向数组的最右边
            int r=nums.length-1;
            while(r>l){
                //判断其和
                int sum=nums[i]+nums[l]+nums[r];
                if(sum<0){
                    l++;
                }else if(sum>0){
                    r--;
                }else{
                    //说明sum为0
                    List<Integer> list=new ArrayList<>();
                    list.add(nums[i]);
                    list.add(nums[l]);
                    list.add(nums[r]);
                    res.add(list);
                    //判断l+1是否和l的值相同
                    while(r>l && nums[l+1]==nums[l]){
                        l++;
                    }
                    //判断r-1是否和r的值相同
                    while(r>l && nums[r]==nums[r-1]){
                        r--;
                    }
                    l++;
                    r--;
                }
            }            
        }
    
    return res;        
    }
}
9.3 思路
使用三指针
1.对数组进行遍历,其中的i就是第一个指针
2.在遍历for中,指针l=i+1,r=nunms.length-1
3.内嵌循环,while(l<r),然后对其和进行判断
10.四数之和(力扣18)
10.1 题目

在这里插入图片描述

10.2 代码
class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {

        List<List<Integer>> res=new ArrayList<>();
        //非法性校验
        if(nums.length<4){
            return res;
        }

        //对数组进行排序
        Arrays.sort(nums);

        //注意:i最多只能到num.length-1,因为下面的j=i+1
        for(int i=0;i<nums.length-1;i++){
            //若i指向的和i-1指向的相同,则跳过
            if(i!=0 && nums[i-1]==nums[i]) continue;
            //j从i+1开始,j最多只能到nums.length-1,因为下面的l=j+1
            for(int j=i+1;j<nums.length-1;j++){
                //注意:当j在i+1之后,若还有相同的元素,则跳过
                if(j>i+1 && nums[j-1]==nums[j]) continue;
                int l=j+1;
                int r=nums.length-1;                
                while(l<r){
                    int sum=nums[i]+nums[j]+nums[l]+nums[r];
                    if(sum<target){
                        l++;
                    }else if(sum>target){
                        r--;
                    }else{
                        List<Integer> list=new ArrayList<>();
                        list.add(nums[i]);
                        list.add(nums[j]);
                        list.add(nums[l]);
                        list.add(nums[r]);
                        res.add(list);
                        while(l<r && nums[l]==nums[l+1]){
                            l++;
                        }
                        while(l<r && nums[r]==nums[r-1]){
                            r--;
                        }
                        l++;
                        r--;
                    }
                }
            }
        }
        return res;
    }
}
10.3 思路
这道题和上面的三数之和思路一样,不过要注意以下几点:
1.第一个for的i<nums.length-1,因为j=i+1
2.第二个for的j<nums.length-1,因为l=j+1
3.第二个for内,在j=i+1之后,如有nums[j-1]==nums[j],则跳过
	即if(j>i+1 && nums[j]==nums[j-1])  continue;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值