LeetCode刷题笔记之双指针算法

一、数组相关操作

1. 27【移除元素】

  • 题目: 给你一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1)额外空间并原地修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

  • 代码:

class Solution {
    public int removeElement(int[] nums, int val) {
    	//快慢指针
    	int slow = 0;
    	for(int j=0;j<nums.length;j++){
    		if(nums[j] != val){
    			nums[slow++] = nums[j];
    		}
    	}
    	return slow;
    }
}

2. 26【删除排序数组中的重复项】

  • 题目: 给你一个非严格递增排列的数组 nums ,请你原地删除重复出现的元素,使每个元素只出现一次 ,返回删除后数组的新长度。元素的相对顺序应该保持一致 。然后返回 nums 中唯一元素的个数。考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:
    更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。返回 k 。
  • 代码:
class Solution {
    public int removeDuplicates(int[] nums) {
    	//快慢指针
    	int slow = 1;
    	for(int i=1;i<nums.length;i++){
    		if(nums[i] != nums[slow-1]){
    			nums[slow++] = nums[i];
    		}
    	}
    	return slow ;
    }
}

3. 283【移动零】

  • 题目: 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
    请注意 ,必须在不复制数组的情况下原地对数组进行操作。
  • 代码:
class Solution {
    public void moveZeroes(int[] nums) {
    	//快慢指针
    	int slow = 0;
    	for(int i=0;i<nums.length;i++){
    		if(nums[i] != 0){
    			nums[slow++] = nums[i];
    		}
    	}
    	for(slow;slow<nums.lenght;slow++){
    		nums[slow] = 0;
    	}
    }
}

4. 977【有序数组的平方】

  • 题目: 给你一个按非递减顺序排序的整数数组 nums,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。
  • 代码:
class Solution {
    public int[] sortedSquares(int[] nums) {
    	for(int i=0;i<nums.length;i++){
    		nums[i] = nums[i] * nums[i];
    	}
    	int[] newNums = new int [nums.length];
    	//双向指针
    	int left = 0;
    	int right = nums.length-1;
    	int i = nums.length-1;
    	while(left <= right){
    		if(nums[left] <= nums[right]){
    			newNums[i--] = nums[right--];
    		}else{
				newNums[i--] = nums[left++]
			}
		}
		return newNums;
    }
}

5. 844【比较含退格的字符串】

  • 题目: 给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。#代表退格字符。
    注意:如果对空文本输入退格字符,文本继续为空。
  • 代码:
class Solution {
    public boolean backspaceCompare(String s, String t) {
        //因为#是退格,所以有后向前比较更容易
        int i = s.length()-1;
        int j = t.length()-1;
        //记录退格次数
        int countS = 0;
        int countT = 0;
        while (i>=0 || j>=0){
            while (i>=0){
                if(s.charAt(i) == '#'){
                    countS++;
                    i--;
                }else if(countS > 0){
                    i--;
                    countS--;
                }else {
                    break;
                }

            }
            while (j>=0){
                if(t.charAt(j) == '#'){
                    countT++;
                    j--;
                }else if(countT > 0) {
                    j--;
                    countT--;
                }else {
                    break;
                }
            }
            if(i>=0 && j>=0){
                if(s.charAt(i) != t.charAt(j)){
                    return false;
                }
            }else if(i>=0 || j>=0){
                return false;
            }
            i--;
            j--;
        }
        return true;
    }
}

二、链表相关操作

1. 206【反转链表】

  • 题目: 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
  • 代码:
class Solution {
    public ListNode reverseList(ListNode head) {
    	//双指针法,一个指向前一个节点,一个指向后一个节点,将next反转
        ListNode tempNode = new ListNode();
        ListNode ansNode = null;
        tempNode = head;
        while (tempNode != null){
            ListNode node = tempNode.next;
            tempNode.next = ansNode;
            ansNode = tempNode;
            tempNode = node;
        }
        return ansNode;
    }
}

2. 19【删除链表的倒数第N个节点】

  • 题目: 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
  • 代码:
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
    	//快慢指针,找到倒数第n个结点
    	//快指针先走n个结点,然后快慢指针一起向下遍历,直到快指针指向最后一个结点
        ListNode dummyNode = new ListNode();
        dummyNode.next = head;
        ListNode left = dummyNode;
        ListNode right = dummyNode;
        while (n >= 0){
            right = right.next;
            n--;
        }

        while (right != null){
            left = left.next;
            right = right.next;
        }
        left.next = left.next.next;
        return dummyNode.next;
    }
}

3. 02.07【链表相交】

  • 题目: 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
  • 代码:
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    	//双指针,相交表示节点完全相同,A与B相交最多在倒数第len(headB)处
        ListNode tempA = headA;
        ListNode tempB = headB;
        int n = 0, m = 0;
        while (tempA != null){
            n++;
            tempA = tempA.next;
        }
        while (tempB != null){
            m++;
            tempB = tempB.next;
        }
        tempA = headA;
        tempB = headB;
        while (n > m){
            tempA = tempA.next;
            n--;
        }
        while (m > n){
            tempB = tempB.next;
            m--;
        }
        while (tempA != null){
            if(tempA == tempB){
                return tempA;
            }
            tempA = tempA.next;
            tempB = tempB.next;
        }
        return null;
    }
}

4. 142 【环形链表Ⅱ】

  • 题目: 给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
    如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改链表。
  • 代码:
public class Solution {
    public ListNode detectCycle(ListNode head) {
    	//快慢指针,慢指针走一个节点,快指针走2个节点
    	//若有环则快指针比慢指针多走n个环
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast){
                ListNode tempNode1 = head;
                ListNode tempNode2 = fast;
                while (tempNode1 != tempNode2){
                    tempNode1 = tempNode1.next;
                    tempNode2 = tempNode2.next;
                }
                return tempNode1;
            }
        }
        return null;
    }
}

三、数之和

1. 15【三数之和】

  • 题目: 给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
    注意: 答案中不可以包含重复的三元组。
  • 代码:
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ansList = new LinkedList<>();
        //利用双指针法,要先固定一个数a,对数组进行排序
        //由最左边开始遍历数组,令该遍历的数为a
        //在剩余位置使用双向指针确定b,c的值
        Arrays.sort(nums);
        for (int i = 0; i < nums.length-2; i++) {
            if(nums[i] > 0){
                break;
            }
            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<Integer> tempList = Arrays.asList(nums[i],nums[left],nums[right]);
                    ansList.add(tempList);
                    while (left < right && nums[left]==nums[left+1]){
                        left++;
                    }
                    while (left < right && nums[right]==nums[right-1]){
                        right--;
                    }
                    left++;
                    right--;
                }
            }
        }

        return ansList;
    }
}

2. 18【四数之和】

  • 题目: 给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):你可以按任意顺序返回答案 。

    • 0 <= a, b, c, d < n
    • a、b、c 和 d 互不相同
    • nums[a] + nums[b] + nums[c] + nums[d] == target
  • 代码:

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        //这个题与上一题相似,只是这里要固定前两数
        List<List<Integer>> ansList = new LinkedList<>();
        Arrays.sort(nums);
        for (int i = 0; i < nums.length-3; i++) {
            if(nums[i]>0 && nums[i]>target){
                break;
            }
            if(i>0 && nums[i]==nums[i-1]){
                continue;
            }
            for (int j = i+1; j < nums.length-2; 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 = nums[i]+nums[j]+nums[left]+nums[right];
                    if(sum < target){
                        left++;
                    }else if(sum > target){
                        right--;
                    }else{
                        List<Integer> tempList = Arrays.asList(nums[i],nums[j],nums[left],nums[right]);
                        ansList.add(tempList);
                        while (left < right && nums[left]==nums[left+1]){
                            left++;
                        }
                        while (left < right && nums[right]==nums[right-1]){
                            right--;
                        }
                        left++;
                        right--;
                    }
                }
            }
        }
        return ansList;
    }
}

四、字符串相关操作

1. 344【反转字符串】

  • **题目:**编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
  • 代码:
class Solution {
    public void reverseString(char[] s) {
        //交换两个数:a^=b; b^=a; a^=b;
        //双指针法反转数组
        int left = 0;
        int right = s.length-1;
        while (left < right){
            s[left] ^= s[right];
            s[right] ^= s[left];
            s[left] ^= s[right];
            left++;
            right--;
        }
    }
}

2. 151【翻转字符串里的单词】

  • 题目: 给你一个字符串 s ,请你反转字符串中单词的顺序。单词是由非空格字符组成的字符串。s中使用至少一个空格将字符串中的单词分隔开。返回单词顺序颠倒且单词之间用单个空格连接的结果字符串。
    注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
  • 代码:
class Solution {
    public String reverseWords(String s) {
        //去除多余的空格,先去除首尾空格,使用while,双向指针
        StringBuilder sb = new StringBuilder();
        int left = 0;
        int right = s.length()-1;
        while (s.charAt(left) == ' '){
            left++;
        }
        while (s.charAt(right) == ' '){
            right--;
        }
        //快慢指针
        for (int i = left; i < right+1; i++) {
            if(s.charAt(i)==' ' && s.charAt(i-1)==' '){
                continue;
            }
            sb.append(s.charAt(i));
        }
        //反转整个字符串
        int len = sb.length();
        char[] charArr = new char[len];

        for (int i = 0; i < len; i++) {
            charArr[i] = sb.charAt(len-i-1);
        }
        //将每个单词反转回正常顺序
        left = 0;
        for (int i = 0; i < len; i++) {
            if(charArr[i] == ' '){
                reverse(charArr,left,i-1);
                left = i+1;
            }
            if(i == len-1){
                reverse(charArr,left,i);
            }
        }
        String str = String.valueOf(charArr);
        return str;
    }
    //双向指针
    public void reverse(char[] s,int left,int right) {
        while (left < right){
            s[left] ^= s[right];
            s[right] ^= s[left];
            s[left] ^= s[right];
            left++;
            right--;
        }
    }
}

你可能感兴趣的:(课程复习,算法,leetcode,笔记)