【算法题型总结】--3、双指针

1、最长无重复子数组--返回数组长度

public int maxLength (int[] arr)

解法1:set

import java.util.*;
 
public class Solution {
    /**
     * 
     * @param arr int整型一维数组 the array
     * @return int整型
     */
    public int maxLength (int[] arr) {
        // write code here
        int n = arr.length;
        int start = 0, end = 0;
        int max = 0;
         
        while (start <= end && end < n) {
            Set<Integer> set = new HashSet<>();
            while (end < n && set.add(arr[end])) {
                if (end == n - 1) break;
                end++;
            }
            max = Math.max(max, set.size());
            if (end == n - 1) break;
            start = end;
        }
        return max;
    }
}

解法2:滑动窗口(双指针)

经典滑动窗口
import java.util.*;
 
public class Solution {
    /**
     * 
     * @param arr int整型一维数组 the array
     * @return int整型
     */
    public int maxLength (int[] arr) {
        // write code here
//         滑动窗口
        if (arr == null || arr.length == 0) return 0;
        HashMap<Integer, Integer> window = new HashMap<>();
        int left = 0, right = 0;
        int n = arr.length;
        int res = -1;
        while (right < n) {
            int in = arr[right];
            right++;
            window.put(in, window.getOrDefault(in, 0)+1);
            while (window.get(in) > 1) {
                int out = arr[left];
                left++;
                window.put(out, window.getOrDefault(out, 0)-1);
            }
            res = Math.max(res, right - left);
        }
        return res;
    }
}

5、合并两个有序的数组  NC22

public void merge(int A[], int m, int B[], int n)

 解法1:从后往前比较&使用函数

public class Solution {
    public void merge(int A[], int m, int B[], int n) {
        int i=m, j=n;
        while(m>0 && n>0){
            if(A[m-1] >= B[n-1]){
                A[m+n-1] = A[m-1];
                m--;
            }else{
                A[m+n-1] = B[n-1];
                n--;
            }
        }
        if(m==0) System.arraycopy(B, 0, A, 0, n);
    }
}

public class Solution {
    public void merge(int A[], int m, int B[], int n) {
        int p1 = m - 1;
        int p2 = n - 1;
        int index = m + n - 1;
        while (p1 >= 0 && p2 >= 0) {
            A[index--] = A[p1] > B[p2] ? A[p1--] : B[p2--];
        }
        while (p2 >= 0) {
            A[index--] = B[p2--];
        }
    }
}

 6、删除链表的倒数第n个节点

public ListNode removeNthFromEnd (ListNode head, int n)

 方案:先走n步,然后快慢一起走,直到最后

public String solve (String str)
public class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode first = head;
        ListNode second = head;
        for(int i = 0; i < n; i++)
            first = first.next;
        //如果n的值等于链表的长度,直接返回去掉头结点的链表
        if(first == null)
            return head.next;
        while(first.next != null)   //同时移动两个指针
        {
            first = first.next;
            second = second.next;
        }
        second.next = second.next.next;
        return head;
    }
}

7、反转字符串

public String solve (String str)

import java.util.*;
 
public class Solution {
    public static void main(String[] args){
        Solution s = new Solution();
        String str = "abcd";
        String str1 = s.solve(str);
        System.out.println(str1);
    }
    //思路:将字符串里的字符对称相互替换
    //     将字符串转为字符数组
    public String solve (String str) {
        char[] ch = str.toCharArray();
        for(int i = 0;i < ch.length/2;i++){
            char temp = ch[i];
            ch[i] = ch[ch.length-1-i];
            ch[ch.length-1-i] = temp;
        }
        String s = new String(ch);
        return s;
    }
}

或StringBuilder对象的reverse方法

8、NC54 数组中相加和为0的三元组

public ArrayList<ArrayList<Integer>> threeSum(int[] num)

 i循环,left和right从i右侧开始移动,判断三数之和并进行移动

import java.util.*;
public class Solution {
    public ArrayList<ArrayList<Integer>> threeSum(int[] num) {
        //双指针
        Arrays.sort(num);//排序
        ArrayList<ArrayList<Integer>> ans=new  ArrayList<ArrayList<Integer>>();
        for(int i=0;i<num.length-2;i++)
        {
            int left=i+1;
            int right=num.length-1;
            if(i>0&&num[i]==num[i-1])continue;//若相同,则跳过
            while(left<right)
            {
            if(num[i]+num[left]+num[right]==0)
                {   //三数相加为0,则输出
                    ArrayList<Integer> list=new ArrayList<>();
                    list.add(num[i]);
                    list.add(num[left]);
                    list.add(num[right]);
                    left++;right--;
                    ans.add(list);
                //若相同,则跳过
                    while(left<right&&num[left]==num[left-1])left++;
                    while(left<right&&num[right]==num[right+1])right--;
                }
             if(num[i]+num[left]+num[right]<0)left++;//三数相加小于0,left右移
             if(num[i]+num[left]+num[right]>0)right--;//三数和大于0,right左移
            }
        }
        return ans;
    }
}

9、NC128 接雨水问题

给定一个整形数组arr,已知其中所有的值都是非负的,将这个数组看作一个柱子高度图,计算按此排列的柱子,下雨之后能接多少雨水。

public long maxWater (int[] arr)

 方案:左右双指针找最大的作为对应的数组值,遍历原数组,结果为左右低的减去当前

public long maxWater (int[] arr) {
        // write code here
        if(arr.length <= 2){
            return 0;
        }
        int[] left = new int[arr.length];
        left[0] = 0;
        int leftMax = arr[0];
        for(int i = 1 ; i < arr.length; i++){
            leftMax = Math.max(leftMax,arr[i-1]);
            left[i] = leftMax;
        }
        int[]right = new int[arr.length];
        right[arr.length-1] = 0;
        int rightMax = arr[arr.length-1];
        for(int j = arr.length -2;j>=0;j--){
            rightMax = Math.max(rightMax,arr[j+1]);
            right[j] = rightMax;
        }
        long result = 0;
        for(int k = 1; k < arr.length-1;k++){
            result += Math.max(Math.min(left[k],right[k]) - arr[k],0);
        }
        return result;
    }

10、NC96 判断一个链表是否为回文结构

public boolean isPail (ListNode head)

 方法1:反转后比较

import java.util.*;
 
public class Solution {
 
    public boolean isPail (ListNode head) {
        ListNode slow = head, fast = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        ListNode reverseList = reverse(slow);
        ListNode cur1 = reverseList, cur2 = head;
        while (cur1 != null) {
            if (cur1.val != cur2.val) {
                return false;
            }
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
        return true;
    }
     
    public ListNode reverse(ListNode head) {
        ListNode cur = head, pre = null;
        while (cur != null) {
            ListNode tmp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
}

方法2:入栈再出栈比较

方法3:存入数组,判断是否相等

    public boolean isPail (ListNode head) {
        // write code here
        ArrayList<Integer> arr=new ArrayList<>();
        while (head!=null){
            arr.add(head.val);
            head=head.next;
        }
        for (int i=0;i<arr.size()-i-1;i++){
            //注意,此处最初写的是arr.get(i)!=arr.get(arr.size()-i-1),但是一直有测试例子无法通过
            //还是equal牛逼
            if (!arr.get(i).equals(arr.get(arr.size()-i-1))){
                return false;
            }
        }
        return true;
    }

方法4:双端队列(入队后两端同时出队)

public boolean isPail (ListNode head) {
        if (head == null || head.next == null) {
            return false;
        }
        Deque<Integer> deque = new ArrayDeque<>();
        while (head != null) {
            deque.addLast(head.val);
            head = head.next;
        }
        while (deque.size() > 1) {
            if (!deque.pollFirst().equals(deque.pollLast())) {
                return false;
            }
        }
        return true;
    }

11、NC82 滑动窗口的最大值

public ArrayList<Integer> maxInWindows(int [] num, int size)

 解法1:双指针

//经典双指针问题
import java.util.*;
public class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size) {
         ArrayList list = new ArrayList();
        if(num.length == 0 || size < 1 || size > num.length)  return list;
        int left = 0, right = size - 1;
       //在窗口中遍历求最大值并插入list,然后左右指针同时右移
        while(right < num.length){
            int max = num[left];
        for(int i = left; i <= right; i++){
            max = Math.max(max,num[i]);
        }
            left += 1;
            right += 1;
            list.add(max);
        }
        return list;
    }
}

解法2:双端队列

import java.util.*;
public class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size) {
        ArrayList<Integer> res = new ArrayList();
        if(size<=0||num.length == 0) return res;
        /**
        维护一个双端队列,里面从first到last存储的是滑动窗口里的值的降序排列的下标
        */
        Deque<Integer> queue = new LinkedList();
        for(int i=0;i<num.length;i++){
            /**
            如果当前加入的这个数大于了队列末尾的数,则一直弹出来,最后再加入末尾
            */
            while(!queue.isEmpty()&&num[queue.getLast()]<num[i]){
                queue.pollLast();
            }
            queue.addLast(i);
            /**
            根据当前下标以及滑动窗口的大小看看first是否过期,过期了就删除
            */
            if(queue.getFirst() == i-size){
                queue.pollFirst();
            }
            /**
            前几个元素不满足滑动窗口的大小,不加入,后面再加入
            */
            if(i>=size-1){
                res.add(num[queue.getFirst()]);
            }
        }
        return res;
    }
}

解法3:优先队列

import java.util.*;
public class Solution {
    public ArrayList<Integer> maxInWindows(int [] num, int size) {
        ArrayList<Integer> list = new ArrayList<>();
        if(size>num.length || size==0)
            return list;
        PriorityQueue<Integer> qu = new PriorityQueue<>(new Comparator<Integer>(){
            public int compare(Integer o1 ,Integer o2){
                return o2-o1;
            }
        });
        for(int i=0;i<size;i++){
            qu.add(num[i]);
        }
        list.add(qu.peek());
        qu.remove(num[0]);
        for(int i=size;i<num.length;i++){
            qu.add(num[i]);
            list.add(qu.peek());
            qu.remove(num[i-size+1]);
        }
        return list;
    }
}

12、NC28 最小覆盖子串

 public String minWindow (String S, String T)

解法1:滑动窗口

这道题的思路是:
1) begin开始指向0, end一直后移,直到begin - end区间包含T中所有字符。
记录窗口长度d
2) 然后begin开始后移移除元素,直到移除的字符是T中的字符则停止,此时T中有一个字符没被
包含在窗口,
3) 继续后移end,直到T中的所有字符被包含在窗口,重新记录最小的窗口d。
4) 如此循环知道end到S中的最后一个字符。
时间复杂度为O(n)
public class Solution {
    public String minWindow(String S, String T) {
        int[] map = new int[128];
        //init map, 记录T中每个元素出现的次数
        for(int i = 0; i < T.length(); i++) {
            map[T.charAt(i)]++;
        }
 
        // begin end两个指针指向窗口的首位,d记录窗口的长度, counter记录T中还有几个字符没被窗口包含
        int begin = 0, end = 0, d = Integer.MAX_VALUE, counter = T.length(), head = 0;
        // end指针一直向后遍历
        while(end < S.length()) {
            // map[] > 0 说明该字符在T中出现,counter-- 表示对应的字符被包含在了窗口,counter--, 如果s中的字符没有在T中出现,则map[]中对应的字符-1后变为负值
            if(map[S.charAt(end++)]-- > 0) {
                counter--;
            }
            // 当counter==0时,说明窗口已经包含了T中的所有字符
            while (counter == 0) {
                if(end - begin < d) {
                    d = end - (head = begin);
                }
                if(map[S.charAt(begin++)]++ == 0) {  // begin开始后移,继续向后寻找。如果begin后移后指向的字符在map中==0,表示是在T中出现的,如果没有出现,map[]中的值会是负值。
                    counter++;                      // 在T中的某个字符从窗口中移除,所以counter++。
                }
            }
        }
        return d==Integer.MAX_VALUE ? "" :S.substring(head, head+d);
    }
}

 13、划分链表

 public ListNode partition (ListNode head, int x)

方法:两个链表相连

    public ListNode partition (ListNode head, int x) {
        // write code here
        ListNode xiao=new ListNode(0);
        ListNode xiaotmp=xiao;
        ListNode da=new ListNode(0);
        ListNode datmp=da;
         
        while (head!=null){
            if (head.val<x){
                xiaotmp.next=new ListNode(head.val);
                xiaotmp=xiaotmp.next;
            }
            else {
                datmp.next=new ListNode(head.val);
                datmp=datmp.next;
            }
            head=head.next;
        }
        xiaotmp.next=da.next;
        return xiao.next;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值