算法总结

算法总结

1.二分查找

2.移除元素

1.移除元素

题目链接:27. 移除元素 - 力扣(LeetCode)

思路:

快慢指针,left 更新数组,right 向右走

代码:
class Solution {
    public int removeElement(int[] nums, int val) {
        int n=nums.length;
        int left=0,right=0;
        for(;right<n;right++){
            if(nums[right]!=val){
                nums[left]=nums[right];
                left++;
            }
        }
        return left;
    }
}

3.滑动窗口

1.水果成篮

题目链接:904. 水果成篮 - 力扣(LeetCode)

思路:

left right指向两个水果,不断更新

代码:
class Solution {
    public int totalFruit(int[] fruits) {
        int n=fruits.length;
        int left=0,right=1;
        if(n==1) return 1;
        int ln=fruits[left],rn=fruits[right];
        int ans=0;
        while(right<n){
            if(fruits[right]==ln||fruits[right]==rn){
                ans=Math.max(ans,right-left+1);
                right++;
            }
            else{
                left=right-1;
                ln=fruits[left];
                while(left>=1&&fruits[left-1]==ln) left--;
                rn=fruits[right];
                ans=Math.max(ans,right-left+1);
            }
        }
        return ans;
    }
}

链表

1.双指针

1.环形链表||

题目链接:142. 环形链表 II - 力扣(LeetCode)

思路:

双指针,fast一次走两次, slow 一次走一次,然后他们肯定会在环形链表内交上,然后 x=(n-1)(y+z)+z, 当n=1时,x=z,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点,当n!=1时,相遇节点也是多走了(n-1)圈,然后与头结点相遇

代码:
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
            if(slow==fast){
                ListNode index1=fast;
                ListNode index2=head;
                while(index1!=index2){
                    index1=index1.next;
                    index2=index2.next;
                }
                return index1;
            }
        }
        return null; 
    }
}

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

题目链接:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

思路:

首先设一个虚拟头结点,删除倒数第N个节点就是找到前一个节点为第M-N个节点,然后fast指针先走N步,slow指针在原地,然后两指针一起走,直到fast.next指针为空,即走了M-N步,slow指针指向第M-N个节点,然后 slow.next=slow.next.next;

代码:
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode p=head;
        ListNode q = new ListNode(0, head);
      ListNode fast=q;
      ListNode slow=q;
      while(n-->0&&fast!=null){
          fast=fast.next;
      }
      while(fast.next!=null){
          fast=fast.next;
          slow=slow.next;
      }
      slow.next=slow.next.next;
        return q.next;
    }
}

字符串:

1.双指针法

反转字符串经常用到

2.反转系列

在反转上还可以在加一些玩法,其实考察的是对代码的掌控能力。双指针法用的比较多

3.KMP

KMP的主要思想是当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配了.

KMP的精髓所在就是前缀表

前缀表:起始位置到下标i之前(包括i)的子串中,有多大长度的相同前缀后缀。

题目:

1.28. 找出字符串中第一个匹配项的下标

KMP思想

代码:

class Solution {
  public int strStr(String haystack, String needle) {
        if (needle.length() == 0) return 0;
        int[] next = new int[needle.length()];
        getNext(next, needle);

        int j = 0;
        for (int i = 0; i < haystack.length(); i++) {
            while (j > 0 && needle.charAt(j) != haystack.charAt(i)) 
                j = next[j - 1];
            if (needle.charAt(j) == haystack.charAt(i)) 
                j++;
            if (j == needle.length()) 
                return i - needle.length() + 1;
        }
        return -1;

    }
    
    private void getNext(int[] next, String s) {
        int j = 0;//前缀末尾位置
        next[0] = 0;
        // i 是后缀末尾位置
        for (int i = 1; i < s.length(); i++) {
            // i 和 j 位置字母不相等
            while (j > 0 && s.charAt(j) != s.charAt(i)) 
                j = next[j - 1];
            // i 和 j 位置字母相等
            if (s.charAt(j) == s.charAt(i)) 
                j++;
            next[i] = j; 
        }
    }
}

2.459. 重复的子字符串

思路:

KMP,

假设字符串s使用多个重复子串构成(这个子串是最小重复单位),重复出现的子字符串长度是x,所以s是由n * x组成。

因为字符串s的最长相同前后缀的长度一定是不包含s本身,所以 最长相同前后缀长度必然是m * x,而且 n - m = 1,(这里如果不懂,看上面的推理)

所以如果 nx % (n - m)x = 0,就可以判定有重复出现的子字符串。

代码:

class Solution {
    public boolean repeatedSubstringPattern(String s) {
        int[] next = getNext(s);
        // 长度 % (长度 - 最长前后缀) == 0
        if (next[s.length() - 1] != 0 && s.length() % (s.length() - next[s.length() - 1]) == 0) {
            return true;
        }
        return false;
    }

    public int[] getNext(String str) {
        int[] next = new int[str.length()];
        next[0] = 0;
        int j = 0;
        for (int i = 1; i < str.length(); i++) {
            // 找到最长相等前后缀长度
            while (j > 0 && str.charAt(i) != str.charAt(j)) {
                j = next[j - 1];
            }
            // 如果匹配, 长度则再加1
            if (str.charAt(i) == str.charAt(j)) {
                j++;
            }
            next[i] = j;
        }
        return next;
    }
}

双指针

1.n数之和

通解:排序+双指针

代码:

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int tar) {
        // n数之和通用解法,排序+固定枚举前n-2个数字,双指针搜索最后两个数字。
        // 每个数字向后搜索时直接去重。
        int n = nums.length;
        Arrays.sort(nums);
        List<List<Integer>> ans = new ArrayList<List<Integer>>();
        if(n < 4)
            return ans;
        for(int i = 0; i < n-3; ++i){
            if(i > 0 && nums[i] == nums[i-1])
                continue;
            for(int j = i+1; j < n-2; ++j){
                if(j > i + 1 && nums[j] == nums[j-1])
                    continue;
                int l = j + 1, r = n-1;
                while(l < r){
                    long sum = (long)nums[i] + nums[j] + nums[l] + nums[r];
                    if(sum == (long)tar){
                        ans.add(Arrays.asList(nums[i],nums[j],nums[l++],nums[r--]));
                        while(l < r && nums[l] == nums[l-1])
                            ++l;
                        while(l < r && nums[r] == nums[r+1])
                            --r;
                    }else if(sum < (long)tar){
                        ++l;
                    }else{
                        --r;
                    }
                }
            }
        }
        return ans;
    }
}
posted @ 2024-01-04 09:27  wnbzw  阅读(8)  评论(0)    收藏  举报