目录
滑动窗口
无重复字符最长子串
class Solution {
public int lengthOfLongestSubstring(String s) {
HashMap<Character,Integer>hm=new HashMap();
//字符,索引
int left=0;
int len=0;
for(int i=0;i<s.length();i++){
if(hm.containsKey(s.charAt(i))){
left=Math.max(left,hm.get(s.charAt(i))+1);
}
hm.put(s.charAt(i),i);
len=Math.max(len,i-left+1);
}
return len;
}
}
最小覆盖子串
class Solution {
public String minWindow(String s, String t) {
//我有的
Map<Character,Integer> window = new HashMap(); // 用来记录窗口中的字符和数量
Map<Character,Integer> need = new HashMap(); // 需要凑齐的字符和数量
// 构建need字符集
for (int i = 0; i < t.length(); i++) {
char needChar = t.charAt(i);
need.put(needChar,need.getOrDefault(needChar,0)+1);
}
int left = 0,right = 0,valid = 0;
// valid是用来记录窗口中满足need要求的字符的数目,比如need中要求字符a数量为2,如果window中的a字符的数量等于了2,valid就+1,反之-1
int len = Integer.MAX_VALUE; // 记录最小字串的长度
int start = 0; // 记录最小字串的起始位置
while(right < s.length()){
char addChar = s.charAt(right); // 即将要加入window的字符
window.put(addChar,window.getOrDefault(addChar,0) + 1);
right++;
// 如果加入的字符是need中要求的字符,并且数量已经达到了need要求的数量,则valid+1
// 这里和下面都有个坑,window.get(addChar)和need.get(addChar)返回的都是对象,最好用.equals()方法比较大小
if(need.containsKey(addChar) && window.get(addChar).equals(need.get(addChar))){
valid++;
}
// 当window中记录的字符和数量满足了need中要求的字符和数量,考虑缩窗口
while(valid == need.size()){
// 先判断当前的最小覆盖字串是否比之前的最小覆盖字串短
if(right - left < len){ // 注意,这里上面已经对right实施了++操作,所以这里的长度不是right - left + 1
len = right - left ;
start = left; // 如果最短,则记录下该最小覆盖字串的起始位置
}
char removeChar = s.charAt(left);
// 开始缩减窗口,left右移,如果要从window删除的字符正好是need中需要的并且,数目也等于need中需要的数目,则删减后,该该字符要求的数量显然不满足need要求的数量,所以valid要-1;
if(need.containsKey(removeChar) && window.get(removeChar).equals(need.get(removeChar))){
valid--;
}
window.put(removeChar,window.get(removeChar) - 1);
left++;
}
}
return len == Integer.MAX_VALUE?"":s.substring(start,start+len);
}
}
长度最小的子数组
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int left=0;
int sum=0;
int res=65536;
for(int i=0;i<nums.length;i++){
sum+=nums[i];
while(sum>=target){
sum-=nums[left];
res=Math.min(res,i-left+1);
left++;
}
}
if(res==65536) res=0;
return res;
}
}
水果成篮
class Solution {
public int totalFruit(int[] fruits) {
int n = fruits.length;
Map<Integer, Integer> cnt = new HashMap<Integer, Integer>();
int left = 0, ans = 0;
for (int right = 0; right < n; ++right) {
cnt.put(fruits[right], cnt.getOrDefault(fruits[right], 0) + 1);
while (cnt.size() > 2) {
cnt.put(fruits[left], cnt.get(fruits[left]) - 1);
if (cnt.get(fruits[left]) == 0) {
cnt.remove(fruits[left]);
}
++left;
}
ans = Math.max(ans, right - left + 1);
}
return ans;
}
}
在排序数组中查找元素的第一个和最后一个位置
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 [-1, -1]。
class Solution {
public int[] searchRange(int[] nums, int target) {
if(nums.length==0)return new int[]{-1,-1};
if(nums.length==1){
if(nums[0]==target)return new int[]{0,0};
if(nums[0]!=target)return new int[]{-1,-1};
}
int res[]=new int[]{-1,-1};
int left=getLeft(nums,target);
int right=getRight(nums,target);
res[0]=left;res[1]=right;
return res;
}
public int getLeft(int[] nums, int target){
int left=0;int right=nums.length-1;
while(left<=right){
int mid=left+(right-left)/2;
if(nums[mid]>=target){
right=mid-1;
}else{
left=mid+1;
}
}
if(right<nums.length-1&&nums[right+1]==target)return right+1;
return -1;
}
public int getRight(int[] nums, int target){
int left=0;int right=nums.length-1;
while(left<=right){
int mid=left+(right-left)/2;
if(nums[mid]<=target){
left=mid+1;
}else{
right=mid-1;
}
}
if(left>0&&nums[left-1]==target)return left-1;
return -1;
}
}
按奇偶顺序排序数组
class Solution {
public int[] sortArrayByParityII(int[] nums) {
int odd=1;int oven=0;
while(odd<nums.length&&oven<nums.length){
if(nums[odd]%2==0&&nums[oven]%2==1){
int tmp=nums[odd];
nums[odd]=nums[oven];
nums[oven]=tmp;
odd+=2;oven+=2;
}
else if(nums[odd]%2==0&&nums[oven]%2==0){
oven+=2;
}else if(nums[odd]%2==1&&nums[oven]%2==1){
odd+=2;
}else{
odd+=2;oven+=2;
}
}
return nums;
}
}
搜索旋转排序数组
class Solution {
public int search(int[] nums, int target) {
//旋转后的数组一定是由一个有序数组组成,一个无序数组组成
//有序数组用二分查找,无序数组继续拆分
//index旋转点右边那个点,即数组最小值
int index=find(nums);
System.out.println(index);
if(nums[0]<=target){//左边有序
if(index==0){//严格单增
return binary(nums,target,0,nums.length-1);
}
return binary(nums,target,0,index);
}else{
if(index==nums.length-1)//严格单减
{
for(int i=0;i<nums.length;i++){
if(nums[i]==target)
return i;
}
}
return binary(nums,target,index,nums.length-1);
}
}
//找到旋转点
public int find(int nums[]){
int lo = 0, hi = nums.length-1;
while(lo < hi) {
int mid = lo+(hi-lo)/2;
if(nums[mid] > nums[hi])
lo = mid+1;
else if(nums[mid] < nums[hi])
hi = mid;
else
hi--;
}
return lo;
}
//二分查找
public int binary(int nums[],int target,int left,int right){
while(left<=right){
int mid=(left+right)/2;
if(nums[mid]==target){
return mid;
}else if(nums[mid]<target){
left=mid+1;
}else if(nums[mid]>target){
right=mid-1;
}
}
return -1;
}
}
字符串相加
class Solution {
public String addStrings(String num1, String num2) {
StringBuilder sb=new StringBuilder();
int sum=0;
int i=num1.length()-1;int j=num2.length()-1;
for(;i>=0||j>=0;i--,j--){
if(i>=0){
sum+=num1.charAt(i)-'0';
}
if(j>=0){
sum+=num2.charAt(j)-'0';
}
sb.append(sum%10);
sum/=10;
}
return sb.reverse().toString();
}
}
合并区间
class Solution {
public int[][] merge(int[][] intervals) {
ArrayList<int[]> list = new ArrayList<>();
Arrays.sort(intervals, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] - o2[0];
}
});
int margin = intervals[0][1];
int start = intervals[0][0];
for (int i = 1; i < intervals.length; i++) {
if(intervals[i][0] <= margin){
//可以合并区间,但是还不能加进答案里,后面可能还有可能能合并的区间
margin = Math.max(margin, intervals[i][1]);
}
else{
//不能再合并了,将之前的答案加入list,更新新的起始区间
list.add(new int[]{start, margin});
start = intervals[i][0];
margin = intervals[i][1];
}
}
//全部都能合并 或者数目为一的情况
list.add(new int[]{start, margin});
return list.toArray(new int[list.size()][2]);
}
}
寻找两个正序数组的中位数
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
PriorityQueue<Integer> SmallRootHeap = new PriorityQueue<>(new SmallRootHeapComparator()); // 小根堆
PriorityQueue<Integer> BigRootHeap = new PriorityQueue<>(new BigRootHeapComparator()); // 大根堆
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < nums1.length; i++) {
list.add(nums1[i]);
}
for (int i = 0; i < nums2.length; i++) {
list.add(nums2[i]);
}
//维护小根堆只比大根堆最多只能多1个数
NumberOfMaintainedData(list,SmallRootHeap,BigRootHeap);
if (list.size() % 2 == 0) {
return (SmallRootHeap.poll() + BigRootHeap.poll()) / 2;
}
return BigRootHeap.poll();
}
private static void NumberOfMaintainedData(ArrayList<Integer> list, PriorityQueue<Integer> smallRootHeap,
PriorityQueue<Integer> bigRootHeap) {
for (int i = 0; i < list.size(); i++) {
if(smallRootHeap.size() == 0 && bigRootHeap.size() == 0){
bigRootHeap.add(list.get(i));
continue;
}
while(smallRootHeap.size() - bigRootHeap.size() > 1){ // 说明smallRootHeap中的数据较为多
bigRootHeap.add(smallRootHeap.poll());
}
while(bigRootHeap.size() - smallRootHeap.size() > 1){ // 说明bigRootHeap中的数据较为多
smallRootHeap.add(bigRootHeap.poll());
}
if (Math.abs(smallRootHeap.size() - bigRootHeap .size())<=1) {
bigRootHeap.add(list.get(i));
}
}
}
class SmallRootHeapComparator implements Comparator<Integer> {
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
}
class BigRootHeapComparator implements Comparator<Integer> {
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
}
}
统计素数个数
public static int erar(int n){
boolean isPrime[]=new boolean[n];//false代表素数
int count=0;
for(int i=2;i<n;i++){
if(!isPrime[i]){//素数
count++;
for(int j=2*i;j<n;j+=i){
isPrime[j]=true;
}
}
}
return count;
}
三个数最大乘积
class Solution {
public int maximumProduct(int[] nums) {
//三个最大乘积或者最大正数*最小两个负数
Arrays.sort(nums);
int n = nums.length;
return Math.max(nums[0] * nums[1] * nums[n - 1], nums[n - 3] * nums[n - 2] * nums[n - 1]);
}
}
class Solution {
public int maximumProduct(int[] nums) {
//三个最大乘积或者最大正数*最小两个负数
int max1=-65536;int max2=-65536;int max3=-65536;
int min1=65535;int min2=65535;
for(int i=0;i<nums.length;i++){
if(nums[i]<min1){
min2=min1;
min1=nums[i];
}else if(nums[i]<min2){
min2=nums[i];
}
if(nums[i]>max1){
max3=max2;
max2=max1;
max1=nums[i];
}else if(nums[i]>max2){
max3=max2;
max2=nums[i];
}else if(nums[i]>max3){
max3=nums[i];
}
}
return Math.max(max1*max2*max3,min1*min2*max1);
}
}
排序数组的两个数之和
class Solution {
public int[] twoSum(int[] numbers, int target) {
//二分查找
iint l = 0, r = numbers.size() - 1;
while(l<r){
if(numbers[l] + numbers[r] == target) return new int[2]{l, r};
else if(numbers[l] + numbers[r] > target) r--;
else l++;
}
return new int[2]{l, r};
}
}
向数组中追加K个整数
给你一个整数数组 nums 和一个整数 k 。请你向 nums 中追加 k 个 未 出现在 nums 中的、互不相同 的 正 整数,并使结果数组的元素和 最小 。
class Solution {
public long minimalKSum(int[] nums, int k) {
//数组排序,声明返回值`ans`
Arrays.sort(nums);
long ans = 0;
int l = 0, r = 1;
//若开头是1
if (nums[0] == 1) {
//如此往复,遍历数组内的相邻两个数字,并且加到`ans`上
while (k > 0 && r < nums.length) {
//没有插入的间隙
if (nums[l] == nums[r] || nums[l] + 1 == nums[r]) {
l++;
r++;
continue;
}
if (nums[r] - nums[l]- 1 <= k) {//l,r之间的间隙不够数
ans += getSum(nums[l], nums[r]);
k -= nums[r] - nums[l] - 1;
} else {//l,r之间的间隙够数
ans += getSum(nums[l], nums[l] + k + 1);
k = 0;
}
l++;
r++;
}
//若数组遍历结束后,k仍然不为0,则从最大值 + 1开始,加上后面的数字,直到k为0
int more = nums[nums.length - 1] + 1;
while (k > 0) {
ans += more;
more++;
k--;
}
return ans;
}
//若开头不是1,则先计算从1到nums[0]的数字的和并减去这些数字的数量
if (nums[0] - 1 >= k) {
ans += getSum(0, 1 + k);
return ans;
} else {
ans += getSum(0, nums[0]);
k -= nums[0] - 1;
}
//如此往复,遍历数组内的相邻两个数字,并且加到`ans`上
while (k > 0 && r < nums.length) {
if (nums[l] == nums[r] || nums[l] + 1 == nums[r]) {
l++;
r++;
continue;
}
if (nums[r] - nums[l]- 1 <= k) {
ans += getSum(nums[l], nums[r]);
k -= nums[r] - nums[l] - 1;
} else {
ans += getSum(nums[l], nums[l] + k + 1);
k = 0;
}
l++;
r++;
}
//若数组遍历结束后,k仍然不为0,则从最大值 + 1开始,加上后面的数字,直到k为0
int more = nums[nums.length - 1] + 1;
while (k > 0) {
ans += more;
more++;
k--;
}
return ans;
}
//计算nums[i]与nums[i + 1]之间的和
//1,2,3,4,5,6,7,8
long getSum(int l, int r) {
int quan = r - l - 1;
long sum = 0;
sum += (long) (l + r) * quan / 2;
return sum;
}
}
旋转数组
轮转数组
给你一个数组,将数组中的元素向右轮转 k
个位置,其中 k
是非负数。
class Solution {
public void rotate(int[] nums, int k) {
// 反转整个字符串
// 反转区间为前k的子串
// 反转区间为k到末尾的子串
//如果k大于nums.size了应该怎么办,所以其实就是右移 k % nums.size()
int n = nums.length;
k %= n;
reverse(nums, 0, n - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, n - 1);
}
private void reverse(int[] nums, int start, int end) {
for (int i = start, j = end; i < j; i++, j--) {
int temp = nums[j];
nums[j] = nums[i];
nums[i] = temp;
}
}
}
找旋转数组的最小值
class Solution {
public int findMin(int[] nums) {
/**
和 I 的做法类似, 都是二分法, 每次进入无序的那部分找出最小值
但是由于有重复值的情况, 需要加入 mid 元素等于 hi 元素的情况
此时应该将 hi 减 1 防止重复数字是最小元素
**/
int lo = 0, hi = nums.length-1;
while(lo < hi) {
int mid = lo+(hi-lo)/2;
if(nums[mid] > nums[hi])
lo = mid+1;
else if(nums[mid] < nums[hi])
hi = mid;
else
hi--;
}
return nums[lo];
}
}
快慢指针
删除有序数组中重复项
class Solution {
public int removeDuplicates(int[] nums) {
int slow=0;
for(int i=1;i<nums.length;i++){
if(nums[i]!=nums[slow]){
slow++;
nums[slow]=nums[i];
}
}
return slow+1;
}
}
移动零
class Solution {
public void moveZeroes(int[] nums) {
int left=0;int i=0;
for(i=0;i<nums.length;i++){
if(nums[i]!=0){
nums[left++]=nums[i];
}
}
for(i=left;i<nums.length;i++)nums[i]=0;
}
}
比较含退格的字符串
class Solution {
public boolean backspaceCompare(String s, String t) {
String s1=doublePointer(s);
String t1=doublePointer(t);
return s1.equals(t1);
}
public String doublePointer(String s){
int slow=0;int fast=0;
char ss[]=s.toCharArray();
for(;fast<ss.length;fast++){
if(ss[fast]!='#'){
ss[slow++]=ss[fast];
}
else if(ss[fast]=='#'&&slow>0){
slow--;
}
}
return String.valueOf(ss, 0, slow);
}
}
有序数组的平方
class Solution {
public int[] sortedSquares(int[] nums) {
int right = nums.length - 1;
int left = 0;
int[] result = new int[nums.length];
int index = result.length - 1;
while (left <= right) {
if (nums[left] * nums[left] > nums[right] * nums[right]) {
result[index--] = nums[left] * nums[left];
++left;
} else {
result[index--] = nums[right] * nums[right];
--right;
}
}
return result;
}
}
把数组所有B放左边,G放右边
一个left指针,一个right指针
right指针发现B,与left指针交换,left++
寻找中心数组的中心下标
数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和
思路:
- 遍历一遍求出总和sum
- 遍历第二遍求中心索引左半和leftSum
- 同时根据sum和leftSum 计算中心索引右半和rightSum
- 判断leftSum和rightSum是否相同
class Solution {
public int pivotIndex(int[] nums) {
int sum=0;
for(int num:nums){
sum+=num;
}
int sum2=0;
for(int i=0;i<nums.length;i++){
if(sum-sum2-nums[i]==sum2){
return i;
}
sum2+=nums[i];
}
return -1;
}
}
螺旋旋转数组
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
int m=matrix.length;int n=matrix[0].length;
int i=0;int j=0;int sum=0;
int loop=Math.min(m,n)/2;
List<Integer> list=new ArrayList();
int startX=0;int startY=0;int offset=1;
while(loop-->0){
for(j=startY;j<startY+n-offset;j++){
list.add(matrix[startX][j]);
}
for(i=startX;i<startX+m-offset;i++){
list.add(matrix[i][j]);
}
for(;j>startY;j--){
list.add(matrix[i][j]);
}
for(;i>startX;i--){
list.add(matrix[i][j]);
}
startX++;
startY++;
offset+=2;
}
if (n == m &&n % 2 ==1){
list.add(matrix[n/2][n/2]);
}
if(m<n&&m%2==1){
for(i=m/2;i<n-m/2;i++){
list.add(matrix[m/2][i]);
}
}
if(m>n&&n%2==1){
for(i=n/2;i<m-n/2;i++){
list.add(matrix[i][n/2]);
}
}
return list;
}
}