leetcode

数组&字符串
9 回文数

26 从排序数组中删除重复项

27 移除元素
#80 从排序数组中删除重复项II
#旋转数组

存在重复元素
136 只出现一次的数字 位运算

137 只出现一次的数字II

260 只出现一次的数字III 多个出现一次
两个数组的交集 II
加一
移动零

有效的数独
#旋转图像
反转字符串
#反转整数
#字符串中的第一个唯一字符
有效的字母异位词
验证回文字符串
字符串转整数 (atoi)
实现strStr()
数数并说
#最长公共前缀
#1 两数之和
#15 三数之和 双指针
#16 最接近的三数之和 双指针
#18 四数之和 双指针+双循环

#11盛最多水的容器 双指针

#31 下一个排列 双指针
88 合并两个有序数组 逆向 双指针
#四数相加
#矩阵置零
#字谜分组
滑动窗口
643 子数组最大平均数 滑动窗口
#3 无重复字符的最长子串 滑动窗口

#209 长度最小的子数组 滑动窗口

#1695 删除子数组的最大得分 滑动窗口
#438 找到字符串中所有字母异位词
#567 字符串的排列
#1004 最大连续1的个数III
#1208 尽可能使字符串相等
#1423 可获得的最大点数
459 重复的子字符串
#5 最长回文子串 中心扩散
贪心
409 最长回文串 贪心,构建
605 种花问题 贪心
455 分发饼干 贪心
#179 最大数 贪心
lambda表达式
#134 加油站 贪心
#406 根据身高重建队列 贪心 排序
#1996 游戏中的弱角色 贪心 排序
#字符串转换整数
#334 递增的三元子序列
##寻找两个有序数组的中位数

##42 接雨水 双指针
链表
#237 删除链表中的节点 复制值+跳过
203 移除链表元素
83 删除链表重复数字的节点 双指针

#82 删除排序链表中的重复元素II 双指针

#19 删除链表的倒数第N个节点 双指针
#206 反转链表
#92 反转链表II 部分反转+拼接
21 合并两个有序链表

#147 链表插入排序
#148 排序链表 快慢指针 + 归并排序

#234 回文链表
#141 环形链表 双指针追及

#142 环形链表II
两数相加
#328 奇偶链表
#160 相交链表
#重排链表
#分隔链表
#对链表进行插入排序
树&图
递归法
三种遍历二叉树——递归
三种遍历二叉树——非递归
144 二叉树的前序遍历-根左右
#94 二叉树的中序遍历-左根右

#145 二叉树的后序遍历
104 二叉树的最大深度
100 相同的树
55 平衡二叉树
#98 验证二叉搜索树
52 展平二叉搜索树 剑指II
101 对称二叉树
226 二叉树的镜像 剑指27 lc
#102 二叉树的层次遍历 BFS——自顶向下
BFS:

107 二叉树的层序遍历2 ——自下向上

108 将有序数组转换为二叉搜索树

#先序遍历构造二叉树
重建二叉树 剑指7 根据前序和中序构建二叉树
根据中序和后序构建二叉树 106
#103 二叉树的锯齿形层次遍历 BFS
#199 二叉树的右视图 BFS

#515 在每个树行中找最大值 BFS
637 二叉树的层平均值 BFS
814 二叉树剪枝
每个节点的右向指针
二叉搜索树中第K小的元素

岛屿的个数
课程表
排序和搜索
快速排序
#33 搜索旋转排序数组 二分

#34 在排序数组中查找元素的第一个和最后一个位置

35 搜索插入位置
#第一个错误的版本
#找出一个无序数组的中位数
颜色分类
#前K个高频元素
#数组中的第K个最大元素
#寻找峰值

合并区间

搜索二维矩阵 II
跳跃游戏
动态规划
70 爬楼梯
121 买卖股票的最佳时机
动态规划
优化dp空间
53 最大子序和

#55 跳跃游戏 倒推!!
动态规划
思路2:贪心 逆序倒推
贪心 正序推
#45 跳跃游戏II 贪心
#122 买卖股票的最佳时机II 二维dp
方法1:动态规划
方法2:贪心
#123 买卖股票的最佳时机III
方法1:三维dp
方法2:节省空间
188 最多交易K次
#309 最佳买卖股票时机含冷冻期 二维/三维

背包问题
01背包
完全背包

多重背包
#198 打家劫舍
#213 打家劫舍II
#337 打家劫舍III 树+dfs+dp
不同路径
#322 零钱兑换
常规动态规划-完全背包思路
#518 零钱兑换2
思路1:类似组合总和39题,dfs 超时
思路2:dp 类似70题爬楼梯
#983 最低票价 动态规划
最长上升子序列
设计问题
Shuffle an Array
#最小栈
#二叉树的序列化与反序列化
#常数时间插入、删除和获取随机元素
数学
Fizz Buzz
计数质数
#3的幂
#罗马数字转整数
快乐数
#阶乘后的零
Excel表列序号
其他
#位1的个数
汉明距离
颠倒二进制位
杨辉三角
20 有效的括号

缺失数字
#朋友圈
1447最简分数
回溯算法
题型一:排列、组合、子集相关问题46 47 39 40 77 78 90 60 93
#46 全排列 dfs 回溯+used数组
#47 全排列II dfs
#39 组合总和 回溯+剪枝+begin
#40 组合总和II 回溯+剪枝+begin
#77 组合 回溯+begin
#78 子集
#90 子集II
题型二:Flood Fill 200 130 79 733
题型三:字符串中的回溯问题 17 22 784
#17 电话号码
#784 字母大小写全排列
#22 括号生成 回溯+剪枝
题型四:游戏问题 51 37 488 529[困难]
DFS
排列组合问题见回溯
二叉树的DFS:
112 路径总和
#113路径总和II 回溯
二叉树的BFS:
网格的DFS
#200 岛屿数量
#695 岛屿最大面积
##827 最大人工岛
463 岛屿的周长 1.数学方法 2.dfs
BFS:
层序遍历树
最短路径
网格结构的层序遍历 BFS:
#1162 离开陆地的最远距离
#542 01矩阵
#994 腐烂的橘子
#310 最小高度树
#子集
单词搜索
极小化极大
1.nim游戏
数组&字符串
9 回文数
public boolean isPalindrome(int x) {
if(x < 0) return false;
String str = Integer.toString(x);
char[] charArr = str.toCharArray();
for(int i = 0; i < charArr.length/2; i++){
if(charArr[i] != charArr[charArr.length-1-i]){
return false;
}
}
return true;
}
方法2:
public boolean isPalindrome(int x) {
if(x<0) return false;
if(x % 10 == 0 && x != 0) return false; //末位是0,不可能是回文数
int halfReserve = 0;
while((x - halfReserve) > 0){
halfReserve = halfReserve*10 + x%10;
x = x/10;
}
System.out.println(halfReserve+" "+ x);
if(x == halfReserve) return true;
if(x == halfReserve/10) return true;//121
return false;
}

26 从排序数组中删除重复项
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

很简单,直接上代码:
class Solution {
public int removeDuplicates(int[] nums) {
int index=0;
for(int i =1 ; i < nums.length ; i++){
if ( nums[i] != nums[index]){
index ++;
nums[index] = nums[i];
}
}
return ++index;
}
}

27 移除元素
原地移除所有=val的值,返回新数组的长度[3,2,2,3] 3 --> [2,2]
public int removeElement(int[] nums, int val) {
if(nums == null || nums.length == 0) return 0;
int index = 0;
for(int i = 0; i < nums.length; i++){
if(nums[i] != val){
nums[index] = nums[i];
index++;
}
}
return index;
}

#80 从排序数组中删除重复项II
/*

  • 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
    */
    扩展 最多出现n次 repeat
    public int removeDuplicatesII(int[] nums) {
    if(nums == null || nums.length == 0) return 0;
    int repeat = 2;
    int slow = repeat;
    for(int fast = repeat; fast < nums.length; fast++){
    if(nums[fast] != nums[slow-repeat]){
    nums[slow] = nums[fast];
    slow++;
    }
    }
    return slow;
    }

public class DeleteRepeatedItemsII {
public static void main(String[] args) {
DeleteRepeatedItemsII d = new DeleteRepeatedItemsII();
int[] nums = {0,0,1,1,1,1,2,3,3,4};
int length = d.removeDuplicates(nums);
for(int i = 0; i < length; i++) {
System.out.println(nums[i]);
}
}
public int removeDuplicates(int[] nums) {
if(nums.length == 0 || nums == null) return 0;
int i = 1;

	for(int j = 2; j < nums.length; j++) {
		
		if(nums[j] != nums[i-1]) {
				i += 1;
    			nums[i] = nums[j];	
		}
	}
	return i+1;
}

}

#旋转数组
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
先将数组的前len-k个元素反转,后k个元素反转,然后再将整个数组反转。注意先让k对len取模。
class Solution {
public void rotate(int[] nums, int k) {
int len = nums.length;
k = k%len;
if(len<2||k==0)return;
swap(nums, 0, len-k-1);
swap(nums, len-k, len-1);
swap(nums, 0, len-1);
}
public void swap(int[] nums, int left, int right){
while(left<right){
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
left++;
right–;
}
}
}

存在重复元素
给定一个整数数组,判断是否存在重复元素。
如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。

方案一:利用HashSet。
class Solution {
public boolean containsDuplicate(int[] nums) {
int len = nums.length;
if(len<2)return false;
HashSet hashSet = new HashSet<>();
for(int num:nums)
if(!hashSet.add(num))return true;
return false;
}
}
方案二:利用Arrays.sort()方法先对数组进行排序,然后判断。
class Solution {
public boolean containsDuplicate(int[] nums) {
int len = nums.length;
if(len<2)return false;
Arrays.sort(nums);
for(int i=0;i+1<len;i++){
if(nums[i]==nums[i+1])return true;
}
return false;
}
}
136 只出现一次的数字 位运算
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

异或,出现偶数次的元素会被清零,于是留下来的就是那个只出现一次的数字。
public int singleNumber(int[] nums) {
int res = 0;
for(int i = 0; i < nums.length; i++){
res = res ^ nums[i];
}
return res;
}

137 只出现一次的数字II
给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。
方法1:map
执行用时:5 ms, 在所有 Java 提交中击败了30.98%的用户
内存消耗:41 MB, 在所有 Java 提交中击败了18.70%的用户
public int singleNumberII(int[] nums) {
Map<Integer,Integer> map = new HashMap<Integer, Integer>();
int res = 0;
for(int i = 0; i < nums.length; i++){
if(!map.containsKey(nums[i])){
map.put(nums[i],1);

    }else{
        Integer v = map.get(nums[i]);
        map.put(nums[i],v+1);//对于已有的key,新的value会覆盖之前的
    }
}
//用迭代器遍历map
Set entrySet = map.entrySet();
Iterator<Map.Entry> it = entrySet.iterator();
while(it.hasNext()){
    Map.Entry entry = it.next();
    if(entry.getValue().equals(1)){
        res = (Integer)entry.getKey();
    }
}
return res;

}

方法2:位运算

260 只出现一次的数字III 多个出现一次
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。
public int singleNumberIII(int[] nums) {
Map<Integer,Integer> map = new HashMap<Integer, Integer>();
List reslist = new ArrayList();
for(int i = 0; i < nums.length; i++){
if(!map.containsKey(nums[i])){
map.put(nums[i],1);

    }else{
        Integer v = map.get(nums[i]);
        map.put(nums[i],v+1);
    }
}
Set entrySet = map.entrySet();
Iterator<Map.Entry> it = entrySet.iterator();
while(it.hasNext()){
    Map.Entry entry = it.next();
    if(entry.getValue().equals(1)){
        reslist.add((Integer)entry.getKey());
    }
}
int[] resarr = new int[reslist.size()];
for(int i = 0 ; i < reslist.size(); i++){
    resarr[i] = reslist.get(i);
}
return resarr;

}
位运算:

两个数组的交集 II
给定两个数组,写一个方法来计算它们的交集。
先排序,再比较。
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
int len1 = nums1.length;
int len2 = nums2.length;
if(len10||len20)return new int[0];
Arrays.sort(nums1);
Arrays.sort(nums2);
List list = new ArrayList<>();
int i=0;
int j=0;
while(i<len1&&j<len2){
if(nums1[i]==nums2[j]){
list.add(nums1[i]);
i++;
j++;
continue;
}
else if(nums1[i]>nums2[j])j++;
else i++;
}
int[] res = new int[list.size()];
for(int k=0;k<list.size();k++)res[k] = list.get(k);
return res;
}
}
加一
给定一个非负整数组成的非空数组,在该数的基础上加一,返回一个新的数组。
最高位数字存放在数组的首位, 数组中每个元素只存储一个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。

注意进位的处理。
class Solution {
public int[] plusOne(int[] digits) {
int len = digits.length;
if(len==0)return new int[0];
int[] res = new int[len+1];
res[len] = (digits[len-1]+1)%10;
int carry = (digits[len-1]+1)/10;
for(int i=len-2;i>=0;i–){
res[i+1] = (digits[i]+carry)%10;
carry = (digits[i]+carry)/10;
}
res[0] = carry;
if(carry!=0)return res;
int[] res1 = new int[len];
System.arraycopy(res, 1, res1, 0, len);
return res1;
}
}
移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

class Solution {
public void moveZeroes(int[] nums) {
int len = nums.length;
if(len<2)return;
int index = 0;
for(int i=0;i<len;i++){
if(nums[i]!=0)nums[index++] = nums[i];
}
for(int i=index;i<len;i++)nums[i] = 0;
}
}

有效的数独
判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

使用一个二维数组seat保存所有数字的位置,遍历输入的数独,遇见数字则先判断该位置是否与seat中相同数字的位置冲突,若冲突则返回false,否则将当前数字的位置保存至seat中。
class Solution {
public boolean isValidSudoku(char[][] board) {
boolean[][] seat=new boolean[9][27];
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
if(board[i][j]==’.’)continue;
int num=board[i][j]-‘0’;
int grid=(i/3)*3+(j/3);
if(seat[num-1][i]||seat[num-1][j+9]||seat[num-1][grid+18])return false;
else{
seat[num-1][i]=true;
seat[num-1][j+9]=true;
seat[num-1][grid+18]=true;
}
}
}
return true;
}
}
#旋转图像
给定一个 n × n 的二维矩阵表示一个图像。
将图像顺时针旋转 90 度。
你必须在原地旋转图像。

先按主对角线翻转,再按垂直对称轴翻转。
class Solution {
public void rotate(int[][] matrix) {
int n = matrix.length;
for(int i=0;i<n;i++)
for(int j=0;j<i;j++)
swap(matrix, i, j);
for(int i=0;i<n;i++)swap(matrix, i);
}
public void swap(int[][] matrix, int i, int j){
int temp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = temp;
}
public void swap(int [][] matrix, int i){
int left = 0;
int right = matrix[i].length-1;
while(left<right){
int temp = matrix[i][left];
matrix[i][left] = matrix[i][right];
matrix[i][right] = temp;
left++;
right–;
}
}
}
反转字符串
编写一个函数,其作用是将输入的字符串反转过来。

注意不要想得太复杂了就好。
class Solution {
public String reverseString(String s) {
int len = s.length();
if(len<2)return s;
char[] chars = s.toCharArray();
int left = 0;
int right = len-1;
while(left<right){
char temp = chars[left];
chars[left] = chars[right];
chars[right] = temp;
left++;
right–;
}
return new String(chars);
}
}
#反转整数
给定一个 32 位有符号整数,将整数中的数字进行反转。
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231,  231 − 1]。根据这个假设,如果反转后的整数溢出,则返回 0。

不能使用long!!!
class Solution {
public int reverse(int x) {
int res = 0;
while (x != 0){
int temp = res;
res = res * 10 + x % 10;
if (res/10 != temp) return 0;//校验溢出
x /= 10;
}
return res;
}
}
#字符串中的第一个唯一字符
给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回-1。

方案一:利用HashMap存下每个字符和其索引的键值对,若该字符重复出现则将HashMap中的key为该字符的value设为-1,最后遍历HashMap中所有的value,找到最小值。
class Solution {
public int firstUniqChar(String s) {
int len = s.length();
if(len0)return -1;
HashMap<Character, Integer> map = new HashMap<>();
for(int i=0;i<len;i++){
char curr = s.charAt(i);
if(map.containsKey(curr))map.put(curr,-1);
else map.put(curr,i);
}
int index = Integer.MAX_VALUE;
Iterator it = map.values().iterator();
while(it.hasNext()){
int i = (int)it.next();
if(i!=-1)index = Math.min(index,i);
}
return index
Integer.MAX_VALUE?-1:index;
}
}

方案二:使用一个256大小的数组来存放每个字符出现的次数,然后再遍历字符串中的每个字符,如果该字符出现的次数为1则返回。
class Solution {
public int firstUniqChar(String s) {
int len = s.length();
if(len==0)return -1;
int[] charTable = new int[256];
for(int i=0;i<len;i++){
int index = s.charAt(i)-‘0’;
charTable[index]++;
}
for(int i=0;i<len;i++){
int index = s.charAt(i)-‘0’;
if(charTable[index]==1)return i;
}
return -1;
}
}
有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的一个字母异位词。
如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?

class Solution {
public boolean isAnagram(String s, String t) {
int lens = s.length();
int lent = t.length();
if(lens!=lent)return false;
HashMap<Character, Integer> map = new HashMap<>();
for(int i=0;i<lens;i++){
char curr = s.charAt(i);
if(map.containsKey(curr))map.put(curr, map.get(curr)+1);
else map.put(curr, 1);
}
for(int i=0;i<lent;i++){
char curr = t.charAt(i);
if(!map.containsKey(curr)||map.get(curr)==0)return false;
map.put(curr, map.get(curr)-1);
}
Iterator it = map.values().iterator();
while(it.hasNext()){
if((int)it.next()!=0)return false;
}
return true;
}
}
验证回文字符串
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。

注意小写字符比大写字符大32!
class Solution {
public boolean isPalindrome(String s) {
int len = s.length();
if(len<2)return true;
int left = 0;
int right = len-1;
while(left<right){
while(left<right&&letter(s.charAt(left))==null)left++;
while(left<right&&letter(s.charAt(right))==null)right–;
if(left<right&&letter(s.charAt(left))!=letter(s.charAt(right)))return false;
left++;
right–;
}
return true;
}
public Character letter(char c){
if(c>=‘a’&&c<=‘z’||c>=‘0’&&c<=‘9’)return c;
else if(c>=‘A’&&c<=‘Z’)return (char)(c+32);
else return null;
}
}
字符串转整数 (atoi)
实现 atoi,将字符串转为整数。
在找到第一个非空字符之前,需要移除掉字符串中的空格字符。如果第一个非空字符是正号或负号,选取该符号,并将其与后面尽可能多的连续的数字组合起来,这部分字符即为整数的值。如果第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
字符串可以在形成整数的字符后面包括多余的字符,这些字符可以被忽略,它们对于函数没有影响。
当字符串中的第一个非空字符序列不是个有效的整数;或字符串为空;或字符串仅包含空白字符时,则不进行转换。
若函数不能执行有效的转换,返回 0。
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。如果数值超过可表示的范围,则返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。

public class Solution {
public int myAtoi(String str) {
str = str.trim();
if (str.isEmpty()) return 0;
int sign = 1, base = 0, i = 0, n = str.length();
if (str.charAt(i) == ‘+’ || str.charAt(i) == ‘-’) {
sign = (str.charAt(i++) == ‘+’) ? 1 : -1;
}
while (i < n && str.charAt(i) >= ‘0’ && str.charAt(i) <= ‘9’) {
if (base > Integer.MAX_VALUE / 10 || (base == Integer.MAX_VALUE / 10 && str.charAt(i) - ‘0’ > 7)) {
return (sign == 1) ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
base = 10 * base + (str.charAt(i++) - ‘0’);
}
return base * sign;
}
}
实现strStr()
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回-1。如果needle是空字符串,则返回0 。

class Solution {
public int strStr(String haystack, String needle) {
int len1 = haystack.length();
int len2 = needle.length();
if(len2==0)return 0;
if(len1<len2)return -1;
int i = 0;
int j = 0;
while(i<len1&&j<len2){
if(haystack.charAt(i)needle.charAt(j)){
j++;
i++;
}else{
i = i-j+1;
j = 0;
}
}
if(j
len2)return i-j;
else return -1;
}
}
数数并说
报数序列是指一个整数序列,按照其中的整数的顺序进行报数,得到下一个数。其前五项如下:

  1. 1
  2. 11
  3. 21
  4. 1211
  5. 111221
    1 被读作 “one 1” (“一个一”) , 即 11。
    11 被读作 “two 1s” (“两个一”), 即 21。
    21 被读作 “one 2”, “one 1” (“一个二” , “一个一”) , 即 1211。

给定一个正整数 n ,输出报数序列的第 n 项。
注意:整数顺序将表示为一个字符串

class Solution {
public String countAndSay(int n) {
String countSequence=“1”;
if(n==1)return countSequence;
for(int i=2;i<=n;i++){
StringBuffer sb=new StringBuffer();
for(int j=0;j<countSequence.length()😉{
char current=countSequence.charAt(j);
int num=0;
while(j<countSequence.length()&&countSequence.charAt(j)==current){
num++;
j++;
}
sb.append(String.valueOf(num));
sb.append(current);
}
countSequence=sb.toString();
}
return countSequence;
}
}
#最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。

使用startsWith()方法判断一个字符串是不是另一个字符串的前缀。
class Solution {
public String longestCommonPrefix(String[] strs) {
int count=strs.length;
String prefix="";
if(count!=0){
prefix=strs[0];
}
for(int i=0;i<count;i++){
while(!strs[i].startsWith(prefix)){
prefix=prefix.substring(0,prefix.length()-1);
}
}
return prefix;
}
}
#1 两数之和
给定一个整数数组和一个目标值,找出数组中和为目标值的两个数,返回它们的坐标。
你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。

用HashMap!!!
class Solution {
public int[] twoSum(int[] nums, int target) {
int len = nums.length;
if(len<2)return null;
HashMap<Integer, Integer> map = new HashMap<>();
for(int i=0;i<len;i++){
if(map.containsKey(target-nums[i])){
return new int[]{i, map.get(target-nums[i])};
}
map.put(nums[i], i);
}
return null;
}
}
#15 三数之和 双指针
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。

先对数组进行排序,然后遍历数组中小于等于0的部分,每次将当前元素设为三数中的其中一个,然后对数组剩余的部分运用两数之和的算法。
public List<List> threeSum(int[] nums) {
List<List> res = new ArrayList<List>();
if(nums == null || nums.length <= 2) 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 target = -nums[i];
int left = i+1;
int right = nums.length-1;
while(left < right){
if(nums[left] + nums[right] == target){
List sublist = new ArrayList();
sublist = Arrays.asList(nums[i],nums[left],nums[right]);
res.add(sublist);
left++;
right–;
while (nums[left] == nums[left-1] && left < right) left++;
while (nums[right] == nums[right+1] && left < right) right–;
}else if(nums[left] + nums[right] < target){
left++;
}else{
right–;
}
}
}
return res;
}
class Solution {
public List<List> threeSum(int[] nums) {
List<List> res = new ArrayList<>();
int len = nums.length;
if(len<3)return res;
Arrays.sort(nums);
int i = 0;
while(i<len&&nums[i]<=0){
int target = 0-nums[i];
int left = i+1;
int right = len-1;
while(left<right){
if(nums[left]+nums[right]==target){
res.add(Arrays.asList(nums[i], nums[left], nums[right]));
int l = nums[left];
while(left<right&&nums[left]==l)left++;
int r = nums[right];
while(left<right&&nums[right]==r)right–;
}else if(nums[left]+nums[right]>target)right–;
else left++;
}
int temp = nums[i];
while(i<len&&nums[i]==temp)i++;
}
return res;
}
}

#16 最接近的三数之和 双指针
三数之和最接近target,返回三数之和。假定存在一个解
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int res = nums[0]+nums[1]+nums[2];
for(int i = 0; i < nums.length-2; i++){
int left = i+1;
int right = nums.length-1;
while (left < right){
int sum = nums[i]+nums[left]+nums[right];
int dev = Math.abs(sum-target);

        if(Math.abs(res-target) > dev){
            res = sum;
        }
        if(sum < target){
            left++;
        }else{
            right--;
        }
    }
}
return res;

}

#18 四数之和 双指针+双循环
/*
给定一个包含 n 个整数的数组 nums 和一个目标值 target,
判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?
找出所有满足条件且不重复的四元组。

给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。

满足要求的四元组集合为:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]

使用双循环固定两个数,用双指针找另外两个数,通过比较与target 的大小(3数之和),移动指针。
??如何跳过重复值
方法一:使用set:不能存重复值
方法二:当有重复值,改变索引(例如sum3)
*/
public class Sum4 {
public List<List> fourSum(int[] nums, int target) {
List<List> resList = new ArrayList<List>();
Arrays.sort(nums);
Set<List> set = new HashSet<List>();
int l = nums.length;
for(int i = 0; i < l-3; i++) {
if(nums[i] > target && target > 0) break;
for(int j = i+1; j < l-2; j++) {
int start = j+1;
int end = l-1;
while(start < end) {
int sum = nums[i]+nums[j]+nums[start]+nums[end];
if(sum == target) {
set.add(Arrays.asList(nums[i],nums[j],nums[start],nums[end]));
start++;
end–;
}else if(sum < target) {
start++;
}else {
end–;
}
}
}
}
for(List list : set) {
resList.add(list);
}
return resList;
}
}

#11盛最多水的容器 双指针

public int maxArea(int[] height) {
if(height == null || height.length <= 1) return 0;
int left = 0;
int right = height.length-1;
int res = 0;

while(left < right){
    res = Math.max(res, (right-left)*Math.min(height[right],height[left]));
    if(height[left] < height[right]){
        left++;
    }else{
        right--;
    }
}
return res;

}

#31 下一个排列 双指针
必须 原地 修改,只允许使用额外常数空间。
思路:
从右往左遍历,找到num[i-1]<nums[i]的位置i-1,然后遍历第二次,找到比num[i-1]数字大的数,二者交换,然后对i-1之后的元素进行翻转
时间复杂度:O(N)O(N),其中 NN 为给定序列的长度。我们至多只需要扫描两次序列,以及进行一次反转操作。
空间复杂度:O(1)O(1),只需要常数的空间存放若干变量

public void nextPermutation(int[] nums) {
int left = -1;
int right = 0;
for(int i = nums.length - 1; i > 0; i–) {
if(nums[i-1] < nums[i]) {
left = i-1;
break;
}
}
if(left >= 0) { //对于321,其下一个排列是第一个123,所以第二次循环和交换不需要做
for(int i = nums.length - 1; i > left; i–) {
if(nums[i] > nums[left]){
right = i;
break;
}
}
int tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
}
reserve(left + 1, nums);
}
public void reserve(int begin, int[] nums){
int end = nums.length - 1;
while(begin < end){
int tmp = nums[begin];
nums[begin] = nums[end];
nums[end] = tmp;
begin ++;
end --;
}
}

88 合并两个有序数组 逆向 双指针
逆向
//非递减数组,合并后仍保持非递减
//输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
//输出:[1,2,2,3,5,6]
public void merge(int[] nums1, int m, int[] nums2, int n) {
int last = m+n; //逆向
while(n > 0){
if(m > 0 && nums1[m-1] > nums2[n-1]){
nums1[–last] = nums1[–m];
}else{
nums1[–last] = nums2[–n];
}
}
}

#四数相加
/*

  • 给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。

  • 遍历 A 和 B 所有元素和的组合情况,并记录在 ab_map 中,ab_map 的 key 为两数和,value 为该两数和出现的次数
    遍历 C 和 D 所有元素和的组合情况,取和的负值判断其是否在 ab_map 中,若存在则取出 ab_map 对应的 value 值,count = count + value
    */
    public class Sum4_2 {

    public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
    int count = 0;
    Map<Integer,Integer> map = new HashMap<Integer,Integer>();
    int len = A.length;
    for(int i = 0; i < len; i++) {
    for(int j = 0; j < len; j++) {
    int val = 0;
    if(map.containsKey(A[i]+B[j])) {
    val = (A[i]+B[j])+1;
    }else {
    val = 1;
    }
    map.put(A[i]+B[j], val);
    }
    }

     for(int m = 0; m < len; m++) {
     	for(int n = 0; n < len; n++) {
     		int sum = -(C[m]+D[n]);
     		int val = 0;
     		if(map.containsKey(sum)) {
     			val = sum;
     		}
     		count += val;
     	}
     }
     
     return count;
    

    }
    }

#矩阵置零
给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用原地算法。
你能想出一个常数空间的解决方案吗?

用第一行的元素表示每一列是否存在0,第一列的元素表示每一行是否存在0,再使用额外两个变量firstRowIsZero,firstColIsZero判断第一行以及第一列本身是否存在0。
class Solution {
public void setZeroes(int[][] matrix) {
int row = matrix.length;
if(row0)return;
int col = matrix[0].length;
if(col
0)return;
boolean firstRowIsZero = false;
boolean firstColIsZero = false;
for(int i=0;i<row;i++){
for(int j=0;j<col;j++){
if(i!=0&&j!=0&&matrix[i][j]0){
matrix[i][0] = 0;
matrix[0][j] = 0;
}else if(matrix[i][j]0){
firstRowIsZero = i
0 ? true:firstRowIsZero;
firstColIsZero = j
0 ? true:firstColIsZero;
}
}
}
for(int i=1;i<row;i++){
if(matrix[i][0]==0){
for(int j=1;j<col;j++)matrix[i][j] = 0;
}
}
for(int j=1;j<col;j++){
if(matrix[0][j]==0){
for(int i=1;i<row;i++)matrix[i][j] = 0;
}
}
if(firstRowIsZero){
for(int j=0;j<col;j++)matrix[0][j] = 0;
}
if(firstColIsZero){
for(int i=0;i<row;i++)matrix[i][0] = 0;
}
}
}
#字谜分组
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串
说明:
● 所有输入均为小写字母。
● 不考虑答案输出的顺序

创建一个HashMap<String, List>,将每个词中的字母重新排序,把排好序之后的词作为key存入HashMap中,对每次词都进行同样的排序操作,然后将这个词放入对应的键值对中,最后返回HashMap中value的集合。
class Solution {
public List<List> groupAnagrams(String[] strs) {
int len = strs.length;
if(len==0)return new ArrayList<List>();
Map<String, List> map = new HashMap<>();
for(String str:strs){
char[] chars = str.toCharArray();
Arrays.sort(chars);
String keyStr = String.valueOf(chars);
if(!map.containsKey(keyStr))map.put(keyStr, new ArrayList());
map.get(keyStr).add(str);
}
return new ArrayList<List>(map.values());
}
}
滑动窗口
class Solution:
def problemName(self, s: str) -> int:
# Step 1: 定义需要维护的变量们 (对于滑动窗口类题目,这些变量通常是最小长度,最大长度,或者哈希表)
x, y = …, …

    # Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口
    start = 0
    for end in range(len(s)):
        # Step 3: 更新需要维护的变量, 有的变量需要一个if语句来维护 (比如最大最小长度)
        x = new_x
        if condition:
            y = new_y

        '''
        ------------- 下面是两种情况,读者请根据题意二选1 -------------
        '''
        # Step 4 - 情况1
        # 如果题目的窗口长度固定:用一个if语句判断一下当前窗口长度是否达到了限定长度 
        # 如果达到了,窗口左指针前移一个单位,从而保证下一次右指针右移时,窗口长度保持不变, 
        # 左指针移动之前, 先更新Step 1定义的(部分或所有)维护变量 
        if 窗口长度达到了限定长度:
            # 更新 (部分或所有) 维护变量 
            # 窗口左指针前移一个单位保证下一次右指针右移时窗口长度保持不变

        # Step 4 - 情况2
        # 如果题目的窗口长度可变: 这个时候一般涉及到窗口是否合法的问题
        # 如果当前窗口不合法时, 用一个while去不断移动窗口左指针, 从而剔除非法元素直到窗口再次合法
        # 在左指针移动之前更新Step 1定义的(部分或所有)维护变量 
        while 不合法:
            # 更新 (部分或所有) 维护变量 
            # 不断移动窗口左指针直到窗口再次合法

    # Step 5: 返回答案
    return ...

643 子数组最大平均数 滑动窗口
public double findMaxAverage(int[] nums, int k) {
double sum = 0;
double max_avg = Integer.MIN_VALUE;
int start = 0;
for(int end = 0; end < nums.length; end++){
sum += nums[end];
if(end-start+1 == k){
max_avg = Math.max(max_avg,sum/(double)k);
}
if(end >= k-1){
sum -= nums[start];
start++;
}
}
return max_avg;
}
#3 无重复字符的最长子串 滑动窗口
https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/solution/yi-ge-mo-ban-miao-sha-10dao-zhong-deng-n-sb0x/
给定一个字符串,找出不含有重复字符的最长子串的长度。

每遇见一个字符,就判断该字符在起点到当前位置是否出现过,如果出现过,则更新子串的长度,然后将起点更新为该字符在起点之后出现的第一个位置。
class Solution {
public int lengthOfLongestSubstring(String s) {
int maxlen = 0;//滑动窗口的长度
int left = 0,right = 1;
if(s.length() <= 1) return s.length();
for(; right < s.length(); right++){
if(s.indexOf(s.charAt(right),left) < right){ // 表示在右窗口之前存在和右窗口相同的元素
maxlen = Math.max(maxlen, right-left); //对于完全没有重复元素的,maxlen不会更新
left = s.indexOf(s.charAt(right),left) + 1; //左窗口滑动到出现重复字符位置后一位
}
}
return Math.max(maxlen,right-left);
}
}

题解套路:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
# Step 1: 定义需要维护的变量,
//本题求最大长度,所以需要定义max_len, 该题又涉及去重,因此还需要一个哈希表
max_len, hashmap = 0, {}

    # Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口
    start = 0
    for end in range(len(s)):
        # Step 3
        # 更新需要维护的变量 (max_len, hashmap)
        # i.e. 把窗口末端元素加入哈希表,使其频率加1,并且更新最大长度
        hashmap[s[end]] = hashmap.get(s[end], 0) + 1
        if len(hashmap) == end - start + 1:
            max_len = max(max_len, end - start + 1)
        
        # Step 4: 
        # 根据题意,  题目的窗口长度可变: 这个时候一般涉及到窗口是否合法的问题
        # 这时要用一个while去不断移动窗口左指针, 从而剔除非法元素直到窗口再次合法
        # 当窗口长度大于哈希表长度时候 (说明存在重复元素),窗口不合法
        # 所以需要不断移动窗口左指针直到窗口再次合法, 同时提前更新需要维护的变量 (hashmap)
        while end - start + 1 > len(hashmap):
            head = s[start]
            hashmap[head] -= 1
            if hashmap[head] == 0:
                del hashmap[head]
            start += 1
    # Step 5: 返回答案 (最大长度)
    return max_len

#209 长度最小的子数组 滑动窗口
public int minSubArrayLen(int target, int[] nums) {
int minlen = Integer.MAX_VALUE;
int sum = 0;
int start = 0;
for(int end = 0; end < nums.length; end++){
sum += nums[end];
if(sum >= target){
minlen = Math.min(minlen,end-start+1);
}
while (sum >= target){
sum -= nums[start];
minlen = Math.min(minlen, end-start+1);
start++;
}
}
if(minlen == Integer.MAX_VALUE) minlen = 0;
return minlen;
}

#1695 删除子数组的最大得分 滑动窗口
// 给你一个正整数数组 nums ,请你从中删除一个含有 若干不同元素 的子数组(子数组是连续子序列)。删除子数组的 得分 就是子数组各元素之 和 。
// 返回 只删除一个 子数组可获得的 最大得分 。
public int maximumUniqueSubarray(int[] nums) {
int max_sum = 0;
int sum = 0;
int start = 0;
for(int end = 0; end < nums.length; end++){
sum += nums[end];
if(!contain(nums,nums[end],start,end)){
max_sum = Math.max(max_sum, sum);
}
while (contain(nums,nums[end],start,end)){
sum

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值