【算法题型总结】--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 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; }
本文来自博客园,作者:哥们要飞,转载请注明原文链接:https://www.cnblogs.com/liujinhui/p/15124664.html