RemNow 动态规划–数组–栈与队列–二叉树
1.动态规划的五个步骤
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
- 把下标为i的3个物品塞到背包最大装在量为4的里面,最多能装多少钱
// 物品:0 1 2
int[] weight = {1,3,4};
int[] value = {15,20,40};
int bagSize = 4;
//dp方程dp[i][j]定义是将i个物品放到背包容量为j的背包里,获得最多的钱!
//有两种组成:一是不放入i的情况,那么最优解就是dp[i-1][j];即在下标为i-1的物品里面随便放,
//放到大小为j的背包装最多的钱是
dp[i-1][j]
//情况二十放入i,那么就是i放入不会超标,所以i是能够放进去的,
//所以要在dp[i-1][j]的基础上将i的重量扣掉才是最接近dp[i-1]的钱,即将(i-1)最值钱+i的最值钱
dp[i-1][j-weight[i]]+value[i]
//所以dp方程最大值是,放i或者不放i里面去最大值(有可能放i就超标了,所以又抠掉了一些数据)
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
回归Demo
- 数组
※:数组是存放在连续内存空间上的相同类型数据的集合,数组的元素是不能删的,只能覆盖。
※:数组是.length-----string.length()------list.size()
※: int mid=(left+right+1)>>1;//向上取整数;(left+right)>>1向下取整
※:int minValue = Integer.MIN_VALUE;-------- int maxValue = Integer.MAX_VALUE;
※:记住map的遍历:
for(Map.Entry<Integer,Integer> e:map.entrySet()){//遍历map里面的两个元素,取一下最大的频数
int v=e.getValue();
sum+=v;
}
※:数组升序排序: Arrays.sort(nums);(1,2,3,6,9)
※:《算法与数据结构之美》:如何写出高效率的代码;设计模式:写出高质量代码;
※:StringBUffer
StringBuffer sb=new StringBuffer();
sb.append("sdff");//拼接字符
StringBuffer reverse = sb.reverse();//sb和reverse都是反转之后的字符
char[] chars = s.toCharArray();
StringBuilder sb=new StringBuilder(s);
List<DemoClass> collect = list.stream().sorted(Comparator.comparing(DemoClass::getValue).reversed())
.collect(Collectors.toList());
List<Integer> collect = list.stream().sorted(Comparator.comparing(Integer::intValue).reversed())
.collect(Collectors.toList());//加上reversed变倒叙
List<Integer> ll = list.stream().sorted(Comparator.comparing(Integer::intValue))
.collect(Collectors.toList());//正序排序
//stream流先按学生年龄降序排序,年龄相等的话,则按年级升级排序
resultList = resultList.stream().sorted(Comparator.comparing(User::getAge)
.reversed()
.thenComparing(Comparator.comparing(User::getGrade).reversed())
).collect(Collectors.toList());
-
栈与队列
※:栈的初始化以及常用方法Stack<Integer> stackIn=new Stack<>(); Integer pop = stackIn.pop();//弹出栈顶元素 Integer peek = stackIn.peek();//查看栈顶元素 Integer push = stackIn.push(5);//将元素存入栈 boolean empty = stackIn.isEmpty();//判断栈是否为空 int size = stackIn.size();//获取栈内容的长度 栈擅长处理的是两个相邻元素的处理,因为后进先出 Queue<Integer> queue=new LinkedList<>(); Integer poll = queue.poll();//获取并移除此队列的头,如果此队列为空,则返回 null Integer peek = queue.peek();//获取队列的头但不移除此队列的头。如果此队列为空,则返回 null boolean offer = queue.offer(5);//如果超过了返回false boolean add = queue.add(5);//如果超出了抛出 boolean empty = queue.isEmpty();//判断一个队列中是否为空。 int size = queue.size();//获取队列内容的长度
-
二叉树(首先访问节点,其次处理节点)
※:递归三部曲
1 确定入参出参数
2 确定终止条件,一般放在第一行
3 确定具体逻辑深度优先遍历DFS:
二叉树前中后序遍:中左右,左中右,左右中
postorder(root.left, list);
postorder(root.right, list);
list.add(root.val); (这一句)
//递归遍历二叉树的话,就是list.add放的位置,决定前中后顺序
栈迭代法实现前序遍历
public static List<Integer> preOrderStack(TreeNode root) {
List<Integer> result = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode peek = stack.peek();
result.add(stack.pop().val);//每次处理的都是当前节点
if (null!=peek.right) stack.push(peek.right);//利用栈先进先出的规则,不断给把当前节点的右、左节点加入
if (null!=peek.left) stack.push(peek.left);
}
return result;
}
栈实现后序遍历
//栈模拟后序遍历leetCOde144
public static List<Integer> afterOrderStack(TreeNode root) {
List<Integer> result = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode peek = stack.peek();
result.add(stack.pop().val);//每次处理的都是当前节点
if (null!=peek.left) stack.push(peek.left);
if (null!=peek.right) stack.push(peek.right);//利用栈先进先出的规则,不断给把当前节点的右、左节点加入
}
Collections.reverse(result);//将list里面的顺序颠倒一下
return result;
}
用栈实现中序遍历
public List<Integer> inorderTraversal(TreeNode root){
List<Integer> result = new ArrayList<>();//记录结果
Stack<TreeNode> stack = new Stack<>();//记录遍历的节点啊
TreeNode cur=root;
while (!stack.isEmpty() || null!=cur) {//栈不为空,或者节点不为空就是没处理完
if (cur!=null){//先向左延伸,记录不为空节点
stack.push(cur);//用栈来接收节点
cur=cur.left;//用指针来记录节点
}else{//如果节点为空,就操作栈弹出元素
TreeNode pop = stack.pop();
result.add(pop.val);
cur= pop.right;//上一步是node.left为null了,所以下一步查找node.right
}
}
return result;
//总之,node不为空就塞到栈里面去,node为空的话栈就弹出元素
}
广度优先遍历BFS(层序遍历):
//l力扣102:程序遍历
public List<List<Integer>> levelOrder(TreeNode root) {
if (null==root) return new ArrayList<>();//判空
List<List<Integer>> list=new ArrayList<>();//记录结果集
Queue<TreeNode> queue=new LinkedList<>();
queue.add(root);//将根节点加入队列,先进先出
int size=1;//第一层的长度设定
while(!queue.isEmpty()){//当队列里面还有元素,就不能终止
List<Integer> l=new ArrayList<>();//记录返回的list
while (size>0){
TreeNode poll = queue.poll();//弹出一个元素,
l.add(poll.val);//将元素加入l
TreeNode left = poll.left;//将左右元素加入队列
TreeNode right = poll.right;
if (null!=left) queue.add(left);
if (null!=right) queue.add(right);
size--;
}
list.add(l);//将程序遍历的结果存入结果集
if (queue.size()!=0) size=queue.size();//如果队列里面没有元素了,说明层的下一层元素全部为null,即到达二叉树的结尾
}
return list;//返回结果集
}
层序遍历递归,巧思
public List<List<Integer>> resList = new ArrayList<List<Integer>>();
public List<List<Integer>> levelOrder(TreeNode root) {
checkFun0ZTs(root,0);
return resList;
}
public void checkFun0ZTs(TreeNode node, Integer deep){
if (null==node) return;
deep++;//走到这一步的意思是,下一层节点不为空,如果resList里面没有创建一个list来接收参数,那就需要添加一个
if (resList.size()<deep){//说明resList里面还没有添加进这个list,需要加进去
resList.add(new ArrayList<Integer>());
}
resList.get(deep-1).add(node.val);//巧妙利用了deep,和创建deep个list添加到结果集合里面
checkFun0ZTs(node.left,deep);
checkFun0ZTs(node.right,deep);
}
链表也是list的实现类,所以
LinkedList<List<Integer>> result = new LinkedList<>();
result.addFirst(xxx);//往队列的头插入数据,
result.addLast(xxx);//往队列的尾部插入数据,
return List<List<Integer>>;//可以控制list的顺序
力扣107
public List<List<Integer>> levelOrderBottom(TreeNode root) {
LinkedList<List<Integer>> result = new LinkedList<>();
if (root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
List<Integer> oneLevel = new ArrayList<>();
// 每次都取出一层的所有数据
int count = queue.size();
for (int i = 0; i < count; i++) {
TreeNode node = queue.poll();
oneLevel.add(node.val);
if (node.left != null) queue.add(node.left);
if (node.right != null) queue.add(node.right);
}
// 每次都往队头塞
result.addFirst(oneLevel);
}
return result;
}