哈希表
3. 无重复字符的最长子串
-
题目描述
-
题解
class Solution { public int lengthOfLongestSubstring(String s) { if(s.length()==0) return 0; Map<Character,Integer> map=new HashMap<>(); int left=0,max=Integer.MIN_VALUE; for(int right=0;right<s.length();right++){ char cRight=s.charAt(right); map.put(cRight,map.getOrDefault(cRight,0)+1); while(map.get(cRight)>1){ //出现重复 char cLeft=s.charAt(left); map.put(cLeft,map.get(cLeft)-1); left++; } max=Math.max(max,right-left+1); } return max; } }
560. 和为 K 的子数组
-
题目描述
-
题解
为什么这题不可以用双指针/滑动窗口:因为
nums[i]可以小于0,也就是说右指针i向后移1位不能保证区间会增大,左指针j向后移1位也不能保证区间和会减小。给定j,i的位置没有二段性class Solution { public int subarraySum(int[] nums, int k) { //扫描一遍数组, 使用map记录出现同样的和的次数 //对每个i计算累计和sum并判断map内是否有sum-k Map<Integer,Integer> map=new HashMap<>(); map.put(0,1); int sum=0,res=0; for(int i=0;i<nums.length;i++){ sum+=nums[i]; if(map.containsKey(sum-k)){ res+=map.get(sum-k); } map.put(sum,map.getOrDefault(sum,0)+1); } return res; } }
1. 两数之和
-
题目描述
-
题解
class Solution { public int[] twoSum(int[] nums, int target) { Map<Integer,Integer> map=new HashMap<>(); for(int i=0;i<nums.length;i++){ int temp=target-nums[i]; if(map.containsKey(temp)){ return new int[]{map.get(temp),i}; } map.put(nums[i],i); } return null; } }
138. 复制带随机指针的链表
-
题目描述
-
题解
class Solution { public Node copyRandomList(Node head) { if(head == null) return head; // map方法,空间复杂度O(n) Node node = head; // 使用hash表存储旧结点和新结点的映射 Map<Node,Node> map = new HashMap<>(); while(node != null){ Node clone = new Node(node.val,null,null); map.put(node,clone); node = node.next; } node = head; while(node != null){ map.get(node).next = map.get(node.next); map.get(node).random = map.get(node.random); node = node.next; } return map.get(head); } }
76. 最小覆盖子串
-
题目描述
-
题解
class Solution { public String minWindow(String s, String t) { if(s.length()==0) return ""; if(t.length()>s.length()) return ""; char[] sArr=s.toCharArray(); char[] tArr=t.toCharArray(); Map<Character,Integer> needMap=new HashMap<>(); Map<Character,Integer> map=new HashMap<>(); int left=0,maxNum=0,start=0,minLen=Integer.MAX_VALUE; for(char c:tArr){ needMap.put(c,needMap.getOrDefault(c,0)+1); } for(int right=0;right<sArr.length;right++){ char ch=sArr[right]; map.put(ch,map.getOrDefault(ch,0)+1); if(map.get(ch).equals(needMap.get(ch))){ //满足条件的字符数 maxNum++; } //满足条件时缩小窗口 while(needMap.size()==maxNum){ //记录当前窗口 if(right-left<minLen){ start=left; minLen=right-left+1; } //缩小左边界 char cleft=sArr[left]; if(map.get(cleft).equals(needMap.get(cleft))){ maxNum--; } map.put(cleft,map.get(cleft)-1); left++; } } return minLen == Integer.MAX_VALUE ? "" : s.substring(start, start + minLen); } }
347. 前 K 个高频元素
-
题目描述

-
题解
class Solution { public int[] topKFrequent(int[] nums, int k) { int[] res=new int[k]; Map<Integer,Integer> map=new HashMap<>(); for(int n:nums){ map.put(n,map.getOrDefault(n,0)+1); } Set<Map.Entry<Integer,Integer>> set=map.entrySet(); PriorityQueue<Map.Entry<Integer,Integer>> queue=new PriorityQueue<>((p1,p2)->p1.getValue()-p2.getValue()); for(Map.Entry<Integer,Integer> entry:set){ queue.offer(entry); if(queue.size()>k){ queue.poll(); } } for(int i=k-1;i>=0;i--){ res[i]=queue.poll().getKey(); } return res; } }
166. 分数到小数
-
题目描述
-
题解
class Solution { public String fractionToDecimal(int numerator, int denominator) { //用哈希表记录所有被除数的下标 //如果出现了重复的被除数,则证明出现了循环,把左括号塞到记录的下标位置,右括号放在最后 StringBuilder sb = new StringBuilder(); long a = numerator, b = denominator; if (a > 0 && b < 0 || a < 0 && b > 0) sb.append('-'); a = Math.abs(a); b = Math.abs(b); sb.append(a / b); if (a % b == 0) return sb.toString(); sb.append('.'); Map<Long, Integer> map = new HashMap<>(); while ((a = (a % b) * 10) > 0 && !map.containsKey(a)) { map.put(a, sb.length()); sb.append(a / b); } if (a == 0) return sb.toString(); return sb.insert(map.get(a).intValue(), '(').append(')').toString(); } }
974. 和可被 K 整除的子数组
-
题目描述
-
题解
题目求连续子数组的和能被K整除,连续子数组的和就可以表示为前缀和的差
比如 sum(A[i : j + 1]) = s[j + 1] - s[i]
如果两个数的差能被K整除,就说明这两个数 mod K得到的结果相同
只要找有多少对 mod k 相同的数就可以组合一下得到结果,
举例 对于A= [4,5,0,-2,-3,1] K = 5 s = [0, 4, 9, 9, 7, 4, 5]
kcnt = [2, 0, 1, 0, 4] 代表有s中有两个元素的余数都为0(即0和5)
1个元素的余数为2(即7),四个元素的余数为4(即4994)
所以在保证余数相同的情况下,取出两个数都可以得到一组答案
对于这个例子答案就是 C22 + C12 + C42 = 1 + 0 + 6 = 7
/** sum[i]=A[0]+A[1]+…+A[i] 某个子数组(如下标从i到j)的和可以表示为 A[i]+…+A[j]=sum[j]−sum[i−1] 判断子数组的和能否被 K 整除就等价于判断 (sum[j] - sum[i-1]) mod K == 0 等价于判断sum[j] mod K == sum[i-1] mod K */ class Solution { public int subarraysDivByK(int[] A, int K) { int sum = 0, result = 0; Map<Integer, Integer> map = new HashMap<>(); map.put(0, 1); for (int n : A) { sum += n; int curMod = (sum%K+K)%K; int preCount = map.getOrDefault(curMod, 0); result += preCount; map.put(curMod, preCount + 1); } return result; } }
O(1) 时间插入、删除和获取随机元素
-
题目描述
-
题解
class RandomizedSet { List<Integer> list=new ArrayList(); Map<Integer,Integer> map=new HashMap(); Random random=new Random(); public RandomizedSet() { } public boolean insert(int val) { if(map.containsKey(val)) return false; map.put(val,list.size()); list.add(list.size(),val); return true; } public boolean remove(int val) { if(!map.containsKey(val)) return false; int idx=map.get(val); int lastElement=list.get(list.size()-1); map.put(lastElement,idx); list.set(idx,lastElement); list.remove(list.size()-1); map.remove(val); return true; } public int getRandom() { return list.get(random.nextInt(list.size())); } } /** * Your RandomizedSet object will be instantiated and called as such: * RandomizedSet obj = new RandomizedSet(); * boolean param_1 = obj.insert(val); * boolean param_2 = obj.remove(val); * int param_3 = obj.getRandom(); */
数组
53. 最大子数组和
-
题目描述
-
题解
class Solution { public int maxSubArray(int[] nums) { int[] dp=new int[nums.length]; int res=nums[0]; dp[0]=nums[0]; for(int i=1;i<nums.length;i++){ dp[i]=Math.max(nums[i],dp[i-1]+nums[i]); res=Math.max(res,dp[i]); } return res; } }
33. 搜索旋转排序数组
-
题目描述
-
题解
class Solution { public int search(int[] nums, int target) { if(nums.length==0) return -1; if(nums.length==1) return nums[0]==target?0:-1; int left=0,right=nums.length-1; while(left<=right){ int mid=(left+right)/2; if(nums[mid]==target) return mid; else if(nums[mid]<nums[right]){ if(nums[mid]<target && target<=nums[right]){ left=mid+1; }else{ right=mid-1; } }else{ if(nums[left]<=target && target<nums[mid]){ right=mid-1; }else{ left=mid+1; } } } return -1; } }
4. 寻找两个正序数组的中位数
-
题目描述
-
题解
class Solution { public double findMedianSortedArrays(int[] nums1, int[] nums2) { int length1 = nums1.length, length2 = nums2.length; int totalLength = length1 + length2; if (totalLength % 2 == 1) { int midIndex = totalLength / 2; double median = getKthElement(nums1, nums2, midIndex + 1); return median; } else { int midIndex1 = totalLength / 2 - 1, midIndex2 = totalLength / 2; double median = (getKthElement(nums1, nums2, midIndex1 + 1) + getKthElement(nums1, nums2, midIndex2 + 1)) / 2.0; return median; } } public int getKthElement(int[] nums1, int[] nums2, int k) { int length1 = nums1.length, length2 = nums2.length; int index1 = 0, index2 = 0; int kthElement = 0; while (true) { // 边界情况 if (index1 == length1) { return nums2[index2 + k - 1]; } if (index2 == length2) { return nums1[index1 + k - 1]; } if (k == 1) { return Math.min(nums1[index1], nums2[index2]); } // 正常情况 int half = k / 2; int newIndex1 = Math.min(index1 + half, length1) - 1; int newIndex2 = Math.min(index2 + half, length2) - 1; int pivot1 = nums1[newIndex1], pivot2 = nums2[newIndex2]; if (pivot1 <= pivot2) { k -= (newIndex1 - index1 + 1); index1 = newIndex1 + 1; } else { k -= (newIndex2 - index2 + 1); index2 = newIndex2 + 1; } } } }
48. 旋转图像
-
题目描述
-
题解
class Solution { public void rotate(int[][] matrix) { int n=matrix.length; for(int i=0;i<n/2;i++){ for(int j=0;j<(n+1)/2;j++){ int temp=matrix[i][j]; matrix[i][j]=matrix[n-j-1][i]; matrix[n-j-1][i]=matrix[n-i-1][n-j-1]; matrix[n-i-1][n-j-1]=matrix[j][n-i-1]; matrix[j][n-i-1]=temp; } } } }
560. 和为 K 的子数组
-
题目描述
-
题解
class Solution { public int subarraySum(int[] nums, int k) { //扫描一遍数组, 使用map记录出现同样的和的次数 //对每个i计算累计和sum并判断map内是否有sum-k Map<Integer,Integer> map=new HashMap<>(); map.put(0,1); int sum=0,res=0; for(int i=0;i<nums.length;i++){ sum+=nums[i]; if(map.containsKey(sum-k)){ res+=map.get(sum-k); } map.put(sum,map.getOrDefault(sum,0)+1); } return res; } }
56. 合并区间
-
题目描述
-
题解
class Solution { public int[][] merge(int[][] intervals) { List<int[]> res=new LinkedList<>(); Arrays.sort(intervals,(o1,o2)->Integer.compare(o1[0],o2[0])); int start=intervals[0][0]; for(int i=1;i<intervals.length;i++){ if(intervals[i][0]>intervals[i-1][1]){ res.add(new int[]{start,intervals[i-1][1]}); start=intervals[i][0]; }else{ intervals[i][1]=Math.max(intervals[i][1],intervals[i-1][1]); } } res.add(new int[]{start,intervals[intervals.length-1][1]}); return res.toArray(new int[res.size()][]); } }
15. 三数之和
-
题目描述
-
题解
class Solution { public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> res=new ArrayList<>(); if(nums.length==0) return res; Arrays.sort(nums); for(int i=0;i<nums.length;i++){ if(nums[i]>0) break; if(i>0 && nums[i]==nums[i-1]) continue; int j=i+1,k=nums.length-1; while(j<k){ int sum=nums[i]+nums[j]+nums[k]; if(sum==0){ res.add(Arrays.asList(nums[i],nums[j],nums[k])); while(j<k && nums[j]==nums[j+1]) j++; while(j<k && nums[k]==nums[k-1]) k--; j++; k--; }else if(sum>0){ k--; }else{ j++; } } } return res; } }
121. 买卖股票的最佳时机
-
题目描述
-
题解
class Solution { public int maxProfit(int[] prices) { int[][] dp=new int[prices.length][2]; dp[0][0]=-prices[0]; for(int i=1;i<prices.length;i++){ dp[i][0]=Math.max(dp[i-1][0],-prices[i]); dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]+prices[i]); } return dp[prices.length-1][1]; } }
39. 组合总和
-
题目描述
-
题解
class Solution { List<List<Integer>> res=new ArrayList<>(); List<Integer> temp=new ArrayList<>(); public List<List<Integer>> combinationSum(int[] candidates, int target) { Arrays.sort(candidates); backtracking(candidates,target,0,0); return res; } public void backtracking(int[] candidates ,int target,int startIndex,int sum){ if(sum==target){ res.add(new ArrayList(temp)); return; } for(int i=startIndex;i<candidates.length;i++){ if(sum+candidates[i]>target) break; temp.add(candidates[i]); sum+=candidates[i]; backtracking(candidates,target,i,sum); sum-=temp.get(temp.size()-1); temp.remove(temp.size()-1); } } }
152. 乘积最大子数组
-
题目描述
-
题解
class Solution { public int maxProduct(int[] nums) { //一个保存最大的,一个保存最小的 int max = Integer.MIN_VALUE, imax = 1, imin = 1; for(int i=0; i<nums.length; i++){ //如果数组的数是负数,那么会导致最大的变最小的,最小的变最大的。因此交换两个的值 if(nums[i] < 0){ int tmp = imax; imax = imin; imin = tmp;} imax = Math.max(imax*nums[i], nums[i]); imin = Math.min(imin*nums[i], nums[i]); max = Math.max(max, imax); } return max; } }
153. 寻找旋转排序数组中的最小值
-
题目描述
-
题解
class Solution { public int findMin(int[] nums) { int low = 0; int high = nums.length - 1; while (low <high) { int mid = low + (high - low) / 2; if (nums[mid] < nums[high]) { high = mid; } else { low = mid + 1; } } return nums[low]; } }
64. 最小路径和
-
题目描述
-
题解
class Solution { public int minPathSum(int[][] grid) { int m=grid.length,n=grid[0].length; int[][] dp=new int[m][n]; dp[0][0]=grid[0][0]; for(int i=1;i<m;i++)dp[i][0]+=dp[i-1][0]+grid[i][0]; for(int j=1;j<n;j++)dp[0][j]+=dp[0][j-1]+grid[0][j]; for(int i=1;i<m;i++){ for(int j=1;j<n;j++){ dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j]; } } return dp[m-1][n-1]; } }
34. 在排序数组中查找元素的第一个和最后一个位置
-
题目描述
-
题解
class Solution { public int[] searchRange(int[] nums, int target) { int[] res=new int[]{-1,-1}; if(nums.length==0) return res; res[0]=searchBorder(nums,target,true); res[1]=searchBorder(nums,target,false); return res; } public int searchBorder(int[] nums,int target,boolean leftOrRight){ int temp=-1; int left=0,right=nums.length-1; while(left<=right){ int mid=(left+right)/2; if(nums[mid]<target){ left=mid+1; }else if(nums[mid]>target){ right=mid-1; }else{ temp=mid; if(leftOrRight){ right=mid-1; }else{ left=mid+1; } } } return temp; } }
189. 轮转数组
-
题目描述
-
题解
class Solution { public void rotate(int[] nums, int k) { //取模取模取模 int len=nums.length; k%=len; //先翻转前n-k个 reserve(nums,0,len-1-k); //在翻转倒数k个 reserve(nums,len-k,len-1); //整体反转 reserve(nums,0,len-1); } public void reserve(int[] nums,int start,int end){ while(start<=end){ int temp=nums[start]; nums[start]=nums[end]; nums[end]=temp; start++; end--; } } }
41. 缺失的第一个正数
-
题目描述
-
题解
class Solution { public int firstMissingPositive(int[] nums) { int len = nums.length; if (len == 0) { return 1; } for (int i = 0; i < len; ++i) { //需要考虑指针移动情况,大于0,小于len+1,不等与i+1,两个交换的数相等时,防止死循环 while (nums[i] > 0 && nums[i] < len + 1 && nums[i] != i+1 && nums[i] != nums[nums[i]-1]) { swap(nums,i,nums[i] - 1); } } //遍历寻找缺失的正整数 for (int i = 0; i < len; ++i) { if(nums[i] != i+1) { return i+1; } } return len + 1; } //交换 public void swap(int[] nums, int i, int j) { int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; } }
排序
179. 最大数
-
题目描述
-
题解
class Solution { public String largestNumber(int[] nums) { int n = nums.length; // 转换成包装类型,以便传入 Comparator 对象(此处为 lambda 表达式) Integer[] numsArr = new Integer[n]; for (int i = 0; i < n; i++) { numsArr[i] = nums[i]; } Arrays.sort(numsArr, (x, y) -> { long sx = 10, sy = 10; while (sx <= x) { sx *= 10; } while (sy <= y) { sy *= 10; } return (int) (-sy * x - y + sx * y + x); }); if (numsArr[0] == 0) { return "0"; } StringBuilder ret = new StringBuilder(); for (int num : numsArr) { ret.append(num); } return ret.toString(); } }
56. 合并区间
-
题目描述
-
题解
class Solution { public int[][] merge(int[][] intervals) { List<int[]> res=new LinkedList<>(); Arrays.sort(intervals,(o1,o2)->Integer.compare(o1[0],o2[0])); int start=intervals[0][0]; for(int i=1;i<intervals.length;i++){ if(intervals[i][0]>intervals[i-1][1]){ res.add(new int[]{start,intervals[i-1][1]}); start=intervals[i][0]; }else{ intervals[i][1]=Math.max(intervals[i][1],intervals[i-1][1]); } } res.add(new int[]{start,intervals[intervals.length-1][1]}); return res.toArray(new int[res.size()][]); } }
148. 排序链表
-
题目描述
-
题解
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ class Solution { /** * 参考:Sort List——经典(链表中的归并排序) * * 归并排序法:在动手之前一直觉得空间复杂度为常量不太可能,因为原来使用归并时,都是 O(N)的, * 需要复制出相等的空间来进行赋值归并。对于链表,实际上是可以实现常数空间占用的(链表的归并 * 排序不需要额外的空间)。利用归并的思想,递归地将当前链表分为两段,然后merge,分两段的方 * 法是使用 fast-slow 法,用两个指针,一个每次走两步,一个走一步,知道快的走到了末尾,然后 * 慢的所在位置就是中间位置,这样就分成了两段。merge时,把两段头部节点值比较,用一个 p 指向 * 较小的,且记录第一个节点,然后 两段的头一步一步向后走,p也一直向后走,总是指向较小节点, * 直至其中一个头为NULL,处理剩下的元素。最后返回记录的头即可。 * * 主要考察3个知识点, * 知识点1:归并排序的整体思想 * 知识点2:找到一个链表的中间节点的方法 * 知识点3:合并两个已排好序的链表为一个新的有序链表 */ public ListNode sortList(ListNode head) { return head == null ? null : mergeSort(head); } private ListNode mergeSort(ListNode head) { if (head.next == null) { return head; } ListNode p = head, q = head, pre = null; while (q != null && q.next != null) { pre = p; p = p.next; q = q.next.next; } pre.next = null; ListNode l = mergeSort(head); ListNode r = mergeSort(p); return merge(l, r); } ListNode merge(ListNode l, ListNode r) { ListNode dummyHead = new ListNode(0); ListNode cur = dummyHead; while (l != null && r != null) { if (l.val <= r.val) { cur.next = l; cur = cur.next; l = l.next; } else { cur.next = r; cur = cur.next; r = r.next; } } if (l != null) { cur.next = l; } if (r != null) { cur.next = r; } return dummyHead.next; } }
剑指 Offer 45. 把数组排成最小的数
-
题目描述
-
题解
/** 规定 排序判断规则为:快排 若拼接字符串 x + y > y + xx+y>y+x ,则 xx “大于” yy 反之,若 x + y < y + xx+y<y+x ,则 xx “小于” yy */ class Solution { public String minNumber(int[] nums) { String[] strs = new String[nums.length]; for (int i = 0; i < nums.length; i++) { strs[i] = String.valueOf(nums[i]); } quickSort(strs, 0, strs.length - 1); StringBuilder res = new StringBuilder(); for (String s : strs) res.append(s); return res.toString(); } public void quickSort(String[] strs, int low, int high) { if (low < high) { int middle = getMiddle(strs, low, high); quickSort(strs, low, middle - 1); quickSort(strs, middle + 1, high); } } public int getMiddle(String[] strs, int low, int high) { //数组的第一个数为基准元素 String temp = strs[low]; while (low < high) { //从后向前找比基准小的数 while (low < high && (strs[high] + temp).compareTo(temp + strs[high]) >= 0) high--; //把比基准小的数移到低端 strs[low] = strs[high]; //从前向后找比基准大的数 while (low < high && (strs[low] + temp).compareTo(temp + strs[low]) <= 0) low++; //把比基准大的数移到高端 strs[high] = strs[low]; } strs[low] = temp; return low; } }
912. 排序数组
-
题目描述
-
题解
class Solution { public int[] sortArray(int[] nums) { if(nums.length <=1)return nums; //qSort(nums,0,nums.length-1); //selectSort(nums); // insertSort(nums); // shellSort(nums); // bucketSort(nums); // countSort(nums); // mergeSort(nums,0,nums.length-1); heapSort(nums); return nums; } //快排 void qSort(int[] arr,int s,int e){ int l = s, r = e; if(l < r){ int temp = arr[l]; while(l < r){ while(l < r && arr[r] >= temp) r--; if(l < r) arr[l] = arr[r]; while(l < r && arr[l] < temp) l++; if(l < r) arr[r] = arr[l]; } arr[l] = temp; qSort(arr,s,l); qSort(arr,l + 1, e); } } //选择排序 void selectSort(int[] arr){ int min; for(int i = 0;i<arr.length;i++){ min = i; for(int j = i;j<arr.length;j++){ if(arr[j] < arr[min]){ min = j; } } if(min != i) { int temp = arr[i]; arr[i] = arr[min]; arr[min] = temp; } } } /** * * 插入排序:数列前面部分看为有序,依次将后面的无序数列元素插入到前面的有序数列中,初始状态有序数列仅有一个元素,即首元素。在将无序数列元素插入有序数列的过程中,采用了逆序遍历有序数列,相较于顺序遍历会稍显繁琐,但当数列本身已近排序状态效率会更高。 * * 时间复杂度:O(N2) 稳定性:稳定 * @param arr */ public void insertSort(int arr[]){ for(int i = 1; i < arr.length; i++){ int rt = arr[i]; for(int j = i - 1; j >= 0; j--){ if(rt < arr[j]){ arr[j + 1] = arr[j]; arr[j] = rt; }else{ break; } } } } /** * 希尔排序 - 插入排序的改进版。为了减少数据的移动次数,在初始序列较大时取较大的步长,通常取序列长度的一半,此时只有两个元素比较,交换一次;之后步长依次减半直至步长为1,即为插入排序,由于此时序列已接近有序,故插入元素时数据移动的次数会相对较少,效率得到了提高。 * * 时间复杂度:通常认为是O(N3/2) ,未验证 稳定性:不稳定 * @param arr */ void shellSort(int arr[]){ int d = arr.length >> 1; while(d >= 1){ for(int i = d; i < arr.length; i++){ int rt = arr[i]; for(int j = i - d; j >= 0; j -= d){ if(rt < arr[j]){ arr[j + d] = arr[j]; arr[j] = rt; }else break; } } d >>= 1; } } /** * 桶排序 - 实现线性排序,但当元素间值得大小有较大差距时会带来内存空间的较大浪费。首先,找出待排序列中得最大元素max,申请内存大小为max + 1的桶(数组)并初始化为0;然后,遍历排序数列,并依次将每个元素作为下标的桶元素值自增1; * 最后,遍历桶元素,并依次将值非0的元素下标值载入排序数列(桶元素>1表明有值大小相等的元素,此时依次将他们载入排序数列),遍历完成,排序数列便为有序数列。 * * 时间复杂度:O(x*N) 稳定性:稳定 * @param arr */ void bucketSort(int[] arr){ int[] bk = new int[50000 * 2 + 1]; for(int i = 0; i < arr.length; i++){ bk[arr[i] + 50000] += 1; } int ar = 0; for(int i = 0; i < bk.length; i++){ for(int j = bk[i]; j > 0; j--){ arr[ar++] = i - 50000; } } } /** * 基数排序 - 桶排序的改进版,桶的大小固定为10,减少了内存空间的开销。首先,找出待排序列中得最大元素max,并依次按max的低位到高位对所有元素排序; * 桶元素10个元素的大小即为待排序数列元素对应数值为相等元素的个数,即每次遍历待排序数列,桶将其按对应数值位大小分为了10个层级,桶内元素值得和为待排序数列元素个数。 * @param arr */ void countSort(int[] arr){ int[] bk = new int[19]; Integer max = Integer.MIN_VALUE; for(int i = 0; i < arr.length; i++){ if(max < Math.abs(arr[i])) max = arr[i]; } if(max < 0) max = -max; max = max.toString().length(); int [][] bd = new int[19][arr.length]; for(int k = 0; k < max; k++) { for (int i = 0; i < arr.length; i++) { int value = (int)(arr[i] / (Math.pow(10,k)) % 10); bd[value+9][bk[value+9]++] = arr[i]; } int fl = 0; for(int l = 0; l < 19; l++){ if(bk[l] != 0){ for(int s = 0; s < bk[l]; s++){ arr[fl++] = bd[l][s]; } } } bk = new int[19]; fl = 0; } } /** * 归并排序 - 采用了分治和递归的思想,递归&分治-排序整个数列如同排序两个有序数列,依次执行这个过程直至排序末端的两个元素,再依次向上层输送排序好的两个子列进行排序直至整个数列有序(类比二叉树的思想,from down to up)。 * * 时间复杂度:O(NlogN) 稳定性:稳定 * @param arr */ void mergeSortInOrder(int[] arr,int bgn,int mid, int end){ int l = bgn, m = mid +1, e = end; int[] arrs = new int[end - bgn + 1]; int k = 0; while(l <= mid && m <= e){ if(arr[l] < arr[m]){ arrs[k++] = arr[l++]; }else{ arrs[k++] = arr[m++]; } } while(l <= mid){ arrs[k++] = arr[l++]; } while(m <= e){ arrs[k++] = arr[m++]; } for(int i = 0; i < arrs.length; i++){ arr[i + bgn] = arrs[i]; } } void mergeSort(int[] arr, int bgn, int end) { if(bgn >= end){ return; } int mid = (bgn + end) >> 1; mergeSort(arr,bgn,mid); mergeSort(arr,mid + 1, end); mergeSortInOrder(arr,bgn,mid,end); } /** * 堆排序 - 堆排序的思想借助于二叉堆中的最大堆得以实现。首先,将待排序数列抽象为二叉树,并构造出最大堆;然后,依次将最大元素(即根节点元素)与待排序数列的最后一个元素交换(即二叉树最深层最右边的叶子结点元素); * 每次遍历,刷新最后一个元素的位置(自减1),直至其与首元素相交,即完成排序。 * * 时间复杂度:O(NlogN) 稳定性:不稳定 * * @param arr */ void heapSort(int[] nums) { int size = nums.length; for (int i = size/2-1; i >=0; i--) { adjust(nums, size, i); } for (int i = size - 1; i >= 1; i--) { int temp = nums[0]; nums[0] = nums[i]; nums[i] = temp; adjust(nums, i, 0); } } void adjust(int []nums, int len, int index) { int l = 2 * index + 1; int r = 2 * index + 2; int maxIndex = index; if (l<len&&nums[l]>nums[maxIndex])maxIndex = l; if (r<len&&nums[r]>nums[maxIndex])maxIndex = r; if (maxIndex != index) { int temp = nums[maxIndex]; nums[maxIndex] = nums[index]; nums[index] = temp; adjust(nums, len, maxIndex); } } }
双指针
15. 三数之和
-
题目描述
-
题解
class Solution { public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> res=new ArrayList<>(); if(nums.length==0) return res; Arrays.sort(nums); for(int i=0;i<nums.length;i++){ if(nums[i]>0) break; if(i>0 && nums[i]==nums[i-1]) continue; int j=i+1,k=nums.length-1; while(j<k){ int sum=nums[i]+nums[j]+nums[k]; if(sum==0){ res.add(Arrays.asList(nums[i],nums[j],nums[k])); while(j<k && nums[j]==nums[j+1]) j++; while(j<k && nums[k]==nums[k-1]) k--; j++; k--; }else if(sum>0){ k--; }else{ j++; } } } return res; } }
141. 环形链表
-
题目描述
-
题解
public class Solution { public boolean hasCycle(ListNode head) { ListNode fast=head; ListNode slow=head; while(fast!=null && fast.next!=null){ fast=fast.next.next; slow=slow.next; if(slow==fast){ return true; } } return false; } }
142. 环形链表 II
-
题目描述
-
题解
public class Solution { public ListNode detectCycle(ListNode head) { if(head==null) return head; ListNode fast=head; ListNode slow=head; while(fast!=null && fast.next!=null){ fast=fast.next.next; slow=slow.next; if(fast==slow){ fast=head; while(fast!=slow){ fast=fast.next; slow=slow.next; } return slow; } } return null; } }
剑指 Offer 04. 二维数组中的查找
-
题目描述
-
题解
从右上角开始走,利用这个顺序关系可以在
O(m+n)的复杂度下解决这个题:- 如果当前位置元素比target小,则row++
- 如果当前位置元素比target大,则col--
- 如果相等,返回true
- 如果越界了还没找到,说明不存在,返回false
class Solution { public boolean findNumberIn2DArray(int[][] matrix, int target) { if(matrix == null || matrix.length == 0) { return false; } int m = matrix.length, n = matrix[0].length; int row = 0, col = n - 1; while(row < m && col >= 0) { if(matrix[row][col] > target) { col--; }else if(matrix[row][col] < target) { row++; }else { return true; } } return false; } }
88. 合并两个有序数组
-
题目描述
-
题解
class Solution { public void merge(int[] nums1, int m, int[] nums2, int n) { int p1=0,p2=0,cur=0; int[] sorted=new int[m+n]; while(p1<m || p2<n){ if(p1==m){ cur=nums2[p2++]; }else if(p2==n){ cur=nums1[p1++]; }else if(nums1[p1]<nums2[p2]){ cur=nums1[p1++]; }else{ cur=nums2[p2++]; } sorted[p1+p2-1]=cur; } for(int i=0;i<sorted.length;i++){ nums1[i]=sorted[i]; } } }
287. 寻找重复数
-
题目描述
-
题解
class Solution { public int findDuplicate(int[] nums) { /** 快慢指针思想, fast 和 slow 是指针, nums[slow] 表示取指针对应的元素 注意 nums 数组中的数字都是在 1 到 n 之间的(在数组中进行游走不会越界), 因为有重复数字的出现, 所以这个游走必然是成环的, 环的入口就是重复的元素, 即按照寻找链表环入口的思路来做 **/ int fast = 0, slow = 0; while(true) { fast = nums[nums[fast]]; slow = nums[slow]; if(slow == fast) { fast = 0; while(nums[slow] != nums[fast]) { fast = nums[fast]; slow = nums[slow]; } return nums[slow]; } } } }
80. 删除有序数组中的重复项 II
-
题目描述
-
题解
本题要求相同元素最多出现两次而非一次,所以我们需要检查上个应该被保留的元素nums[slow−2] 是否和当前待检查元素nums[fast] 相同。当且仅当 nums[slow−2]=nums[fast] 时,当前待检查元素nums[fast] 不应该被保留(因为此时必然有 nums[slow−2]=nums[slow−1]=nums[fast])。最后slow 即为处理好的数组的长度。
特别地,数组的前两个数必然可以被保留,因此对于长度不超过 2 数组,我们无需进行任何处理,对于长度超过 2 的数组,我们直接将双指针的初始值设为 2 即可
class Solution { public int removeDuplicates(int[] nums) { int n = nums.length; if (n <= 2) { return n; } int slow = 2, fast = 2; while (fast < n) { if (nums[slow - 2] != nums[fast]) { nums[slow] = nums[fast]; ++slow; } ++fast; } return slow; } }
986. 区间列表的交集
-
题目描述
-
题解
class Solution { public int[][] intervalIntersection(int[][] firstList, int[][] secondList) { List<int[]> ans = new ArrayList(); int i = 0, j = 0; while (i < firstList.length && j < secondList.length) { // Let's check if A[i] intersects B[j]. // lo - the startpoint of the intersection // hi - the endpoint of the intersection int lo = Math.max(firstList[i][0], secondList[j][0]); int hi = Math.min(firstList[i][1], secondList[j][1]); if (lo <= hi) ans.add(new int[]{lo, hi}); // Remove the interval with the smallest endpoint if (firstList[i][1] < secondList[j][1]) i++; else j++; } return ans.toArray(new int[ans.size()][]); } }
二分查找
33. 搜索旋转排序数组
-
题目描述
-
题解
class Solution { public int search(int[] nums, int target) { if(nums.length==0) return -1; if(nums.length==1) return nums[0]==target?0:-1; int left=0,right=nums.length-1; while(left<=right){ int mid=(left+right)/2; if(nums[mid]==target) return mid; else if(nums[mid]<nums[right]){ if(nums[mid]<target && target<=nums[right]){ left=mid+1; }else{ right=mid-1; } }else{ if(nums[left]<=target && target<nums[mid]){ right=mid-1; }else{ left=mid+1; } } } return -1; } }
4. 寻找两个正序数组的中位数
240. 搜索二维矩阵 II
-
题目描述
-
题解
class Solution { public boolean searchMatrix(int[][] matrix, int target) { int m = matrix.length, n = matrix[0].length; int x = 0, y = n - 1; while (x < m && y >= 0) { if (matrix[x][y] == target) { return true; } if (matrix[x][y] > target) { --y; } else { ++x; } } return false; } }
34. 在排序数组中查找元素的第一个和最后一个位置
-
题目描述
-
题解
class Solution { public int[] searchRange(int[] nums, int target) { int[] res=new int[]{-1,-1}; if(nums.length==0) return res; res[0]=searchBorder(nums,target,true); res[1]=searchBorder(nums,target,false); return res; } public int searchBorder(int[] nums,int target,boolean leftOrRight){ int temp=-1; int left=0,right=nums.length-1; while(left<=right){ int mid=(left+right)/2; if(nums[mid]<target){ left=mid+1; }else if(nums[mid]>target){ right=mid-1; }else{ temp=mid; if(leftOrRight){ right=mid-1; }else{ left=mid+1; } } } return temp; } }
153. 寻找旋转排序数组中的最小值
-
题目描述
-
题解
class Solution { public int findMin(int[] nums) { int low = 0; int high = nums.length - 1; while (low <high) { int mid = low + (high - low) / 2; if (nums[mid] < nums[high]) { high = mid; } else { low = mid + 1; } } return nums[low]; } }
69. x 的平方根
-
题目描述
-
题解
class Solution { public int mySqrt(int x) { int left = 1, right = x; while(left<=right){ int mid = left + (right - left) / 2; if(x/mid>mid){ left = mid + 1; }else if (x / mid < mid) { right=mid-1; }else { return mid; } } return right; } }
1095. 山脉数组中查找目标值
-
题目描述
-
题解
class Solution { public int findInMountainArray(int target, MountainArray mountainArr) { //三次二分查找: // 1. 全局二分 找到 最大值对应的下标 // 2. 0~最大值 二分 查找,找得到返回 // 3. 最大值~结尾, 二分查找,找得到返回 int l = 0, r = mountainArr.length() - 1; while (l < r) { int mid = (l + r) / 2; if (mountainArr.get(mid) < mountainArr.get(mid + 1)) { l = mid + 1; } else { r = mid; } } int peak = l; int index = binarySearch(mountainArr, target, 0, peak, true); if (index != -1) { return index; } return binarySearch(mountainArr, target, peak + 1, mountainArr.length() - 1, false); } public int binarySearch(MountainArray mountainArr, int target, int l, int r, boolean flag) { if (!flag) { target *= -1; } while (l <= r) { int mid = (l + r) / 2; int cur = mountainArr.get(mid) * (flag ? 1 : -1); if (cur == target) { return mid; } else if (cur < target) { l = mid + 1; } else { r = mid - 1; } } return -1; } }
162. 寻找峰值
-
题目描述
-
题解
class Solution { public int findPeakElement(int[] nums) { int left = 0, right = nums.length - 1; while(left < right) { int mid = left + (right - left) / 2; if (nums[mid] > nums[mid + 1]) { right = mid; } else { left = mid + 1; } } return left; } }
74. 搜索二维矩阵
-
题目描述
-
题解
class Solution { public boolean searchMatrix(int[][] matrix, int target) { int m = matrix.length, n = matrix[0].length; int low = 0, high = m * n - 1; while (low <= high) { int mid = (high - low) / 2 + low; int x = matrix[mid / n][mid % n]; if (x < target) { low = mid + 1; } else if (x > target) { high = mid - 1; } else { return true; } } return false; } }
面试题 10.03. 搜索旋转数组
-
题目描述
-
题解
class Solution { public int search(int[] arr, int target) { if(arr[0]==target) return 0; int l=0; int r=arr.length-1; int mid=0; while(l<=r){ mid=l+(r-l)/2; //mid值==target,则继续往左搜寻,找到最小的索引,最小索引一定不为0 if(arr[mid]==target){ while(mid>0&&arr[mid-1]==arr[mid]) mid--; return mid; } //说明mid~r是递增序列,判读target是否在中间 if(arr[mid]<arr[r]){ if(arr[mid]<target&&target<=arr[r]) l=mid+1; else r=mid-1; } //说明 l~mid 是递增序列,判读target是否在中间 else if(arr[mid]>arr[r]){ if(arr[l]<=target&&target<arr[mid]) r=mid-1; else l=mid+1; } //arr[mid]==arr[r]说明要么r~0~mid都相等,要么mid~r都相等,无论哪种r 都可以舍去 else{ r--; } } return -1; } }
滑动窗口
3. 无重复字符的最长子串
-
题目描述
-
题解
class Solution { public int lengthOfLongestSubstring(String s) { if(s.length()==0) return 0; Map<Character,Integer> map=new HashMap<>(); int left=0,max=Integer.MIN_VALUE; for(int right=0;right<s.length();right++){ char cRight=s.charAt(right); map.put(cRight,map.getOrDefault(cRight,0)+1); while(map.get(cRight)>1){ //出现重复 char cLeft=s.charAt(left); map.put(cLeft,map.get(cLeft)-1); left++; } max=Math.max(max,right-left+1); } return max; } }
239. 滑动窗口最大值
-
题目描述
-
题解
class Solution { public int[] maxSlidingWindow(int[] nums, int k) { int[] res=new int[nums.length-k+1]; int index=0; Deque<Integer> deque=new LinkedList<>(); for(int i=0;i<nums.length;i++){ while(!deque.isEmpty() && deque.peek()<i-k+1){ deque.poll(); } while(!deque.isEmpty() && nums[deque.peekLast()]<nums[i]){ deque.pollLast(); } deque.offer(i); if(i>=k-1){ res[index++]=nums[deque.peek()]; } } return res; } }
76. 最小覆盖子串
-
题目描述
-
题解
class Solution { public String minWindow(String s, String t) { if(s.length()==0) return ""; if(t.length()>s.length()) return ""; char[] sArr=s.toCharArray(); char[] tArr=t.toCharArray(); Map<Character,Integer> needMap=new HashMap<>(); Map<Character,Integer> map=new HashMap<>(); int left=0,maxNum=0,start=0,minLen=Integer.MAX_VALUE; for(char c:tArr){ needMap.put(c,needMap.getOrDefault(c,0)+1); } for(int right=0;right<sArr.length;right++){ char ch=sArr[right]; map.put(ch,map.getOrDefault(ch,0)+1); if(map.get(ch).equals(needMap.get(ch))){ //满足条件的字符数 maxNum++; } //满足条件时缩小窗口 while(needMap.size()==maxNum){ //记录当前窗口 if(right-left<minLen){ start=left; minLen=right-left+1; } //缩小左边界 char cleft=sArr[left]; if(map.get(cleft).equals(needMap.get(cleft))){ maxNum--; } map.put(cleft,map.get(cleft)-1); left++; } } return minLen == Integer.MAX_VALUE ? "" : s.substring(start, start + minLen); } }
1438. 绝对差不超过限制的最长连续子数组
-
题目描述
-
题解
class Solution { public int longestSubarray(int[] nums, int limit) { //维护两个单调队列 PriorityQueue<Integer> minQueue = new PriorityQueue<>(Comparator.naturalOrder()); PriorityQueue<Integer> maxQueue = new PriorityQueue<>(Comparator.reverseOrder()); int left = 0; int right = 0; int ans = 0; while (right < nums.length && left < nums.length) { minQueue.add(nums[right]); maxQueue.add(nums[right]); //根据窗口的最小值和最大值的差更新窗口 if (maxQueue.peek() - minQueue.peek() <= limit) { ans = Math.max(ans, right - left + 1); right++; continue; } maxQueue.remove((Integer) nums[left]); minQueue.remove((Integer) nums[left]); left++; right++; } return ans; } }
567. 字符串的排列
-
题目描述
-
题解
class Solution { public boolean checkInclusion(String s1, String s2) { //异位词 int[] word = new int[26]; for(int i = 0; i < s1.length(); i++) { char c = s1.charAt(i); word[c - 'a']++; } //滑动窗口 for(int i = 0, j = 0; i < s2.length(); i++) { //消耗 word[s2.charAt(i) - 'a']--; //补充 while(word[s2.charAt(i) - 'a'] < 0) { word[s2.charAt(j) - 'a']++; j++; } //存在 if(i - j + 1 == s1.length()) { return true; } } return false; } }
1004. 最大连续1的个数 III
-
题目描述
-
题解
class Solution { public int longestOnes(int[] nums, int k) { int l = 0, r = 0, res = 0; while (r < nums.length) { if (nums[r] == 0) { if (k == 0) { while (nums[l] == 1) l++; l++; } else { k--; } } res = Math.max(res, ++r - l); } return res; } }