1.滑动窗口算法:最小覆盖子串
class Solution {
public String minWindow(String s, String t) {
//使用滑动窗口算法
//base case
if(s==null || s.length()==0){
return "";
}
//使用needs记录target字符串中的字符以及个数
Map<Character,Integer> needs=new HashMap<>();
for(char ch:t.toCharArray()){
needs.put(ch,needs.getOrDefault(ch,0)+1);
}
//定义一个window,用于后面记录窗口
Map<Character,Integer> window=new HashMap<>();
//使用双指针用于窗口的滑动,定义两个指针
int left=0,right=0;
//定义最小长度值minLen,用于比较得到最小长度子串
int minLen=Integer.MAX_VALUE;
//定义start,用于记录最小子串的开始位置
int start=-1;
//定义一个变量valid,记录已经满足要求的有效字符的个数
int valid=0;
//开始滑动窗口
while(right<s.length()){
char c=s.charAt(right);
right++;
if(needs.containsKey(c)){
window.put(c,window.getOrDefault(c,0)+1);
if(needs.get(c).equals(window.get(c))){
valid++;
}
}
//将窗口中包含target中所有字符时,进行窗口收缩
while(valid==needs.size()){
if((right-left)<minLen){
start=left;
minLen=right-left;
}
//缩小窗口
char d=s.charAt(left);
left++;
if(needs.containsKey(d)){
if(needs.get(d).equals(window.get(d))){
valid--;
}
window.put(d,window.getOrDefault(d,0)-1);
}
}
}
//最后返回最小子串
return minLen==Integer.MAX_VALUE?"":s.substring(start,start+minLen);
}
}
2.滑动窗口算法:字符串的排列
class Solution {
public boolean checkInclusion(String s1, String s2) {
//使用滑动窗口
//使用needs记录s2中字符以及个数
Map<Character,Integer> needs=new HashMap<>();
for(char ch:s1.toCharArray()){
needs.put(ch,needs.getOrDefault(ch,0)+1);
}
//使用window记录窗口中字符以及个数
Map<Character,Integer> window=new HashMap<>();
//定义双指针
int left=0,right=0;
//有效字符个数
int valid=0;
//开始滑动窗口
while(right<s2.length()){
char c=s2.charAt(right);
right++;
if(needs.containsKey(c)){
window.put(c,window.getOrDefault(c,0)+1);
if(needs.get(c).equals(window.get(c))){
valid++;
}
}
while(valid==needs.size()){
if((right-left)==s1.length()){//当有效字符数和子串长度这两个条件都满足时,可以确定是子串的排列
return true;
}
char d=s2.charAt(left);
left++;
if(needs.containsKey(d)){
if(needs.get(d).equals(window.get(d))){
valid--;
}
window.put(d,window.getOrDefault(d,0)-1);
}
}
}
return false;
}
}
3.滑动窗口算法:找到字符串中所有字母异位词
class Solution {
public List<Integer> findAnagrams(String s, String p) {
//使用滑动窗口
List<Integer> res=new ArrayList<>();
Map<Character,Integer> needs=new HashMap<>();
for(char ch:p.toCharArray()){
needs.put(ch,needs.getOrDefault(ch,0)+1);
}
Map<Character,Integer> window=new HashMap<>();
int left=0,right=0;
int valid=0;
while(right<s.length()){
char c=s.charAt(right);
right++;
if(needs.containsKey(c)){
window.put(c,window.getOrDefault(c,0)+1);
if(needs.get(c).equals(window.get(c))){
valid++;
}
}
while(valid==needs.size()){
if((right-left)==p.length()){
res.add(left);
}
char d=s.charAt(left);
left++;
if(needs.containsKey(d)){
if(needs.get(d).equals(window.get(d))){
valid--;
}
window.put(d,window.getOrDefault(d,0)-1);
}
}
}
return res;
}
}
4.滑动窗口算法:无重复字符的最长子串
class Solution {
public int lengthOfLongestSubstring(String s) {
if(s.length()==0) return 0;
//滑动窗口,当遇到重复字符时,右移left
int maxLen=Integer.MIN_VALUE;
int left=0,right=0;
Map<Character,Integer> window=new HashMap<>();
while(right<s.length()){
char c=s.charAt(right);
right++;
window.put(c,window.getOrDefault(c,0)+1);
//当窗口中元素出现重复时,开始收缩左窗口
while(window.get(c)>1){
char d=s.charAt(left);
left++;
window.put(d,window.getOrDefault(d,0)-1);
}
//更新放到外面,因为要等左边界收缩完事之后再计算窗口长度
maxLen=Math.max(maxLen,right-left);
}
return maxLen;
}
}
5.数组操作:O(1)时间插入、删除和获取元素
class RandomizedSet {
private HashMap<Integer,Integer> map;//存储值到索引的映射
private ArrayList<Integer> list;//存储值
private Random rand;//产生随机数
/** Initialize your data structure here. */
public RandomizedSet() {
map=new HashMap<>();
list=new ArrayList<>();
rand=new Random();
}
/** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
public boolean insert(int val) {
if(!map.containsKey(val)){
map.put(val,list.size());
list.add(val);
return true;
}else{
return false;
}
}
/** Removes a value from the set. Returns true if the set contained the specified element. */
public boolean remove(int val) {
//首先找到val的位置,然后和末尾元素进行交换,交换之后再删除,这样时间复杂度就是O(1)
if(map.containsKey(val)){
//拿到val的索引
int index=map.get(val);
//交换
int temp=list.get(list.size()-1);
list.set(list.size()-1,val);
list.set(index,temp);
//删除末尾元素
list.remove(list.get(list.size()-1));
//别忘记map的操作!!!!
map.put(temp,index);
map.remove(val);
return true;
}else{
return false;
}
}
/** Get a random element from the set. */
public int getRandom() {
return list.get(rand.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();
*/
6.数组操作:黑名单中的随机数
是通过将黑名单对应位置的元素换为末尾元素,然后减小原数组长度,最后随机返回的数值就不包含黑名单中的数。
class Solution {
int sz;//去除黑名单之后的数组长度
Map<Integer,Integer> mapping;
Random rand;
public Solution(int N, int[] blacklist) {
sz=N-blacklist.length;
mapping=new HashMap<>();
rand=new Random();
//先存放一个初始值,为了后面进行操作
for(int b:blacklist){
mapping.put(b,666);
}
int last=N-1;
for(int b:blacklist){
if(sz<=b) continue;
while(mapping.containsKey(last)){
last--;
}
mapping.put(b,last);//就是把该位置上的黑名单的值换成末尾的值
last--;
}
}
public int pick() {
int index=rand.nextInt(sz);
//如果mapping中含有该索引,说明该位置的值被换了,所以应该返回mapping中的值
if(mapping.containsKey(index)){
return mapping.get(index);
}else{
return index;
}
}
}
/**
* Your Solution object will be instantiated and called as such:
* Solution obj = new Solution(N, blacklist);
* int param_1 = obj.pick();
*/
7.单调栈:去除重复字母
class Solution {
public String removeDuplicateLetters(String s) {
//目标:(1)去重(2)保持相对顺序(3)字典序最小
//单调栈思想
Deque<Character> stack=new LinkedList<>();
boolean[] instack=new boolean[256];//256是ASCII字符个数
int[] count=new int[256];//记录字符个数
for(char c:s.toCharArray()){
count[c]++;
}
for(char c:s.toCharArray()){
count[c]--;//遍历一个字符,就把这个元素的个数减1
if(instack[c]) continue;
//当待添加元素小于栈顶元素时
while(!stack.isEmpty() && stack.peek()>c){
if(count[stack.peek()]==0) break;//后面没有这个字符了,就不能pop出去
instack[stack.pop()]=false;
}
stack.push(c);
instack[c]=true;
}
StringBuilder sb=new StringBuilder();
while(!stack.isEmpty()){
sb.append(stack.pop());
}
return sb.reverse().toString();//由于出栈顺序使反的,所以需要reverse一下
}
}