无重复字符的最长字串
3:给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
class Solution {
public int lengthOfLongestSubstring(String s) {
if(s.length()==0)
return 0;
HashMap<Character,Integer> map = new HashMap<>();
int left = 0;
int res = 0;
for(int i =0; i<s.length();i++){
char ch = s.charAt(i);
if(map.containsKey(ch)){
//更新窗口左边
left = Math.max(left,map.get(ch)+1);
}
map.put(ch,i);
res = Math.max(res,i-left+1);
}
return res;
}
}
接雨水
42:给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
双指针:
class Solution {
public int trap(int[] height) {
int left = 0;
int right = height.length-1;
int leftmax = height[left];//初始化
int rightmax = height[right];
int ans = 0;
while(left<=right){
if(leftmax<=rightmax){//如果左边最高 小于右边最高 那么右边有人兜底,可以从左往右把这次雨水算进去,否则会漏
leftmax = Math.max(height[left],leftmax);//更新左边最高
ans += leftmax - height[left];
left++;
}else{//如果左边最高 大于右边最高 那么左边有人兜底,可以从右往左把这次雨水算进去,否则会漏
rightmax = Math.max(height[right],rightmax);//更新右边最高
ans += rightmax - height[right];
right--;
}
}
return ans;
}
}
单调栈:
class Solution {
public int trap(int[] height) {
if(height ==null || height.length<=2)
return 0;
LinkedList<Integer> stack = new LinkedList<>();
int ans = 0;
for(int right =0; right<height.length;right++){
while(!stack.isEmpty()&&height[stack.peek()]<height[right]){
//找到一个低洼处 并计算
int bottom = stack.pop();
if(stack.isEmpty())
break;
int left = stack.peek();
int leftheight = height[left];
int bottomheight = height[bottom];
int rightheight = height[right];
//由于计算完低洼处,低洼处的index就已经被弹出了,所以right-left是有跨度的
ans += (Math.min(leftheight,rightheight)-bottomheight)*(right-left-1);
}
stack.push(right);
}
return ans;
}
}
盛水最多的容器
11:给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
class Solution {
public int maxArea(int[] height) {
int result = 0;
int left = 0;
int right = height.length-1;
while(left<right){
result =Math.max(result,Math.min(height[left],height[right])*(right-left));
//移动短的那边,水的容量才可能变大,移动长的不仅底部宽度变小,两边高的较小者也不会变大
if(height[left]<height[right])
left++;
else
right--;
}
return result;
}
}
滑动窗口的最大值
239:给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
利用优先队列,找到当前处理窗口之内的最大值
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int count = nums.length-k +1;
int[] result = new int[count];
PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o2[0]-o1[0];
}
});
for(int i=0;i<k;i++){
queue.offer(new int[]{nums[i],i});
}
result[0] = queue.peek()[0];
for(int i=k;i<nums.length;i++){
queue.offer(new int[]{nums[i],i});
while(queue.peek()[1]<i-k+1){
queue.poll();
}
result[i-k+1] = queue.peek()[0];
}
return result;
}
}
单调队列
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
LinkedList<Integer> queue = new LinkedList<>();
int[] ans = new int[nums.length-k+1];
for(int i=0;i<k;i++){
while(!queue.isEmpty()&&nums[i]>=nums[queue.peekLast()]){
queue.pollLast();
}
queue.offerLast(i);
}
ans[0] = nums[queue.peekFirst()];
for(int i = k;i<nums.length;i++){
while(!queue.isEmpty()&&nums[i]>=nums[queue.peekLast()]){
queue.pollLast();
}
queue.offerLast(i);
while(queue.peekFirst()<i-k+1){
queue.pollFirst();
}
ans[i-k+1] = nums[queue.peekFirst()];
}
return ans;
}
}
每日温度
739:请根据每日 气温 列表 temperatures ,请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。
单调递减栈:
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int[] result = new int[temperatures.length];
LinkedList<Integer> stack = new LinkedList<>();
for(int i=0;i<temperatures.length;i++){
while(!stack.isEmpty()&&temperatures[i]>temperatures[stack.peek()]){
int pre = stack.pop();
result[pre] = i-pre;
}
stack.push(i);
}
return result;
}
}
长度最小的子数组
209:给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int left =0;
int cursum =0 ;
int right =0;
int minlength =Integer.MAX_VALUE;
while(right<nums.length){
cursum+=nums[right];
//如果找到比target大的和,尝试缩小窗口,直到小于为止
while(cursum>=target&&left<=right){
minlength = Math.min(right-left+1,minlength);
cursum -=nums[left];
left++;
}
right++;
}
if(left==0&&cursum<target)
return 0;
return minlength;
}
}
移掉K位数字
402:给你一个以字符串表示的非负整数 num 和一个整数 k ,移除这个数中的 k 位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。
class Solution {
public String removeKdigits(String num, int k) {
StringBuilder res = new StringBuilder();
int count = num.length()-k;
LinkedList<Character> stack = new LinkedList<>();
char[] chars = num.toCharArray();
for(char ch : chars){
while(k>0&&!stack.isEmpty()&&ch<stack.peek()){
stack.pop();
k--;
}
//开头是0
if(stack.size()==0&&ch=='0'){
count--;
continue;
}
stack.push(ch);
}
System.out.println(stack);
while(!stack.isEmpty()&&count>0){
res.append(stack.pollLast());
count--;
}
return res.toString().equals("")?"0":res.toString();
}
}
最小覆盖字串
76:给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。 请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
class Solution {
public String minWindow(String s, String t) {
if(s == null||s.length()==0||t==null||t.length()==0){
return "";
}
int[] need =new int[128];
//正数代表还需要,负数和0代表以及满足数量需求
for(int i=0;i<t.length();i++){
need[t.charAt(i)]++;
}
int left = 0,right=0,count=t.length(),size = Integer.MAX_VALUE,start=0;
while(right<s.length()){
char ch = s.charAt(right);
//如果是t中还需要的元素,count减1
if(need[ch]>0){
count--;
}
need[ch]--;
//包含t的所有元素
if(count==0){
//尝试缩小左边界,将多余的元素去除,对应的need需求+1,
while(left<right&&need[s.charAt(left)]<0){
need[s.charAt(left)]++;
left++;
}
//能缩小的最终状态,查看窗口大小,最左端肯定是t中元素,因为need[s.charAt(left)]==0,往右移动肯定需要+1
if(right-left+1<size){
size = right-left+1;
start = left;
}
//左端向右移动,对应需求量+1
need[s.charAt(left)]++;
left++;
count++;
}
right++;
}
return size == Integer.MAX_VALUE?"":s.substring(start,start+size);
}
}
得到连续 K 个 1 的最少相邻交换次数
1703:前缀和+滑动窗口
给你一个整数数组 nums 和一个整数 k 。 nums 仅包含 0 和 1 。每一次移动,你可以选择 相邻 两个数字并将它们交换。请你返回使 nums 中包含 k 个 连续 1 的 最少 交换次数。
class Solution {
public int minMoves(int[] nums, int k) {
int n = nums.length;
int ans = Integer.MAX_VALUE;
List<Integer> list = new ArrayList<>();
// 1. 找到每个1的位置,并将其nums[i]-i的存入,i<-[0,1的总数],方便后续前缀和计算
for(int i=0;i<n;i++){
if(nums[i]==1)
list.add(i-list.size());
}
// sum[i] --- 前i个的和 ai-i
int[] sum = new int[list.size()+1];
for(int i=1;i <= list.size();i++){
sum[i] = sum[i-1] + list.get(i-1);
}
// 滑动大小为 k 的窗口,计算每个窗口的最优值
for(int i=k;i<=list.size();i++){ // i最好从k开始,代表在sum中的下标
int left = i-k+1;
int right = i;
int mid = (left+right)/2;
int x = list.get(mid-1);
int leftSum = (mid-left)*x-(sum[mid-1]-sum[left-1]);
int rightSum = sum[right]-sum[mid] -(right-mid)*x;
ans = Math.min(ans,leftSum+rightSum);
}
return ans;
}
}
有效三角形个数
611:给定一个包含非负整数的数组 nums,返回其中可以组成三角形三条边的三元组个数。
class Solution {
public int triangleNumber(int[] nums) {
Arrays.sort(nums);
int length = nums.length;
int left = 0, right = 0;
int res = 0;
for(int i = length-1;i >= 2; i--){
left = 0;
right = i-1;
while(left<right){
if(nums[left]+nums[right]>nums[i]){
res += right - left;
right--;
}else{
left++;
}
}
}
return res;
}
}