刷题笔记4——数组问题、快慢指针、双指针

数组问题、快慢指针、双指针刷题小结

  • 原地修改的要求
  • 一般说到**'必须只使用常量级的额外空间"**要求,就是需要用指针来解决不能新建数组
  • 快慢指针一定要各司其职,慢指针负责控制有效数组,快指针负责在后边进行遍历
  • 一些基本用法要回顾
    • charAt()
    • substring()
    • length()
    • String一定要大写
    • 返回数组格式时的定义方法: return new int[]{left + 1, right + 1};

26. 删除有序数组中的重复项(一次AC)

  • 初步思路,遍历数组并存入到hash表中,用新的数组存储return的数组,如果有重复,则跳过这一项,最后返回新的数组
  • 初步思路G:原地修改原来是不让new数组,只能在原数组上操作,返回结果为数组的前k项
  • 二步思路就是,用两个指针,分别指向第一个元素a和比该元素大的下一个元素b,将b放在a的下一个位置然后a++, b++,继续判断,如果小于等于就跳过,最后a停下的位置就是有效数组的长度
  • 二步思路 == 题解
class Solution {
    public int removeDuplicates(int[] nums) {
        int slow = 0;
        int fast = 0;

        while(fast<nums.length){
            if (nums[slow]==nums[fast]){
                fast++;
            }
            else{
                nums[slow+1] = nums[fast];
                slow++;
                fast++;
            }
        }
        return slow+1;
    }
}

83. 删除排序链表中的重复元素

  • Java/Python 这类带有垃圾回收的语言,可以帮我们自动找到并回收这些「悬空」的链表节点的内存, 不需要释放
  • 这里链表要比数组麻烦一点,就是链表返回的是头节点,而不是一个带长度的数组,数组的话很确定的知道有多少元素是最终结果,但是链表注意一定要把链表尾部清理干净,不要留下一些需要替换的元素但是没有删除或者没有处理
  • 另外,先 fast = fast.next;,再slow.next = null; 非常重要,如果顺序颠倒,那么fast和slow此刻指向同样的位置,slow经过null之后就断掉了!!!
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode slow = new ListNode();
        ListNode fast = new ListNode();
        slow = head;
        fast = head;

        while(fast!=null){
            if(slow.val == fast.val){
                fast = fast.next;
                slow.next = null;
            }
            else{
                slow.next = fast;
                slow = slow.next;
                fast = fast.next;
                slow.next = null;         
            }
        }
        return head;
    }
}

27. 移除元素

  • 一开始思路就跟shit一样,一直在考虑对于该位置是val的情况下怎么处理,双指针的用法就是用slow记录是val的位置然后用fast记录不是val的位置,用fast来替换slow。但是用双指针指了半天还是很混乱

    • 我考虑如果两个相连位置都是val怎么办,那我用fast代替了第一个位置的val,fast处的值要如何替换,这个值已经不能再被存储一遍了,但是这个值改成val那么轮到它的时候还需要再处理一遍
    • fast已经遍历到第二个val后边了,而我设计的流程中其实只能依靠fast来找是val的位置,所以此刻fast只能移动到和slow相同的位置,也就是还需要退回去
    • 返回长度也很难计算,因为slow处理完当前为val的地方就会被置为-1,而fast会一直到结尾
  • 看思路后一句话点醒

    • 我最后只需要返回需要的值,那我只需要把需要的值找到,然后挨个放置到当前可以放置的地方即可
    • 对于那些val的位置,我完全可以看作为空,类似于我把空去掉的一道题,正常做法就是把值往前挪
    • 我的思路最错误的一点就是为了省事,所以把slow变成一个标记的指针,而没有挨个挪动,这样的话fast又需要找当前的位置,还需要探测后边的值,一直需要回退,效率非常低!!!
    • 双指针的要点就是:各自分工,slow一直指向当前已经处理好的最后位置,fast用于探索
class Solution {
    public int removeElement(int[] nums, int val) {
        int slow = 0;
        int fast = 0;

        while(fast<nums.length){
            if(nums[fast]!=val){
                nums[slow] = nums[fast];
                slow++;
                fast++;
            }
            else{
                fast++;
            }
        }
        return slow;
    }
}

283. 移动零 (一次AC)

class Solution {
    public void moveZeroes(int[] nums) {
        int fast = 0;
        int slow = 0;

        while(fast<nums.length){
            if(nums[fast]==0){
                fast++;
            }
            else{
                nums[slow] = nums[fast];
                slow++;
                fast++;
            }
        }

        if (slow<nums.length){
            for(int i=slow;i<nums.length;i++){
                nums[i] = 0;
            }
        }
        
    }
}

167. 两数之和 II - 输入有序数组

  • 双指针逼近的思路
class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int left = 0;
        int right = numbers.length-1;

        while(left<right){
            int sum = numbers[left] + numbers[right];
            if(sum==target){
                return new int[]{left + 1, right + 1};
            }
            else if(sum<target){
                left++;
            }
            else{
                right--;
            }
        }

        return new int[] {-1,-1};
    }
}

344. 反转字符串

class Solution {
    public void reverseString(char[] s) {
        int left = 0; 
        int right = s.length-1;
        while(left<right){
            char temp;
            temp = s[left];
            s[left] = s[right];
            s[right] = temp;
            left++;
            right--;
        }
    }
}

5. 最长回文子串(之前的思路是dp,现在用双指针试一下子)

  • 字符串中,s.length()要加括号,并且在取某个值的时候,需要用charAt()
  • s.substring 包括前不包括后边
  • 一直纠结一个点就是palindrome(s, i,i+1) 传入的话,i如果是最后一位,那么i+1就越界了,在palindrome函数中会return s.substring(l + 1, r); 不就会越界报错么,但是 s.substring 包括前不包括后边,所以如果是s.substring(1000, 1000),里边的值无论多大都是无效的,不会报错
class Solution {
    public String longestPalindrome(String s) {
        String p = "";
        for (int i=0;i<s.length();i++){
            // 判断是否满足奇数回文
            String p1 = palindrome(s, i,i);
            // 判断满足偶数回文不
            String p2 = palindrome(s, i,i+1);
            p = p1.length()>p.length()? p1:p;
            p = p2.length()>p.length()? p2:p;
        }
        return p;
    }


    public String palindrome(String s, int l, int r){
        while(l>=0 && r<s.length()){
            if(s.charAt(l)==s.charAt(r)){
                l--;
                r++;
            }
            else{
                break;
            }
        }
        return s.substring(l + 1, r);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值