目录
1、如何判断程序的复杂程度:时间和空间复杂度
2、动态规划算法
2.1动态规划基本概念与思想
2.2常见动态规划问题
2.2.1找零钱问题
2.2.2走方格问题
2.2.3最长公共序列数
3、树与二叉树
3.1树与二叉树基本概念与思想
3.1.1二叉树的前序中序后序遍历
3.1.2 102. 二叉树的层序遍历
3.1.3 226. 翻转二叉树
3.1.4 101. 对称二叉树
3.2树与二叉树经典例题
3.2.1 104. 二叉树的最大深度
3.2.2 110. 平衡二叉树
3.2.3 257. 二叉树的所有路径
4、回溯算法
4.1回溯基本概念与思想
4.2回溯经典例题
4.2.1 77. 组合
4.2.2 17. 电话号码的字母组合
5、贪心算法
5.1 贪心基本概念与思想
5.2 贪心经典例题
5.2.1 455. 分发饼干
6、DFS/BFS:深度优先搜索 /广度优先搜索
6.1 DFS/BFS基本概念与思想
6.2 DFS/BFS经典例题
6.2.1 994.腐烂的橘子
6.2.2 417.太平洋大西洋水流问题
6.2.3 399.除法求值
排序算法
Java语言学习的其他内容 见Java基础知识总结(刷力扣必备)_青衫倚斜桥的博客-CSDN博客
刷题顺序见: 力扣
如何判断程序的复杂程度:时间和空间复杂度_衣冠の禽兽的博客-CSDN博客_怎么看程序的时间复杂度
GitHub - youngyangyang04/leetcode-master: 《代码随想录》LeetCode 刷题攻略:200道经典题目刷题顺序,共60w字的详细图解,视频难点剖析,50余张思维导图,支持C++,Java,Python,Go,JavaScript等多语言版本,从此算法学习不再迷茫! 来看看,你会发现相见恨晚!
动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。
基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。
注意动态规划必须要先初始化!
sinat_yt的博客_CSDN博客-python,测试理论,无人机领域博主
背包问题(Java)动态规划_sinat_yt的博客-CSDN博客_java动态规划背包问题
有数组penny,penny中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim,请返回有多少种方法可以凑成aim。
给定数组penny及它的大小(小于等于50),同时给定一个整数aim,请返回有多少种方法可以凑成aim。
测试样例:
[1,2,4], aim=3
返回:2
解析:设dp[n][m]为使用前n中货币凑成的m的种数,那么就会有两种情况:
使用第n种货币:dp[n-1][m]+dp[n][m-peney[n]]
不用第n种货币:dp[n-1][m],为什么不使用第n种货币呢,因为penney[n]>m。
这样就可以求出当m>=penney[n]时 dp[n][m] = dp[n-1][m]+dp[n][m-peney[n]],否则,dp[n][m] = dp[n-1][m]
public static int Penny(int[] penny, int aim) {
int n=penny.length;
if(n==0||penny==null||aim<0){
return 0;
}
int[][] pd = new int[n][aim+1];
for(int i=0;i=penny[i]){
pd[i][j] = pd[i-1][j]+pd[i][j-penny[i]];
}else{
pd[i][j] = pd[i-1][j];
}
}
}
return pd[n-1][aim];
}
有一个矩阵map,它每个格子有一个权值。从左上角的格子开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,返回所有的路径中最小的路径和。
给定一个矩阵map,请返回最小路径和。
测试样例:
[[1,2,3],[1,1,1]]
返回:4
解析:设dp[n][m]为走到n*m位置的路径长度,那么显而易见dp[n][m] = min(dp[n-1][m]+map[i][j],dp[n][m-1]+map[i][j]);
public class MinimumPath {
public int getMin(int[][] map, int n, int m) {
// write code here
int[][] dp = new int[n][m];
for(int i=0;ib){
return b;
}else{
return a;
}
}
}
给定两个字符串A和B,返回两个字符串的最长公共子序列的长度。例如,A="1A2C3D4B56”,B="B1D23CA45B6A”,”123456"或者"12C4B6"都是最长公共子序列。
给定两个字符串A和B,同时给定两个串的长度n和m,请返回最长公共子序列的长度。保证两串长度均小于等于300。
测试样例:
"1A2C3D4B56",10,"B1D23CA45B6A",12
返回:6
解析:设dp[n][m] ,为A的前n个字符与B的前m个字符的公共序列长度,则当A[n]==B[m]的时候,dp[i][j] = max(dp[i-1][j-1]+1,dp[i-1][j],dp[i][j-1]),否则,dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]);
import java.util.*;
public class LCS {
public int findLCS(String A, int n, String B, int m) {
// write code here
int[][] dp = new int[n][m];
char[] a = A.toCharArray();
char[] b = B.toCharArray();
//首先进行初始化
for(int i=0;imax)
max=b;
if(c>max)
max = c;
return max;
}
}
class Solution {
public int wiggleMaxLength(int[] nums) {
int n = nums.length;
if (n < 2) {
return n;
}
int[] up = new int[n];
int[] down = new int[n];
up[0] = down[0] = 1;
for (int i = 1; i < n; i++) {
if (nums[i] > nums[i - 1]) {
up[i] = Math.max(up[i - 1], down[i - 1] + 1);
down[i] = down[i - 1];
} else if (nums[i] < nums[i - 1]) {
up[i] = up[i - 1];
down[i] = Math.max(up[i - 1] + 1, down[i - 1]);
} else {
up[i] = up[i - 1];
down[i] = down[i - 1];
}
}
return Math.max(up[n - 1], down[n - 1]);
}
}
java数据结构与算法之树基本概念及二叉树(BinaryTree)的设计与实现
java数据结构与算法之树基本概念及二叉树(BinaryTree)的设计与实现_zejian_的博客-CSDN博客_java树
java数据结构与算法之平衡二叉树(AVL树)的设计与实现
java数据结构与算法之平衡二叉树(AVL树)的设计与实现_zejian_的博客-CSDN博客_java 平衡二叉树
前序遍历递归的流程示意图
测试用例用图来描述:
144. 二叉树的前序遍历
二叉树的前序遍历:按照访问根节点——左子树——右子树的方式遍历这棵树,
根 -> 左 -> 右
递归+迭代解法:迭代运用了栈的思想
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
class Solution {
public List preorderTraversal(TreeNode root) {
List res = new ArrayList();
preorder(root, res);
return res;
}
//前序
public void preorder(TreeNode root, List res) {
if (root == null) {
return;
}
res.add(root.val);
preorder(root.left, res);
preorder(root.right, res);
}
}
//迭代
class Solution {
public List preorderTraversal(TreeNode root) {
List res = new ArrayList();
if (root == null) {
return res;
}
Deque stack = new LinkedList();
TreeNode node = root;
while (!stack.isEmpty() || node != null) {
while (node != null) {
res.add(node.val);
stack.push(node);
node = node.left;
}
node = stack.pop();
node = node.right;
}
return res;
}
}
94. 二叉树的中序遍历
左 -> 根 -> 右
递归+迭代解法:迭代运用了栈的思想
class Solution {
public List inorderTraversal(TreeNode root) {
List res = new ArrayList();
inorder(root, res);
return res;
}
public void inorder(TreeNode root, List res) {
if (root == null) {
return;
}
inorder(root.left, res);
res.add(root.val);
inorder(root.right, res);
}
}
//迭代
class Solution {
public List inorderTraversal(TreeNode root) {
List res = new ArrayList();
Deque stk = new LinkedList();
while (root != null || !stk.isEmpty()) {
while (root != null) {
stk.push(root);
root = root.left;
}
root = stk.pop();
res.add(root.val);
root = root.right;
}
return res;
}
}
145. 二叉树的后序遍历
左 -> 右 -> 根
先递归后迭代
//递归
class Solution {
public List postorderTraversal(TreeNode root) {
List res = new ArrayList();
postorder(root, res);
return res;
}
public void postorder(TreeNode root, List res) {
if (root == null) {
return;
}
postorder(root.left, res);
postorder(root.right, res);
res.add(root.val);
}
}
//迭代
class Solution {
public List postorderTraversal(TreeNode root) {
List res = new ArrayList();
if (root == null) {
return res;
}
Deque stack = new LinkedList();
TreeNode prev = null;
while (root != null || !stack.isEmpty()) {
while (root != null) {
stack.push(root);
root = root.left;
}
root = stack.pop();
if (root.right == null || root.right == prev) {
res.add(root.val);
prev = root;
root = null;
} else {
stack.push(root);
root = root.right;
}
}
return res;
}
}
给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
class Solution {
List> res=new ArrayList>();
public void bianli(TreeNode root, int level){
if(level==res.size()){
res.add(new ArrayList());
}
res.get(level).add(root.val);
if(root.left!=null){
bianli(root.left,level+1);
}
if(root.right!=null){
bianli(root.right,level+1);
}
}
public List> levelOrder(TreeNode root) {
if(root==null){
return res;
}
bianli(root,0);
return res;
}
}
翻转一棵二叉树。
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root==null ){
return null;
}
TreeNode left= invertTree(root.left);
TreeNode right= invertTree(root.right);
root.right=left;
root.left=right;
return root;
}
}
class Solution {
public boolean check(TreeNode leftnode,TreeNode rightnode) {
if(leftnode==null && rightnode==null) {
return true;
}
if(leftnode==null || rightnode==null){
return false;
}
if(leftnode.val!=rightnode.val){
return false;
}
return check(leftnode.left,rightnode.right)&&check(leftnode.right,rightnode.left);
}
public boolean isSymmetric(TreeNode root) {
return check(root,root);
}
}
平衡二叉树的实现(java代码)_随性而活的风的博客-CSDN博客_二叉平衡树java实现
按照递归三部曲,来看看如何来写。
确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回这棵树的深度,所以返回值为int类型。
int Depth(TreeNode root)
确定终止条件:如果为空节点的话,就返回0,表示高度为0。
if(root==null){
return 0;
}
确定单层递归的逻辑:先求它的左子树的深度,再求的右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。
int depthl=Depth(root.left);
int depthr=Depth(root.right);
return Math.max(depthl,depthr)+1;
class Solution {
public int Depth(TreeNode root){
if(root==null){
return 0;
}
int depthl=Depth(root.left);
int depthr=Depth(root.right);
return Math.max(depthl,depthr)+1;
}
public int maxDepth(TreeNode root) {
return Depth(root);
}
}
力扣
222. 完全二叉树的节点个数
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
二叉树:我平衡么?
class Solution {
//计算每个节点的最大高度
public int height(TreeNode root){
if(root==null){
return 0;
}
int lheight=height(root.left);
int rheight=height(root.right);
return Math.max(lheight,rheight)+1;
}
//计算每个节点是否是平衡,如果有一个节点不平衡则返回false
public boolean check(TreeNode root){
if(root==null){
return true;
}
if(Math.abs(height(root.left)-height(root.right))>1){
return false;
}
return check(root.left)&&check(root.right);
}
public boolean isBalanced(TreeNode root) {
return check(root);
}
}
给定一个二叉树,返回所有从根节点到叶子节点的路径。
说明: 叶子节点是指没有子节点的节点。
class Solution {
public List binaryTreePaths(TreeNode root) {
List paths = new ArrayList();
constructPaths(root, "", paths);
return paths;
}
public void constructPaths(TreeNode root, String path, List paths) {
if (root != null) {
StringBuffer pathSB = new StringBuffer(path);
pathSB.append(Integer.toString(root.val));
if (root.left == null && root.right == null) { // 当前节点是叶子节点
paths.add(pathSB.toString()); // 把路径加入到答案中
} else {
pathSB.append("->"); // 当前节点不是叶子节点,继续递归遍历
constructPaths(root.left, pathSB.toString(), paths);
constructPaths(root.right, pathSB.toString(), paths);
}
}
}
}
关于回溯算法,你该了解这些!
回溯法解决的问题都可以抽象为树形结构(N叉树),用树形结构来理解回溯就容易多了。
大家可以从图中看出「for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历」,这样就把这棵树全遍历完了,一般来说,搜索叶子节点就是找的其中一个结果了。
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
给定两个整数 n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。你可以按 任何顺序 返回答案。
class Solution {
List> res= new ArrayList>();
Deque path=new LinkedList();
//回溯,可以想象成一棵树
public void backtracking(int n, int k, int startindex){
if(path.size()==k){
res.add(new ArrayList(path));
return;
}
for(int i=startindex;i<=n;i++){
//处理节点
path.addLast(i);
System.out.println("递归之前 => " + path);
backtracking(n,k,i+1);
path.removeLast(); 回溯,撤销处理的节点
System.out.println("递归之后 => " + path);
}
}
public List> combine(int n, int k) {
backtracking(n,k,1);
return res;
}
}
216. 组合总和 III
贪心算法一般分为如下四步:
将问题分解为若干个子问题
找出适合的贪心策略
求解每一个子问题的最优解
将局部最优解堆叠成全局最优解
排序+贪心
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i
,都有一个胃口值 g[i]
,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j
,都有一个尺寸 s[j]
。如果 s[j] >= g[i]
,我们可以将这个饼干 j
分配给孩子 i
,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
class Solution {
public int findContentChildren(int[] g, int[] s) {
//贪心算法,尽可能完成胃口最小的
int res=0;
Arrays.sort(g);
Arrays.sort(s);
int leng=g.length;
int lens=s.length;
int i=0;
int j=0;
for(;i
class Solution {
public int findMinArrowShots(int[][] points) {
if (points.length == 0) {
return 0;
}
//按照右边界从小到大排序
Arrays.sort(points,new Comparator(){
public int compare(int[] o1, int[] o2){
if(o1[1]>o2[1]){
return 1;
}
if(o1[1]pre){
pre=balloon[1];
++res;
}
}
return res;
}
}
在给定的网格中,每个单元格可以有以下三个值之一:
值 0 代表空单元格;
值 1 代表新鲜橘子;
值 2 代表腐烂的橘子。
每分钟,任何与腐烂的橘子(在 4 个正方向上)相邻的新鲜橘子都会腐烂。
返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1。
class Solution {
public int orangesRotting(int[][] grid) {
// 边界 长宽
int M = grid.length;
int N = grid[0].length;
Deque queue = new LinkedList<>();
// count 表示新鲜橘子的数量
int count = 0;
// 遍历二维数组 找出所有的新鲜橘子和腐烂的橘子
for (int r = 0; r < M; r++) {
for (int c = 0; c < N; c++) {
// 新鲜橘子计数
if (grid[r][c] == 1) {
count++;
// 腐烂的橘子就放进队列
} else if (grid[r][c] == 2) {
// 缓存腐烂橘子的坐标
queue.add(new int[]{r, c});
}
}
}
// round 表示腐烂的轮数,或者分钟数
int round = 0;
// 如果有新鲜橘子 并且 队列不为空
// 直到上下左右都触及边界 或者 被感染的橘子已经遍历完
while (count > 0 && !queue.isEmpty()) {
// BFS 层级 + 1
round++;
// 拿到当前层级的腐烂橘子数量, 因为每个层级会更新队列
int n = queue.size();
// 遍历当前层级的队列
for (int i = 0; i < n; i++) {
// 踢出队列(拿出一个腐烂的橘子)
int[] orange = queue.poll();
// 恢复橘子坐标
int r = orange[0];
int c = orange[1];
// ↑ 上邻点 判断是否边界 并且 上方是否是健康的橘子
if (r-1 >= 0 && grid[r-1][c] == 1) {
// 感染它
grid[r-1][c] = 2;
// 好橘子 -1
count--;
// 把被感染的橘子放进队列 并缓存
queue.add(new int[]{r-1, c});
}
// ↓ 下邻点 同上
if (r+1 < M && grid[r+1][c] == 1) {
grid[r+1][c] = 2;
count--;
queue.add(new int[]{r+1, c});
}
// ← 左邻点 同上
if (c-1 >= 0 && grid[r][c-1] == 1) {
grid[r][c-1] = 2;
count--;
queue.add(new int[]{r, c-1});
}
// → 右邻点 同上
if (c+1 < N && grid[r][c+1] == 1) {
grid[r][c+1] = 2;
count--;
queue.add(new int[]{r, c+1});
}
}
}
// 如果此时还有健康的橘子
// 返回 -1
// 否则 返回层级
if (count > 0) {
return -1;
} else {
return round;
}
}
}
https://huanglei.blog.csdn.net/article/details/103149074?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EOPENSEARCH%7Edefault-2.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EOPENSEARCH%7Edefault-2.control