目录
3.3 给定两个可能有环也可能没有环的单链表,如果相交,返回第一个交点
5.1 给定一颗二叉树的头结点head,返回这棵树是不是平衡二叉树
5.4 给定一颗二叉树的头结点head,任何两个节点之间都存在距离,返回整颗二叉树的最大距离
6.1 给定一个由字符串组成的数组strs,必须把所有的字符串拼接起来,返回所有可能得拼接结果中,字典序最小的结果
6.3 放灯,给定一个字符串str,只由X和.两种字符组成,X表示墙,"."表示居民点,可以放灯,如果i位置放灯,,可以让i1,i和i+1位置被点亮,问至少需要几盏灯
6.5 花费数组costs,利润数组profits,最多能做的项目k,启动资金m,输出最后获得的最大钱数
7.2 如果user,如果a,b,c其中一个字段一样,就可以认为是一个人,请合并users,返回合并后的用户数量
9.6 背包问题: 给定两个长度为n的数组weight和values,给定一个载重为正数的袋子bag,最多能装的价值为多少
9.7 给定一个数组arr,玩家A和玩家B每次只能拿最左或者最右的纸牌,玩家A和B都绝顶聪明,返回最后获胜者的分数
1. 堆结构和堆排序
import java.util.Arrays;
public class MyMaxHeap {
private int[] heap;
private final int limit;
private int heapSize;
public MyMaxHeap(int limit) {
this.limit = limit;
heap = new int[limit];
heapSize = 0;
}
public boolean isEmpty() {
return heapSize == 0;
}
public boolean isFull() {
return heapSize == limit;
}
public void push(int value) {
if (heapSize == limit) {
throw new RuntimeException("heap is full");
}
heap[heapSize] = value;
heapInsert(heap, heapSize++);
}
public int pop() {
int ans = heap[0];
swap(heap, 0, --heapSize);
heapify(heap, 0, heapSize);
return ans;
}
private void heapInsert(int[] arr, int index) {
//当arr[index]不比arr[index父]大了,或者index来到0位置了
while (arr[index] > arr[(index - 1) / 2]) {
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
//从index位置,往下看,不断的下沉
private void heapify(int[] arr, int index, int heapSize) {
int left = index * 2 + 1;
while (left < heapSize) {
int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
largest = arr[largest] > arr[index] ? largest : index;
if (largest == index) {
break;
}
swap(arr, largest, index);
index = largest;
left = index * 2 + 1;
}
}
private void swap(int[] arr, int l, int r) {
int temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
}
//从小到大进行排序
public void heapSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
// O(N*log(N))
// for (int i = 0; i < arr.length; i++) {
// heapInsert(arr, i);
// }
// O(N)
for (int i = arr.length - 1; i >= 0; i--) {
heapify(arr, i, arr.length);
}
int size = arr.length;
swap(arr, 0, --size);
// O(N*logN)
while (size > 0) {
heapify(arr, 0, size);
swap(arr, 0, --size);
}
}
public static void main(String[] args) {
int arr[] = {3, 6, 4, 8, 4, 8, 4, 9, 4, 76, 2, 5, 1};
MyMaxHeap heap = new MyMaxHeap(arr.length);
for (int i = 0; i < arr.length; i++) {
heap.push(arr[i]);
}
for (int i = 0; i < arr.length; i++) {
int val = heap.pop();
System.out.print(val + " ");
}
System.out.println();
heap.heapSort(arr);
System.out.println(Arrays.toString(arr));
}
}
2. 前缀树
public class TrieTree {
private Node root;
public TrieTree() {
root = new Node();
}
//增加
public void insert(String word) {
if (word == null) {
return;
}
char[] chars = word.toCharArray();
Node node = root;
node.pass++;
int idx = 0;
for (int i = 0; i < chars.length; i++) {
idx = chars[i] - 'a';
if (node.nexts[idx] == null) {
node.nexts[idx] = new Node();
}
node = node.nexts[idx];
node.pass++;
}
node.end++;
}
//删除
public void delete(String word) {
if (search(word) != 0) {
char[] chars = word.toCharArray();
Node node = root;
node.pass--;
int idx = 0;
for (int i = 0; i < chars.length; i++) {
idx = chars[i] - 'a';
if (--node.nexts[idx].pass == 0) {
node.nexts[idx] = null;
return;
}
}
node.end--;
}
}
//查询
public int search(String word) {
if (word == null) {
return 0;
}
char[] chars = word.toCharArray();
Node node = root;
int idx = 0;
for (int i = 0; i < chars.length; i++) {
idx = chars[i] - 'a';
if (node.nexts[idx] == null) {
return 0;
}
node = node.nexts[idx];
}
return node.end;
}
//按前缀查
public int searchPre(String word) {
if (word == null) {
return 0;
}
char[] chars = word.toCharArray();
Node node = root;
int idx = 0;
for (int i = 0; i < chars.length; i++) {
idx = chars[i] - 'a';
if (node.nexts[idx] == null) {
return 0;
}
node = node.nexts[idx];
}
return node.pass;
}
}
class Node{
int pass;
int end;
Node[] nexts;
public Node() {
nexts = new Node[26];
}
}
3. 链表常见面试题
3.1 小于等于大于分区
public static Node listPartition(Node head, int proit) {
Node sH = null;
Node sT = null;
Node eH = null;
Node eT = null;
Node bH = null;
Node bT = null;
Node next = null;
while (head != null) {
next = head.next;
head.next = null;
if (head.val < proit) {
if (sH == null) {
sH = head;
sT = head;
} else {
sT.next = head;
sT = head;
}
} else if (head.val == proit) {
if (eH == null) {
eH = head;
eT = head;
} else {
eT.next = head;
eT = head;
}
} else {
if (bH == null) {
bH = head;
bT = head;
} else {
bT.next = head;
bT = head;
}
}
head = next;
}
if (sT != null) {
sT.next = eH;
eT = eT == null ? sT : eT;
}
if (eT != null) {
eT.next = bH;
}
return sH != null ? sH : (eH != null ? eH : bH);
}
3.2 复制带随机指针的链表
//方法一,使用map集合存储所有的节点
public Node copyRandomList(Node head) {
Map<Node, Node> map = new HashMap<>();
Node p = head;
while (p != null) {
map.put(p, new Node(p.val));
p = p.next;
}
p = head;
while (p != null) {
map.get(p).next = map.get(p.next);
map.get(p).random = map.get(p.random);
p = p.next;
}
return map.get(head);
}
//方法二,先将要插入的节点插入原链表,再分割
public Node copyRandomList(Node head) {
if (head == null) {
return head;
}
Node curr = head;
Node next = null;
while (curr != null) {
next = curr.next;
Node newNode = new Node(curr.val);
newNode.next = next;
curr.next = newNode;
curr = next;
}
curr = head;
Node copyNode = null;
while (curr != null) {
next = curr.next.next;
copyNode = curr.next;
copyNode.random = curr.random == null ? null : curr.random.next;
curr = next;
}
Node res = head.next;
curr = head;
while (curr != null) {
next = curr.next.next;
copyNode = curr.next;
curr.next = next;
copyNode.next = next != null ? next.next : null;
curr = next;
}
return res;
}
3.3 给定两个可能有环也可能没有环的单链表,如果相交,返回第一个交点
public class Main {
// 给定两个可能有环也可能没有环的单链表,如果相交,返回第一个交点
public static Node getIntersectNode(Node head1, Node head2) {
if (head1 == null || head2 == null) {
return null;
}
Node loop1 = getLoopNode(head1);
Node loop2 = getLoopNode(head2);
if (loop1 == null && loop2 == null) {
return noLoop(head1, head2);
}
if (loop1 != null && loop2 != null) {
return bothLoop(head1, loop1, head2, loop2);
}
return null;
}
//如果有环,返回入环节点,否则返回null
public static Node getLoopNode(Node head) {
if (head == null || head.next == null) {
return null;
}
Node slow = head.next;
Node fast = head.next.next;
while (fast != slow) {
if (fast == null || fast.next == null) {
return null;
}
slow = slow.next;
fast = fast.next.next;
if (fast == slow) {
break;
}
}
fast = head;
while (slow != fast) {
fast = fast.next;
slow = slow.next;
}
return fast;
}
//如果两个链表都无环,返回第一个相交节点,如果不想交,返回null
public static Node noLoop(Node head1, Node head2) {
if (head1 == null || head2 == null) {
return null;
}
Node p1 = head1;
Node p2 = head2;
int n = 0;
while (p1.next != null) {
p1 = p1.next;
n++;
}
while (p2.next != null) {
p2 = p2.next;
n--;
}
if (p1 != p2) {
return null;
}
p1 = n > 0 ? head1 : head2;
p2 = p1 == head1 ? head2 : head1;
n = Math.abs(n);
while (n > 0) {
n--;
p1 = p1.next;
}
while (p1 != p2) {
p1 = p1.next;
p2 = p2.next;
}
return p1;
}
// 两个有环链表,返回第一个相交节点,如果不想交返回null
public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
Node cur1 = null;
Node cur2 = null;
if (loop1 == loop2) {
cur1 = head1;
cur2 = head2;
int n = 0;
while (cur1 != loop1) {
n++;
cur1 = cur1.next;
}
while (cur2 != loop2) {
n--;
cur2 = cur2.next;
}
cur1 = n > 0 ? head1 : head2;
cur2 = cur1 == head1 ? head2 : head1;
n = Math.abs(n);
while (n != 0) {
n--;
cur1 = cur1.next;
}
while (cur1 != cur2) {
cur1 = cur1.next;
cur2 = cur2.next;
}
return cur1;
} else {
cur1 = loop1.next;
while (cur1 != loop1) {
if (cur1 == loop2) {
return loop1;
}
cur1 = cur1.next;
}
return null;
}
}
public static void main(String[] args) {
// 1->2->3->4->5->6->7->null
Node head1 = new Node(1);
head1.next = new Node(2);
head1.next.next = new Node(3);
head1.next.next.next = new Node(4);
head1.next.next.next.next = new Node(5);
head1.next.next.next.next.next = new Node(6);
head1.next.next.next.next.next.next = new Node(7);
// 0->9->8->6->7->null
Node head2 = new Node(0);
head2.next = new Node(9);
head2.next.next = new Node(8);
head2.next.next.next = head1.next.next.next.next.next; // 8->6
System.out.println(getIntersectNode(head1, head2).val);
// 1->2->3->4->5->6->7->4...
head1 = new Node(1);
head1.next = new Node(2);
head1.next.next = new Node(3);
head1.next.next.next = new Node(4);
head1.next.next.next.next = new Node(5);
head1.next.next.next.next.next = new Node(6);
head1.next.next.next.next.next.next = new Node(7);
head1.next.next.next.next.next.next = head1.next.next.next; // 7->4
// 0->9->8->2...
head2 = new Node(0);
head2.next = new Node(9);
head2.next.next = new Node(8);
head2.next.next.next = head1.next; // 8->2
System.out.println(getIntersectNode(head1, head2).val);
// 0->9->8->6->4->5->6..
head2 = new Node(0);
head2.next = new Node(9);
head2.next.next = new Node(8);
head2.next.next.next = head1.next.next.next.next.next; // 8->6
System.out.println(getIntersectNode(head1, head2).val);
}
}
class Node {
int val;
Node next;
public Node(int val) {
this.val = val;
}
}
4.二叉树的基本算法
4.1 二叉树的前中后序遍历,层次遍历
import java.util.*;
public class Main {
//二叉树的前序遍历
public static void pre(Node root) {
if (root == null) {
return;
}
Stack<Node> stack = new Stack<>();
stack.add(root);
while (!stack.isEmpty()) {
root = stack.pop();
System.out.print(root.val + " ");
if (root.right != null) {
stack.add(root.right);
}
if (root.left != null) {
stack.add(root.left);
}
}
System.out.println();
}
//二叉树的中序遍历
public static void in(Node root) {
if (root == null) {
return;
}
Stack<Node> stack = new Stack<>();
while (!stack.isEmpty() || root != null) {
while (root != null) {
stack.add(root);
root = root.left;
}
root = stack.pop();
System.out.print(root.val + " ");
root = root.right;
}
System.out.println();
}
//二叉树的后序遍历
public static void pos(Node root) {
if (root == null) {
return;
}
Stack<Node> stack1 = new Stack<>();
Stack<Node> stack2 = new Stack<>();
stack1.add(root);
while (!stack1.isEmpty()) {
root = stack1.pop();
stack2.add(root);
if (root.left != null) {
stack1.add(root.left);
}
if (root.right != null) {
stack1.add(root.right);
}
}
while (!stack2.isEmpty()) {
System.out.print(stack2.pop().val + " ");
}
System.out.println();
}
//二叉树的层次遍历
public List<List<Integer>> levelOrder(Node root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) {
return res;
}
Queue<Node> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
int size = queue.size();
List<Integer> list = new ArrayList<>();
for (int i = 0; i < size; i++) {
root = queue.poll();
list.add(root.val);
if (root.left != null) {
queue.add(root.left);
}
if (root.right != null) {
queue.add(root.right);
}
}
res.add(list);
}
return res;
}
}
class Node {
int val;
Node left;
Node right;
public Node(int val) {
this.val = val;
}
}
4.2 获取树的最大宽度
public class Main {
public static int maxWidthUseMap(Node head) {
if (head == null) {
return 0;
}
Queue<Node> queue = new LinkedList<>();
queue.add(head);
Map<Node, Integer> map = new HashMap<>();
map.put(head, 1);
int currLevel = 1;
int currLevelNodes = 0;
int ans = 0;
while (!queue.isEmpty()) {
Node curr = queue.poll();
int currNodeLevel = map.get(curr);
if (curr.left != null) {
map.put(curr.left, currNodeLevel + 1);
queue.add(curr.left);
}
if (curr.right != null) {
map.put(curr.right, currNodeLevel + 1);
queue.add(curr.right);
}
if (currNodeLevel == currLevel) {
currLevelNodes++;
} else {
ans = Math.max(ans, currLevelNodes);
currLevel++;
currLevelNodes = 1;
}
}
ans = Math.max(ans, currLevelNodes);
return ans;
}
public static int maxWidthNoMap(Node head) {
if (head == null) {
return 0;
}
Queue<Node> queue = new LinkedList<>();
queue.add(head);
Node currEnd = head;
Node nextEnd = null;
int ans = 0;
int currLevelNodes = 0;
while (!queue.isEmpty()) {
Node curr = queue.poll();
if (curr.left != null) {
queue.add(curr.left);
nextEnd = curr.left;
}
if (curr.right != null) {
queue.add(curr.right);
nextEnd = curr.right;
}
currLevelNodes++;
if (curr == currEnd) {
ans = Math.max(ans, currLevelNodes);
currLevelNodes = 0;
currEnd = nextEnd;
}
}
return ans;
}
// for test
public static Node generateRandomBST(int maxLevel, int maxValue) {
return generate(1, maxLevel, maxValue);
}
// for test
public static Node generate(int level, int maxLevel, int maxValue) {
if (level > maxLevel || Math.random() < 0.5) {
return null;
}
Node head = new Node((int) (Math.random() * maxValue));
head.left = generate(level + 1, maxLevel, maxValue);
head.right = generate(level + 1, maxLevel, maxValue);
return head;
}
public static void main(String[] args) {
int maxLevel = 10;
int maxValue = 100;
int testTimes = 1000000;
for (int i = 0; i < testTimes; i++) {
Node head = generateRandomBST(maxLevel, maxValue);
if (maxWidthUseMap(head) != maxWidthNoMap(head)) {
System.out.println("Oops!");
}
}
System.out.println("finish!");
}
}
class Node {
int val;
Node left;
Node right;
public Node(int val) {
this.val = val;
}
}
4.3 二叉树的序列化和反序列化
public class Main {
//先序遍历的序列化和反序列化
public static Queue<String> preSerial(Node head) {
Queue<String> ans = new LinkedList<>();
pres(head, ans);
return ans;
}
private static void pres(Node head, Queue<String> ans) {
if (head == null) {
ans.add(null);
} else {
ans.add(String.valueOf(head.val));
pres(head.left, ans);
pres(head.right, ans);
}
}
public static Node buildByPreQueue(Queue<String> preList) {
if (preList == null || preList.size() == 0) {
return null;
}
return preb(preList);
}
private static Node preb(Queue<String> preList) {
String val = preList.poll();
if (val == null) {
return null;
}
Node head = new Node(Integer.valueOf(val));
head.left = preb(preList);
head.right = preb(preList);
return head;
}
//通过队列的方式实现序列化和反序列化
public static Queue<String> levelSerial(Node head) {
Queue<String> ans = new LinkedList<>();
if (head == null) {
ans.add(null);
} else {
ans.add(String.valueOf(head.val));
Queue<Node> queue = new LinkedList<>();
queue.add(head);
while (!queue.isEmpty()) {
head = queue.poll();
if (head.left != null) {
ans.add(String.valueOf(head.left.val));
queue.add(head.left);
} else {
ans.add(null);
}
if (head.right != null) {
ans.add(String.valueOf(head.right.val));
queue.add(head.right);
} else {
ans.add(null);
}
}
}
return ans;
}
public static Node buildByLevelQueue(Queue<String> levelList) {
if (levelList == null || levelList.size() == 0) {
return null;
}
Node head = generateNode(levelList.poll());
Queue<Node> queue = new LinkedList<>();
if (head != null) {
queue.add(head);
}
Node node = null;
while (!queue.isEmpty()) {
node = queue.poll();
node.left = generateNode(levelList.poll());
node.right = generateNode(levelList.poll());
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
}
return head;
}
private static Node generateNode(String val) {
if (val == null) {
return null;
}
return new Node(Integer.valueOf(val));
}
//后序遍历序列化和反序列化
public static Queue<String> posSerial(Node head) {
Queue<String> ans = new LinkedList<>();
poss(head, ans);
return ans;
}
private static void poss(Node head, Queue<String> ans) {
if (head == null) {
ans.add(null);
} else {
poss(head.left, ans);
poss(head.right, ans);
ans.add(String.valueOf(head.val));
}
}
public static Node buildByPostQueue(Queue<String> poslist) {
if (poslist == null || poslist.size() == 0) {
return null;
}
Stack<String> stack = new Stack<>();
while (!poslist.isEmpty()) {
stack.push(poslist.poll());
}
return posb(stack);
}
private static Node posb(Stack<String> posStack) {
String val = posStack.pop();
if (val == null) {
return null;
}
Node node = new Node(Integer.parseInt(val));
node.right = posb(posStack);
node.left = posb(posStack);
return node;
}
}
class Node {
int val;
Node left;
Node right;
public Node(int val) {
this.val = val;
}
}
4.4 二叉树的后继节点
public class Main {
//获取一个节点的后继节点
public static Node getSuccessorNode(Node node) {
if (node == null) {
return null;
}
if (node.right != null) {
return getLeftMost(node.right);
} else {
Node parent = node.parent;
while (parent != null && parent.left != node) {
node = parent;
parent = node.parent;
}
return parent;
}
}
private static Node getLeftMost(Node node) {
if (node == null) {
return null;
}
while (node.left != null) {
node = node.left;
}
return node;
}
}
class Node {
int val;
Node left;
Node right;
Node parent;
public Node(int val) {
this.val = val;
}
}
4.5 打印纸带的凹凸折痕
public class Main {
private static void printAllFolds(int n) {
printProcess(1, n, true);
}
public static void printProcess(int i, int n, boolean down) {
if (i > n) {
return;
}
printProcess(i + 1, n, true);
System.out.println(down ? "凹" : "凸");
printProcess(i + 1, n, false);
}
}
5. 二叉树的递归套路
用于解决大多数二叉树尤其是书信dp的问题
1) 假设以X节点为头,假设可以向X左树和X右树要任何信息
2) 在上一步的假设下,讨论以X为头结点的树,得到答案的可能性(最重要)
常见分类:和X有关信息,和X无关信息
3) 列出所有可能性后,确定到底需要向左树和右树要什么样的信息
4) 把左树信息和右树信息求全集,就是任何一颗子树都需要返回的信息S
5) 递归函数都返回S,每一颗子树都这么要求
6) 写代码,在代码中考虑如何把左树信息和右树信息整合出整棵树的信息
5.1 给定一颗二叉树的头结点head,返回这棵树是不是平衡二叉树
class Solution {
public boolean isBalanced(TreeNode root) {
return process(root).isBanance;
}
public Info process(TreeNode node) {
if (node == null) {
return new Info(true, 0);
}
Info leftInfo = process(node.left);
Info rightInfo = process(node.right);
int hight = Math.max(leftInfo.hight, rightInfo.hight) + 1;
boolean isBalance = true;
if (!leftInfo.isBanance || !rightInfo.isBanance || Math.abs(leftInfo.hight - rightInfo.hight) > 1) {
isBalance = false;
}
return new Info(isBalance, hight);
}
}
class Info {
boolean isBanance;
int hight;
public Info(boolean isBanance, int hight) {
this.isBanance = isBanance;
this.hight = hight;
}
}
5.2 判断一颗二叉树是不是满二叉树
public class Solution {
public static boolean isFull1(Node head) {
if (head == null) {
return true;
}
int height = h(head);
int nodes = n(head);
return 2 * height - 1 == nodes;
}
private static int h(Node head) {
if (head == null) {
return 0;
}
return Math.max(h(head.left), h(head.right)) + 1;
}
private static int n(Node head) {
if (head == null) {
return 0;
}
return n(head.left) + n(head.right) + 1;
}
public static boolean isFull2(Node head) {
if (head == null) {
return true;
}
Info all = process(head);
return all.height * 2 - 1 == all.nodes;
}
private static Info process(Node head) {
if (head == null) {
return new Info(0, 0);
}
Info leftInfo = process(head.left);
Info rightInfo = process(head.right);
int height = Math.max(leftInfo.height, rightInfo.height) + 1;
int nodes = leftInfo.nodes + rightInfo.nodes + 1;
return new Info(height, nodes);
}
}
class Info {
int height;
int nodes;
public Info(int height, int nodes) {
this.height = height;
this.nodes = nodes;
}
}
5.3 判断是否是完全二叉树
import java.util.LinkedList;
import java.util.Queue;
public class Solution{
public static boolean isCST(Node head) {
if (head == null) {
return true;
}
Queue<Node> queue = new LinkedList<>();
queue.add(head);
boolean flag = false;
while (!queue.isEmpty()) {
head = queue.poll();
Node l = head.left;
Node r = head.right;
if ((r != null && l == null) || (flag && !(l == null && r == null))) {
return false;
}
if (l != null) {
queue.add(l);
}
if (r != null) {
queue.add(r);
}
if (l == null || r == null) {
flag = true;
}
}
return true;
}
public static boolean isCBT2(Node head) {
if (head == null) {
return true;
}
Info all = process(head);
return all.isCBT;
}
private static Info process(Node head) {
if (head == null) {
return new Info(true, true, 0);
}
Info leftInfo = process(head.left);
Info rightInfo = process(head.right);
boolean isFull = leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height;
int height = Math.max(leftInfo.height, rightInfo.height) + 1;
boolean isCBT = false;
if (isFull) {
isCBT = true;
} else {
if (leftInfo.isCBT && rightInfo.isCBT) {
//左满,右满,高度差1
if (leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) {
isCBT = true;
//左完全,右满,高度差1
} else if (leftInfo.isCBT && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) {
isCBT = true;
//左满,右完全,高度相同
} else if (leftInfo.isFull && rightInfo.isCBT && leftInfo.height == rightInfo.height) {
isCBT = true;
}
}
}
return new Info(isFull, isCBT, height);
}
public static Node generateRandomBST(int maxLevel, int maxValue) {
return generate(1, maxLevel, maxValue);
}
public static Node generate(int level, int maxLevel, int maxValue) {
if (level > maxLevel || Math.random() < 0.5) {
return null;
}
Node head = new Node((int) (Math.random() * maxValue));
head.left = generate(level + 1, maxLevel, maxValue);
head.right = generate(level + 1, maxLevel, maxValue);
return head;
}
public static void main(String[] args) {
int maxLevel = 5;
int maxValue = 100;
int testTimes = 10000000;
for (int i = 0; i < testTimes; i++) {
Node head = generateRandomBST(maxLevel, maxValue);
if (isCST(head) != isCBT2(head)) {
System.out.println("Oops!");
}
}
System.out.println("finish");
}
}
class Info{
boolean isFull;
boolean isCBT;
int height;
public Info(boolean isFull, boolean isCBT, int height) {
this.isFull = isFull;
this.isCBT = isCBT;
this.height = height;
}
}
class Node{
int val;
Node left;
Node right;
public Node(int val) {
this.val = val;
}
}
5.4 给定一颗二叉树的头结点head,任何两个节点之间都存在距离,返回整颗二叉树的最大距离
public class Solution {
public static int maxDistance(Node head) {
return process(head).maxDistance;
}
public static Info process(Node head) {
if (head == null) {
return new Info(0, 0);
}
Info leftInfo = process(head.left);
Info rightInfo = process(head.right);
int hight = Math.max(leftInfo.hight, rightInfo.hight) + 1;
int maxDistance = Math.max(Math.max(leftInfo.maxDistance, rightInfo.maxDistance), leftInfo.hight + rightInfo.hight + 1);
return new Info(maxDistance, hight);
}
}
class Info{
int maxDistance;
int hight;
public Info(int maxDistance, int hight) {
this.maxDistance = maxDistance;
this.hight = hight;
}
}
5.5 返回二叉树中最大二叉搜索子树的节点数量
public class Solution {
public static int maxSubBSTSize(Node head) {
if (head == null) {
return 0;
}
return process(head).maxSubBSTSize;
}
public static Info process(Node head) {
if (head == null) {
return null;
}
Info leftInfo = process(head.left);
Info rightInfo = process(head.right);
int max = head.val;
int min = head.val;
int maxSubBSTSize = 0;
if (leftInfo != null) {
max = Math.max(max, leftInfo.max);
min = Math.min(min, leftInfo.min);
maxSubBSTSize = Math.max(maxSubBSTSize, leftInfo.maxSubBSTSize);
}
if (rightInfo != null) {
max = Math.max(max, rightInfo.max);
min = Math.min(min, rightInfo.min);
maxSubBSTSize = Math.max(maxSubBSTSize, rightInfo.maxSubBSTSize);
}
boolean isBST = false;
//左树整体是二叉搜索树
if ((leftInfo == null ? true : (leftInfo.isBST && leftInfo.max < head.val)) &&
//右树整体是二叉搜索树
(rightInfo == null ? true : (rightInfo.isBST && rightInfo.min > head.val))) {
isBST = true;
maxSubBSTSize = (leftInfo == null ? 0 : leftInfo.maxSubBSTSize) + (rightInfo == null ? 0 : rightInfo.maxSubBSTSize) + 1;
}
return new Info(isBST, maxSubBSTSize, max, min);
}
}
class Info {
boolean isBST;
int maxSubBSTSize;
int max;
int min;
public Info(boolean isBST, int maxSubBSTSize, int max, int min) {
this.isBST = isBST;
this.maxSubBSTSize = maxSubBSTSize;
this.max = max;
this.min = min;
}
}
5.6 派对的最大快乐值
import java.util.ArrayList;
import java.util.List;
public class Solution{
public static int maxHappy(Employee boss) {
if (boss == null) {
return 0;
}
Info all = process(boss);
return Math.max(all.yes, all.no);
}
public static Info process(Employee employee) {
if (employee.nexts.isEmpty()) {
return new Info(employee.happy, 0);
}
int yes = employee.happy;
int no = 0;
for (Employee next : employee.nexts) {
Info nextInfo = process(next);
yes += nextInfo.no;
no += Math.max(nextInfo.yes, nextInfo.no);
}
return new Info(yes, no);
}
}
class Info{
int yes;
int no;
public Info(int yes, int no) {
this.yes = yes;
this.no = no;
}
}
class Employee{
int happy;
List<Employee> nexts;
public Employee(int happy) {
this.happy = happy;
nexts = new ArrayList<>();
}
}
5.7 返回二叉树中最大二叉搜索子树的头结点
public class Solution {
public static Node maxSubBSTHead(Node head) {
if (head == null) {
return null;
}
return process(head).maxSubBSTHead;
}
public static Info process(Node head) {
if (head == null) {
return null;
}
Info leftInfo = process(head.left);
Info rightInfo = process(head.right);
Node maxSubBSTHead = null;
int maxSubBSTSize = 0;
int max = head.val;
int min = head.val;
if (leftInfo != null) {
min = Math.min(min, leftInfo.min);
max = Math.max(max, leftInfo.max);
maxSubBSTSize = leftInfo.maxSubBSTSize;
maxSubBSTHead = leftInfo.maxSubBSTHead;
}
if (rightInfo != null) {
min = Math.min(min, rightInfo.min);
max = Math.max(max, rightInfo.max);
if (rightInfo.maxSubBSTSize > maxSubBSTSize) {
maxSubBSTHead = rightInfo.maxSubBSTHead;
maxSubBSTSize = rightInfo.maxSubBSTSize;
}
}
if ((leftInfo == null ? true : (leftInfo.maxSubBSTHead == head.left && leftInfo.max < head.val)) &&
(rightInfo == null ? true : (rightInfo.maxSubBSTHead == head.right && rightInfo.min > head.val))) {
maxSubBSTHead = head;
maxSubBSTSize = (leftInfo == null ? 0 : leftInfo.maxSubBSTSize) + (rightInfo == null ? 0 : rightInfo.maxSubBSTSize) + 1;
}
return new Info(maxSubBSTHead, maxSubBSTSize, max, min);
}
}
class Info {
Node maxSubBSTHead;
int maxSubBSTSize;
int max;
int min;
public Info(Node maxSubBSTHead, int maxSubBSTSize, int max, int min) {
this.maxSubBSTHead = maxSubBSTHead;
this.maxSubBSTSize = maxSubBSTSize;
this.max = max;
this.min = min;
}
}
5.8 返回二叉树中两个节点a和b的最低公共祖先
import java.util.*;
class Solution {
public static TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) {
return null;
}
Info all = process(root, p, q);
return all.ans;
}
private static Info process(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) {
return new Info(false, false, null);
}
Info leftInfo = process(root.left, p, q);
Info rightInfo = process(root.right, p, q);
boolean findP = leftInfo.findP || rightInfo.findP || root.val == p.val;
boolean findQ = leftInfo.findQ || rightInfo.findQ || root.val == q.val;
TreeNode ans = null;
if (leftInfo.ans != null) {
ans = leftInfo.ans;
} else if (rightInfo.ans != null) {
ans = rightInfo.ans;
} else if (findP && findQ) {
ans = root;
}
return new Info(findP, findQ, ans);
}
//使用集合,效率低
public static TreeNode lowestCommonAncestor2(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) {
return null;
}
Map<TreeNode, TreeNode> parentMap = new HashMap<>();
Set<TreeNode> set = new HashSet<>();
parentMap.put(root, null);
fillMap(parentMap, root);
while (p != null) {
set.add(p);
p = parentMap.get(p);
}
while (q != null && !set.contains(q)) {
q = parentMap.get(q);
}
return q;
}
private static void fillMap(Map<TreeNode, TreeNode> parentMap, TreeNode root) {
if (root.left != null) {
parentMap.put(root.left, root);
fillMap(parentMap, root.left);
}
if (root.right != null) {
parentMap.put(root.right, root);
fillMap(parentMap, root.right);
}
}
}
class Info {
boolean findP;
boolean findQ;
TreeNode ans;
public Info(boolean findP, boolean findQ, TreeNode ans) {
this.findP = findP;
this.findQ = findQ;
this.ans = ans;
}
}
6. 贪心算法
6.1 给定一个由字符串组成的数组strs,必须把所有的字符串拼接起来,返回所有可能得拼接结果中,字典序最小的结果
import java.util.*;
public class Solution {
public static String lowestString1(String[] strs) {
if (strs == null || strs.length == 0) {
return "";
}
List<String> list = new ArrayList<>();
Set<Integer> set = new HashSet<>();
dfs(strs, list, "", set);
String lowest = list.get(0);
for (int i = 1; i < list.size(); i++) {
if (list.get(i).compareTo(lowest) < 0) {
lowest = list.get(i);
}
}
return lowest;
}
private static void dfs(String[] strs, List<String> list, String path, Set<Integer> set) {
if (set.size() == strs.length) {
list.add(path);
} else {
for (int i = 0; i < strs.length; i++) {
if (!set.contains(i)) {
set.add(i);
dfs(strs, list, path + strs[i], set);
set.remove(i);
}
}
}
}
public static String lowestString2(String[] strs) {
if (strs == null || strs.length == 0) {
return "";
}
Arrays.sort(strs, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return (o1 + o2).compareTo(o2 + o1);
}
});
String res = "";
for (String str : strs) {
res += str;
}
return res;
}
}
6.2 会议安排问题
import java.util.*;
public class Solution {
//贪心
public static int bestArrange1(Program[] programs) {
if (programs == null || programs.length == 0) {
return 0;
}
Arrays.sort(programs, new Comparator<Program>() {
@Override
public int compare(Program o1, Program o2) {
return o1.end - o2.end;
}
});
int timeLine = 0;
int res = 0;
for (Program program : programs) {
if (timeLine <= program.start) {
timeLine = program.end;
res++;
}
}
return res;
}
//回溯
public static int bestArrange2(Program[] programs) {
if (programs == null || programs.length == 0) {
return 0;
}
return dfs(programs, 0, 0);
}
//done 已经安排的会议数量
//timeLine目前来到的时间点
private static int dfs(Program[] programs, int done, int timeLine) {
if (programs.length == 0) {
return done;
}
int max = done;
for (int i = 0; i < programs.length; i++) {
if (timeLine <= programs[i].start) {
Program[] nexts = copyAndExcept(programs, i);
max = Math.max(max, dfs(nexts, done + 1, programs[i].end));
}
}
return max;
}
private static Program[] copyAndExcept(Program[] programs, int i) {
Program[] res = new Program[programs.length - 1];
int k = 0;
for (int j = 0; j < programs.length; j++) {
if (j != i) {
res[k++] = programs[j];
}
}
return res;
}
}
class Program {
int start;
int end;
public Program(int start, int end) {
this.start = start;
this.end = end;
}
}
6.3 放灯,给定一个字符串str,只由X和.两种字符组成,X表示墙,"."表示居民点,可以放灯,如果i位置放灯,,可以让i1,i和i+1位置被点亮,问至少需要几盏灯
import java.util.*;
public class Solution {
public static int minLight1(String str) {
if (str == null || str.length() == 0) {
return 0;
}
return process(str.toCharArray(), 0, new HashSet<>());
}
private static int process(char[] chars, int idx, Set<Integer> set) {
if (idx == chars.length) {
for (int i = 0; i < chars.length; i++) {
if (chars[i] != 'X') {
if (!set.contains(i - 1) && !set.contains(i) && !set.contains(i + 1)) {
return Integer.MAX_VALUE;
}
}
}
return set.size();
} else {
int no = process(chars, idx + 1, set);
int yes = Integer.MAX_VALUE;
if (chars[idx] != 'X') {
set.add(idx);
yes = process(chars, idx + 1, set);
set.remove(idx);
}
return Math.min(yes, no);
}
}
public static int minLight2(String str) {
if (str == null || str.length() == 0) {
return 0;
}
char[] chars = str.toCharArray();
int lights = 0;
int idx = 0;
while (idx < chars.length) {
if (chars[idx] == 'X') {
idx++;
} else {
lights++;
if (idx + 1 == str.length()) {
break;
} else {
if (chars[idx + 1] == 'X') {
idx += 2;
} else {
idx += 3;
}
}
}
}
return lights;
}
// for test
public static String randomString(int len) {
char[] res = new char[(int) (Math.random() * len) + 1];
for (int i = 0; i < res.length; i++) {
res[i] = Math.random() < 0.5 ? 'X' : '.';
}
return String.valueOf(res);
}
public static void main(String[] args) {
int len = 20;
int testTime = 100000;
for (int i = 0; i < testTime; i++) {
String test = randomString(len);
int ans1 = minLight1(test);
int ans2 = minLight2(test);
if (ans1 != ans2) {
System.out.println("oops!");
}
}
System.out.println("finish!");
}
}
6.4 分隔金条,求最小代价
import java.util.PriorityQueue;
public class Solution {
//堆+贪心
public static int lessMoney1(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
PriorityQueue<Integer> heap = new PriorityQueue<>();
for (int i = 0; i < arr.length; i++) {
heap.add(arr[i]);
}
int sum = 0;
int cur = 0;
while (heap.size() > 1) {
cur = heap.poll() + heap.poll();
sum += cur;
heap.add(cur);
}
return sum;
}
//回溯暴力
public static int lessMoney2(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
return process(arr, 0);
}
private static int process(int[] arr, int pre) {
if (arr.length == 1) {
return pre;
}
int ans = Integer.MAX_VALUE;
for (int i = 0; i < arr.length; i++) {
for (int j = i + 1; j < arr.length; j++) {
int[] copyAndMergeArr = copyAndMerge(arr, i, j);
ans = Math.min(ans, process(copyAndMergeArr, pre + arr[i] + arr[j]));
}
}
return ans;
}
private static int[] copyAndMerge(int[] arr, int i, int j) {
int[] ans = new int[arr.length - 1];
int k = 0;
for (int l = 0; l < arr.length; l++) {
if (l != i && l != j) {
ans[k++] = arr[l];
}
}
ans[k] = arr[i] + arr[j];
return ans;
}
}
6.5 花费数组costs,利润数组profits,最多能做的项目k,启动资金m,输出最后获得的最大钱数
import java.util.*;
public class Solution {
//k: 一共可以做多少个项目,w: 初始资金
public static int findMaxCapital(int k, int w, int[] profits, int[] costs) {
PriorityQueue<Program> minCost = new PriorityQueue<>(new Comparator<Program>() {
@Override
public int compare(Program o1, Program o2) {
return o1.c - o2.c;
}
});
PriorityQueue<Program> maxProfit = new PriorityQueue<>(new Comparator<Program>() {
@Override
public int compare(Program o1, Program o2) {
return o2.p - o1.p;
}
});
for (int i = 0; i < profits.length; i++) {
minCost.add(new Program(profits[i], costs[i]));
}
for (int i = 0; i < k; i++) {
while (!minCost.isEmpty() && w >= minCost.poll().c) {
maxProfit.add(minCost.poll());
}
if (maxProfit.isEmpty()) {
return w;
}
w += maxProfit.poll().p;
}
return w;
}
}
class Program{
int p;
int c;
public Program(int p, int c) {
this.p = p;
this.c = c;
}
}
7. 并查集
7.1 模板
class UnionFind<V> {
Map<V, Node<V>> nodes;
Map<Node<V>, Node<V>> parents;
Map<Node<V>, Integer> sizeMap;
public UnionFind(List<V> values) {
for (V value : values) {
Node<V> node = new Node<>(value);
nodes.put(value, node);
parents.put(node, node);
sizeMap.put(node, 1);
}
}
public Node<V> findFather(Node<V> cur) {
Stack<Node<V>> path = new Stack<>();
while (cur != parents.get(cur)) {
path.push(cur);
cur = parents.get(cur);
}
//cur头结点
while (!path.isEmpty()) {
parents.put(path.pop(), cur);
}
return cur;
}
public void union(V a, V b) {
if (!nodes.containsKey(a) || !nodes.containsKey(b)) {
return;
}
Node<V> aHead = findFather(nodes.get(a));
Node<V> bHead = findFather(nodes.get(b));
if (aHead != bHead) {
int aSetSize = sizeMap.get(aHead);
int bSetSize = sizeMap.get(bHead);
if (aSetSize >= bSetSize) {
parents.put(bHead, aHead);
sizeMap.put(aHead, aSetSize + bSetSize);
sizeMap.remove(bHead);
} else {
parents.put(aHead, bHead);
sizeMap.put(bHead, aSetSize + bSetSize);
sizeMap.remove(aHead);
}
}
}
public int getSetNum() {
return sizeMap.size();
}
}
class Node<V> {
V val;
public Node(V val) {
this.val = val;
}
}
7.2 如果user,如果a,b,c其中一个字段一样,就可以认为是一个人,请合并users,返回合并后的用户数量
public class Solution {
public static int mergeUser(List<User> users) {
UnionFind<User> unionFind = new UnionFind<>(users);
Map<String, User> mapA = new HashMap<>();
Map<String, User> mapB = new HashMap<>();
Map<String, User> mapC = new HashMap<>();
for (User user : users) {
if (mapA.containsKey(user.a)) {
unionFind.union(user, mapA.get(user.a));
} else {
mapA.put(user.a, user);
}
if (mapB.containsKey(user.b)) {
unionFind.union(user, mapB.get(user.b));
} else {
mapB.put(user.b, user);
}
if (mapC.containsKey(user.c)) {
unionFind.union(user, mapC.get(user.c));
} else {
mapC.put(user.c, user);
}
}
return unionFind.getSetNum();
}
}
class User {
String a;
String b;
String c;
public User(String a, String b, String c) {
this.a = a;
this.b = b;
this.c = c;
}
}
8. 图
8.1 图结构模板
import java.util.*;
public class Solution{
//[weight,from节点上面的值,to节点上面的值] matrix所有的边 N*3的矩阵
public static Graph createGraph(Integer[][] matrix) {
Graph graph = new Graph();
for (int i = 0; i < matrix.length; i++) {
Integer weight = matrix[i][0];
Integer from = matrix[i][1];
Integer to = matrix[i][2];
if (!graph.nodes.containsKey(from)) {
graph.nodes.put(from, new Node(from));
}
if (!graph.nodes.containsKey(to)) {
graph.nodes.put(to, new Node(to));
}
Node fromNode = graph.nodes.get(from);
Node toNode = graph.nodes.get(to);
Edge edge = new Edge(weight, fromNode, toNode);
fromNode.nexts.add(toNode);
fromNode.out++;
toNode.in++;
fromNode.edges.add(edge);
graph.edges.add(edge);
}
return graph;
}
}
class Node{//节点
int val;
int in;//入度
int out;//出度
List<Node> nexts;//直接指向的节点
List<Edge> edges;//直接连接的边
public Node(int val) {
this.val = val;
nexts = new ArrayList<>();
edges = new ArrayList<>();
}
}
class Edge{//边
int weight;
Node from;
Node to;
public Edge(int weight, Node from, Node to) {
this.weight = weight;
this.from = from;
this.to = to;
}
}
class Graph{//图
Map<Integer, Node> nodes;
Set<Edge> edges;
public Graph() {
nodes = new HashMap<>();
edges = new HashSet<>();
}
}
8.2 图的DFS
public static void dfs(Node node) {
if (node == null) {
return;
}
Stack<Node> stack = new Stack<>();
HashSet<Node> set = new HashSet<>();
stack.add(node);
set.add(node);
System.out.println(node.val);
while (!stack.isEmpty()) {
Node cur = stack.pop();
for (Node next : cur.nexts) {
if (!set.contains(next)) {
stack.push(cur);
stack.push(next);
set.add(next);
System.out.println(next.val);
break;
}
}
}
}
8.3 图的BFS
public static void bfs(Node node) {
if (node == null) {
return;
}
Queue<Node> queue = new LinkedList<>();
Set<Node> set = new HashSet<>();
queue.add(node);
set.add(node);
while (!queue.isEmpty()) {
Node cur = queue.poll();
System.out.println(cur.val);
for (Node next : cur.nexts) {
if (!set.contains(next)) {
set.add(next);
queue.add(next);
}
}
}
}
8.4 拓扑排序
public static List<Node> topologySort(Graph graph) {
//key:某一个node,value:剩余的入度
Map<Node, Integer> inMap = new HashMap<>();
//入度为0的节点
Queue<Node> zeroNode = new LinkedList<>();
for (Node node : graph.nodes.values()) {
inMap.put(node, node.in);
if (node.in == 0) {
zeroNode.add(node);
}
}
List<Node> res = new LinkedList<>();
while (!zeroNode.isEmpty()) {
Node cur = zeroNode.poll();
res.add(cur);
for (Node next : cur.nexts) {
inMap.put(next, inMap.get(next) - 1);
if (inMap.get(next) == 0) {
zeroNode.add(next);
}
}
}
return res;
}
8.5 图的最小生成树 克鲁斯卡算法(kruskal)
public class Solution {
//克鲁斯卡 最小生成树
public static Set<Edge> kruskalMst(Graph graph) {
UnionFind unionFind = new UnionFind();
unionFind.makeSets(graph.nodes.values());
PriorityQueue<Edge> heap = new PriorityQueue<>(new Comparator<Edge>() {
@Override
public int compare(Edge o1, Edge o2) {
return o1.weight - o2.weight;
}
});
for (Edge edge : graph.edges) {
heap.add(edge);
}
Set<Edge> res = new HashSet<>();
while (!heap.isEmpty()) {
Edge edge = heap.poll();
if (!unionFind.isSameSet(edge.from, edge.to)) {
res.add(edge);
unionFind.union(edge.from, edge.to);
}
}
return res;
}
}
//并查集
class UnionFind{
Map<Node, Node> parentMap;
Map<Node, Integer> sizeMap;
public UnionFind() {
parentMap = new HashMap<>();
sizeMap = new HashMap<>();
}
public void makeSets(Collection<Node> nodes) {
parentMap.clear();
sizeMap.clear();
for (Node node : nodes) {
parentMap.put(node, node);
sizeMap.put(node, 1);
}
}
private Node findParent(Node node) {
Stack<Node> stack = new Stack<>();
while (node != parentMap.get(node)) {
stack.add(node);
node = parentMap.get(node);
}
while (!stack.isEmpty()) {
parentMap.put(stack.pop(), node);
}
return node;
}
public boolean isSameSet(Node a, Node b) {
return findParent(a) == findParent(b);
}
public void union(Node a, Node b) {
if (a == null || b == null) {
return;
}
Node headA = findParent(a);
Node headB = findParent(b);
if (headA != headB) {
int sizeA = sizeMap.get(headA);
int sizeB = sizeMap.get(headB);
if (sizeA <= sizeB) {
parentMap.put(headA, headB);
sizeMap.put(headB, sizeA + sizeB);
sizeMap.remove(sizeA);
} else {
parentMap.put(headB, headA);
sizeMap.put(headA, sizeA + sizeB);
sizeMap.remove(sizeB);
}
}
}
}
8.6 图的最小生成树 普利姆算法(Prim)
public static Set<Edge> primMST(Graph graph) {
PriorityQueue<Edge> heap = new PriorityQueue<>(new Comparator<Edge>() {
@Override
public int compare(Edge o1, Edge o2) {
return o1.weight - o2.weight;
}
});
Set<Node> set = new HashSet<>();
Set<Edge> res = new HashSet<>();
for (Node node : graph.nodes.values()) {//防森林
if (!set.contains(node)) {
set.add(node);
for (Edge edge : node.edges) {
heap.add(edge);
}
while (!heap.isEmpty()) {
Edge edge = heap.poll();
Node toNode = edge.to;
if (!set.contains(toNode)) {
set.add(toNode);
res.add(edge);
for (Edge nextEdge : toNode.edges) {
heap.add(nextEdge);
}
}
}
}
}
return res;
}
8.7 图的最短距离,迪杰斯特拉算法(dijkstra)
import java.util.*;
// no negative weight
public class Main {
public static Map<Node, Integer> dijkstra1(Node head) {
// 从head出发到所有点的最小距离
// key : 从head出发到达key
// value : 从head出发到达key的最小距离
// 如果在表中,没有T的记录,含义是从head出发到T这个点的距离为正无穷
Map<Node, Integer> distanceMap = new HashMap<>();
distanceMap.put(head, 0);
// 已经求过距离的节点,存在selectedNodes中,以后再也不碰
Set<Node> selectedNodes = new HashSet<>();
Node minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes);
while (minNode != null) {
int distance = distanceMap.get(minNode);
for (Edge edge : minNode.edges) {
Node toNode = edge.to;
if (!distanceMap.containsKey(toNode)) {
distanceMap.put(toNode, distance + edge.weight);
} else {
distanceMap.put(edge.to, Math.min(distanceMap.get(toNode), distance + edge.weight));
}
}
selectedNodes.add(minNode);
minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes);
}
return distanceMap;
}
public static Node getMinDistanceAndUnselectedNode(Map<Node, Integer> distanceMap, Set<Node> touchedNodes) {
Node minNode = null;
int minDistance = Integer.MAX_VALUE;
for (Entry<Node, Integer> entry : distanceMap.entrySet()) {
Node node = entry.getKey();
int distance = entry.getValue();
if (!touchedNodes.contains(node) && distance < minDistance) {
minNode = node;
minDistance = distance;
}
}
return minNode;
}
public static class NodeRecord {
public Node node;
public int distance;
public NodeRecord(Node node, int distance) {
this.node = node;
this.distance = distance;
}
}
public static class NodeHeap {
private Node[] nodes; // 实际的堆结构
// key 某一个node, value 上面数组中的位置
private HashMap<Node, Integer> heapIndexMap;
// key 某一个节点, value 从源节点出发到该节点的目前最小距离
private HashMap<Node, Integer> distanceMap;
private int size; // 堆上有多少个点
public NodeHeap(int size) {
nodes = new Node[size];
heapIndexMap = new HashMap<>();
distanceMap = new HashMap<>();
size = 0;
}
public boolean isEmpty() {
return size == 0;
}
// 有一个点叫node,现在发现了一个从源节点出发到达node的距离为distance
// 判断要不要更新,如果需要的话,就更新
public void addOrUpdateOrIgnore(Node node, int distance) {
if (inHeap(node)) {
distanceMap.put(node, Math.min(distanceMap.get(node), distance));
insertHeapify(node, heapIndexMap.get(node));
}
if (!isEntered(node)) {
nodes[size] = node;
heapIndexMap.put(node, size);
distanceMap.put(node, distance);
insertHeapify(node, size++);
}
}
public NodeRecord pop() {
NodeRecord nodeRecord = new NodeRecord(nodes[0], distanceMap.get(nodes[0]));
swap(0, size - 1);
heapIndexMap.put(nodes[size - 1], -1);
distanceMap.remove(nodes[size - 1]);
// free C++同学还要把原本堆顶节点析构,对java同学不必
nodes[size - 1] = null;
heapify(0, --size);
return nodeRecord;
}
private void insertHeapify(Node node, int index) {
while (distanceMap.get(nodes[index]) < distanceMap.get(nodes[(index - 1) / 2])) {
swap(index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
private void heapify(int index, int size) {
int left = index * 2 + 1;
while (left < size) {
int smallest = left + 1 < size && distanceMap.get(nodes[left + 1]) < distanceMap.get(nodes[left])
? left + 1
: left;
smallest = distanceMap.get(nodes[smallest]) < distanceMap.get(nodes[index]) ? smallest : index;
if (smallest == index) {
break;
}
swap(smallest, index);
index = smallest;
left = index * 2 + 1;
}
}
private boolean isEntered(Node node) {
return heapIndexMap.containsKey(node);
}
private boolean inHeap(Node node) {
return isEntered(node) && heapIndexMap.get(node) != -1;
}
private void swap(int index1, int index2) {
heapIndexMap.put(nodes[index1], index2);
heapIndexMap.put(nodes[index2], index1);
Node tmp = nodes[index1];
nodes[index1] = nodes[index2];
nodes[index2] = tmp;
}
}
// 改进后的dijkstra算法
// 从head出发,所有head能到达的节点,生成到达每个节点的最小路径记录并返回
public static Map<Node, Integer> dijkstra2(Node head, int size) {
NodeHeap nodeHeap = new NodeHeap(size);
nodeHeap.addOrUpdateOrIgnore(head, 0);
Map<Node, Integer> result = new HashMap<>();
while (!nodeHeap.isEmpty()) {
NodeRecord record = nodeHeap.pop();
Node cur = record.node;
int distance = record.distance;
for (Edge edge : cur.edges) {
nodeHeap.addOrUpdateOrIgnore(edge.to, edge.weight + distance);
}
result.put(cur, distance);
}
return result;
}
}
9. 暴力递归
9.1 汉罗塔问题
public static void func(int n) {
if (n > 0) {
func(n, "from", "to", "other");
}
}
public static void func(int n, String from, String to, String other) {
if (n == 1) {
System.out.println("move 1 from " + from + " to " + to);
} else {
func(n - 1, from, other, to);
System.out.println("move " + n + " from " + from + " to " + to);
func(n - 1, other, to, from);
}
}
9.2 逆序一个栈,不能申请额外的数据结构
//逆序一个栈
public static void reverse(Stack<Integer> stack) {
if (stack.isEmpty()) {
return;
}
int i = f(stack);
reverse(stack);
stack.push(i);
}
//取出栈底的元素
public static int f(Stack<Integer> stack) {
int res = stack.pop();
if (stack.isEmpty()) {
return res;
} else {
int last = f(stack);
stack.add(res);
return last;
}
}
从左往右的尝试模型
9.3 打印一个字符串的全部子序列
import java.util.*;
public class Main {
//打印一个字符串的全部子序列
public static List<String> subs(String s) {
char[] chars = s.toCharArray();
String path = "";
List<String> ans = new ArrayList<>();
process1(chars, 0, ans, "");
return ans;
}
private static void process1(char[] chars, int idx, List<String> ans, String path) {
if (idx == chars.length) {
ans.add(path);
return;
}
String no = path;
process1(chars, idx + 1, ans, no);
String yes = path + String.valueOf(chars[idx]);
process1(chars, idx + 1, ans, yes);
}
//打印一个字符串的全部子序列且没有重复字面值
public static List<String> subsNoRepeat(String s) {
char[] chars = s.toCharArray();
String path = "";
Set<String> set = new HashSet<>();
process2(chars, 0, set, "");
List<String> ans = new ArrayList<>();
for (String temp : set) {
ans.add(temp);
}
return ans;
}
private static void process2(char[] chars, int idx, Set<String> set, String path) {
if (idx == chars.length) {
set.add(path);
return;
}
String no = path;
process2(chars, idx + 1, set, no);
String yes = path + String.valueOf(chars[idx]);
process2(chars, idx + 1, set, yes);
}
}
9.4 打印一个字符串的全部排列
import java.util.*;
public class Main {
//打印一个字符串的全部排列
public static List<String> permutation(String s) {
List<String> ans = new ArrayList<>();
if (s == null || s.length() == 0) {
return ans;
}
char[] chars = s.toCharArray();
process1(chars, 0, ans);
return ans;
}
private static void process1(char[] chars, int idx, List<String> ans) {
if (idx == chars.length) {
ans.add(String.valueOf(chars));
return;
}
for (int i = idx; i < chars.length; i++) {
swap(chars, idx, i);
process1(chars, idx + 1, ans);
swap(chars, idx, i);
}
}
//打印一个字符串的全部排列,且字面值无重复
public static List<String> permutationNoRepeat(String s) {
List<String> ans = new ArrayList<>();
if (s == null || s.length() == 0) {
return ans;
}
char[] chars = s.toCharArray();
process2(chars, 0, ans);
return ans;
}
private static void process2(char[] chars, int idx, List<String> ans) {
if (idx == chars.length) {
ans.add(String.valueOf(chars));
return;
}
boolean[] visited = new boolean[26];
for (int i = idx; i < chars.length; i++) {
if (!visited[chars[i] - 'a']) {
visited[chars[i] - 'a'] = true;
swap(chars, i, idx);
process2(chars, idx + 1, ans);
swap(chars, i, idx);
}
}
}
private static void swap(char[] chars, int i, int j) {
char temp = chars[i];
chars[i] = chars[j];
chars[j] = temp;
}
}
9.5 编码转化
public class Main {
//递归
public static int number1(String s) {
if (s == null || s.length() == 0) {
return 0;
}
return process(s.toCharArray(), 0);
}
private static int process(char[] chars, int idx) {
if (idx == chars.length) {
return 1;
}
if (chars[idx] == '0') {
return 0;
}
if (chars[idx] == '1') {
int res = process(chars, idx + 1);
if (idx + 1 < chars.length) {
res += process(chars, idx + 2);
}
return res;
}
if (chars[idx] == '2') {
int res = process(chars, idx + 1);
if (idx + 1 < chars.length && chars[idx + 1] >= '0' && chars[idx] <= '6') {
res += process(chars, idx + 2);
}
return res;
}
return process(chars, idx + 1);
}
//动态规划
public static int number2(String s) {
if (s == null || s.length() == 0) {
return 0;
}
char[] chars = s.toCharArray();
int n = chars.length;
int[] dp = new int[n + 1];
dp[n] = 1;
for (int i = n - 1; i >= 0; i--) {
if (chars[i] == '0') {
dp[i] = 0;
} else if (chars[i] == '1') {
dp[i] = dp[i + 1];
if (i + 1 < n) {
dp[i] += dp[i + 2];
}
} else if (chars[i] == '2') {
dp[i] = dp[i + 1];
if (i + 1 < chars.length && chars[i + 1] >= '0' && chars[i] <= '6') {
dp[i] += dp[i + 2];
}
} else {
dp[i] = dp[i + 1];
}
}
return dp[0];
}
public static void main(String[] args) {
System.out.println(number1("111"));
System.out.println(number2("111"));
}
}
9.6 背包问题: 给定两个长度为n的数组weight和values,给定一个载重为正数的袋子bag,最多能装的价值为多少
public class Main {
//方式一 暴力回溯
public static int getMaxValue1(int[] w, int[] v, int bag) {
return process1(w, v, 0, 0, bag);
}
private static int process1(int[] w, int[] v, int idx, int alreadyW, int bag) {
if (alreadyW > bag) {
return -1;
}
if (idx == w.length) {
return 0;
}
int p1 = process1(w, v, idx + 1, alreadyW, bag);
int p2Next = process1(w, v, idx + 1, alreadyW + w[idx], bag);
int p2 = -1;
if (p2Next != -1) {
p2 = p2Next + v[idx];
}
return Math.max(p1, p2);
}
//方式二 暴力回溯
public static int getMaxValue2(int[] w, int[] v, int bag) {
return process2(w, v, 0, bag);
}
private static int process2(int[] w, int[] v, int idx, int rest) {
if (rest < 0) {
return -1;
}
if (idx == w.length) {
return 0;
}
int p1 = process2(w, v, idx + 1, rest);
int p2Next = process2(w, v, idx + 1, rest - w[idx]);
int p2 = -1;
if (p2Next != -1) {
p2 = p2Next + v[idx];
}
return Math.max(p1, p2);
}
//方式三 动态规划(方式二改)
public static int getMaxValue3(int[] w, int[] v, int bag) {
int n = w.length;
int[][] dp = new int[n + 1][bag + 1];
for (int idx = n - 1; idx >= 0; idx--) {
for (int res = 1; res <= bag; res++) {
dp[idx][res] = dp[idx + 1][res];
if (res >= w[idx]) {
dp[idx][res] = Math.max(dp[idx][res], v[idx] + dp[idx + 1][res - w[idx]]);
}
}
}
return dp[0][bag];
}
public static void main(String[] args) {
int[] weights = {3, 2, 4, 7};
int[] values = {5, 6, 3, 19};
int bag = 11;
System.out.println(getMaxValue1(weights, values, bag));
System.out.println(getMaxValue2(weights, values, bag));
System.out.println(getMaxValue3(weights, values, bag));
}
}
范围上尝试模型
9.7 给定一个数组arr,玩家A和玩家B每次只能拿最左或者最右的纸牌,玩家A和B都绝顶聪明,返回最后获胜者的分数
public class Main {
public static int win1(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
return Math.max(f(arr, 0, arr.length - 1), s(arr, 0, arr.length - 1));
}
public static int f(int[] arr, int i, int j) {
if (i == j) {
return arr[i];
}
return Math.max(arr[i] + s(arr, i + 1, j), arr[j] + s(arr, i, j - 1));
}
public static int s(int[] arr, int i, int j) {
if (i == j) {
return 0;
}
return Math.min(f(arr, i + 1, j), f(arr, i, j - 1));
}
public static int win2(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
int n = arr.length;
int[][] f = new int[n + 1][n + 1];
int[][] s = new int[n + 1][n + 1];
for (int r = 0; r < n; r++) {
f[r][r] = arr[r];
for (int l = r - 1; l >= 0; l--) {
f[l][r] = Math.max(arr[l] + s[l + 1][r], arr[r] + s[l][r - 1]);
s[l][r] = Math.min(f[l + 1][r], f[l][r - 1]);
}
}
return Math.max(f[0][n - 1], s[0][n - 1]);
}
public static void main(String[] args) {
int[] arr = {1, 9, 1};
System.out.println(win1(arr));
System.out.println(win2(arr));
}
}
9.8 n皇后求最多数量
public class Solution {
public static int totalNQueens(int n) {
if (n == 1) {
return 1;
}
int[] record = new int[n];
return process(0, n, record);
}
//i表示当前在多少行
//record记录的是多少行多少列记录的数据
private static int process(int i, int n, int[] record) {
if (i == n) {
return 1;
}
int res = 0;
for (int j = 0; j < n; j++) {
if (isValid(i, j, record)) {
record[i] = j;
res += process(i + 1, n, record);
}
}
return res;
}
private static boolean isValid(int i, int j, int[] record) {
for (int k = 0; k < i; k++) {
if (record[k] == j || Math.abs(i - k) == Math.abs(record[k] - j)) {
return false;
}
}
return true;
}
public static int totalNQueues2(int n) {
if (n == 1) {
return 1;
}
int limit = n == 32 ? -1 : (1 << n) - 1;
return process2(limit, 0, 0, 0);
}
private static int process2(int limit, int colLimit, int leftLimit, int rightLimit) {
if (colLimit == limit) {
return 1;
}
int pos = limit & (~(colLimit | leftLimit | rightLimit));
int rightOne = 0;
int res = 0;
while (pos != 0) {
rightOne = pos & (~pos + 1);
pos = pos - rightOne;
res += process2(limit,
colLimit | rightOne,
(leftLimit | rightOne) << 1,
(rightLimit | rightOne) >>> 1);
}
return res;
}
}
9.9 机器人走法
假设排成一行的N个位置,记为1~N,N一定大于等于2
开始时机器人在其中的M位置上(M一定是1~N中的一个)
如果机器人来到1位置,那么下一步只能向右走到2位置
如果机器人来到N位置,那么下一步只能向左来到N-1位置
如果机器人来到中间位置,那么下一步既可以向左走,也可以向右走
规定机器人必须走K步,最终能来到P位置(P也是1~N中的一个)的方法有多少种
给定四个参数N,M,K,P,返回方法数
public class Main {
//n: 位置为1~n,固定参数
//cur: 当前在cur位置,可变参数
//res: 剩余还有多少步没有走
//p: 最终要到达的位置
//方式1: 暴力回溯
public static int ways1(int n, int cur, int res, int p) {
if (n < 2 || res < 1 || cur > n || p < 1 || p > n) {
return 0;
}
return walk1(n, cur, res, p);
}
private static int walk1(int n, int cur, int res, int p) {
if (res == 0) {
return cur == p ? 1 : 0;
}
if (cur == 1) {
return walk1(n, cur + 1, res - 1, p);
}
if (cur == n) {
return walk1(n, cur - 1, res - 1, p);
}
return walk1(n, cur + 1, res - 1, p) + walk1(n, cur - 1, res - 1, p);
}
//方式二: 暴力回溯+缓存 记忆化搜索下的动态规划
public static int ways2(int n, int cur, int res, int p) {
if (n < 2 || res < 1 || cur > n || p < 1 || p > n) {
return 0;
}
int[][] dp = new int[res + 1][n + 1];
for (int i = 0; i <= res; i++) {
for (int j = 0; j <= n; j++) {
dp[i][j] = -1;
}
}
return walk2(n, cur, res, p, dp);
}
private static int walk2(int n, int cur, int res, int p, int[][] dp) {
if (dp[res][cur] != -1) {
return dp[res][cur];
}
if (res == 0) {
dp[res][cur] = cur == p ? 1 : 0;
return dp[res][cur];
}
if (cur == 1) {
dp[res][cur] = walk2(n, cur + 1, res - 1, p, dp);
return dp[res][cur];
}
if (cur == n) {
dp[res][cur] = walk2(n, cur - 1, res - 1, p, dp);
return dp[res][cur];
}
dp[res][cur] = walk2(n, cur + 1, res - 1, p, dp) + walk2(n, cur - 1, res - 1, p, dp);
return dp[res][cur];
}
//改动态规划
public static int ways3(int n, int cur, int res, int p) {
if (n < 2 || res < 1 || cur > n || p < 1 || p > n) {
return 0;
}
int[][] dp = new int[res + 1][n + 1];
dp[0][p] = 1;
for (int i = 1; i <= res; i++) {
for (int j = 1; j <= n; j++) {
if (j == 1) {
dp[i][j] = dp[i - 1][j + 1];
} else if (j == n) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = dp[i - 1][j + 1] + dp[i - 1][j - 1];
}
}
}
return dp[res][cur];
}
public static void main(String[] args) {
System.out.println(ways1(7, 4, 9, 5));
System.out.println(ways2(7, 4, 9, 5));
System.out.println(ways3(7, 4, 9, 5));
}
}
9.10 硬币合并问题,一共有多少种方式可以凑齐一个数
public class Main {
public static int ways1(int[] arr, int target) {
if (arr == null || arr.length == 0 || target < 0) {
return 0;
}
return process1(arr, 0, target);
}
private static int process1(int[] arr, int idx, int res) {
if (idx == arr.length) {
return res == 0 ? 1 : 0;
}
int ways = 0;
for (int zhang = 0; zhang * arr[idx] <= res; zhang++) {
ways += process1(arr, idx + 1, res - zhang * arr[idx]);
}
return ways;
}
public static int ways2(int[] arr, int target) {
if (arr == null || arr.length == 0 || target < 0) {
return 0;
}
int[][] dp = new int[arr.length + 1][target + 1];
for (int i = 0; i < dp.length; i++) {
for (int j = 0; j < dp[0].length; j++) {
dp[i][j] = -1;
}
}
return process2(arr, 0, target, dp);
}
private static int process2(int[] arr, int idx, int res, int[][] dp) {
if (dp[idx][res] != -1) {
return dp[idx][res];
}
if (idx == arr.length) {
dp[idx][res] = res == 0 ? 1 : 0;
return dp[idx][res];
}
int ways = 0;
for (int zhang = 0; zhang * arr[idx] <= res; zhang++) {
ways += process2(arr, idx + 1, res - zhang * arr[idx], dp);
}
dp[idx][res] = ways;
return ways;
}
public static int waysdp1(int[] arr, int target) {
if (arr == null || arr.length == 0 || target < 0) {
return 0;
}
int n = arr.length;
int[][] dp = new int[n + 1][target + 1];
dp[n][0] = 1;
for (int idx = n - 1; idx >= 0; idx--) {
for (int res = 0; res <= target; res++) {
int ways = 0;
for (int zhang = 0; zhang * arr[idx] <= res; zhang++) {
ways += dp[idx + 1][res - zhang * arr[idx]];
}
dp[idx][res] = ways;
}
}
return dp[0][target];
}
public static int waysdp2(int[] arr, int target) {
if (arr == null || arr.length == 0 || target < 0) {
return 0;
}
int n = arr.length;
int[][] dp = new int[n + 1][target + 1];
dp[n][0] = 1;
for (int i = n - 1; i >= 0; i--) {
for (int res = 0; res <= target; res++) {
dp[i][res] = dp[i + 1][res];
if (res - arr[i] >= 0) {
dp[i][res] += dp[i][res - arr[i]];
}
}
}
return dp[0][target];
}
public static void main(String[] args) {
int[] arr = {5, 10, 50, 100};
int sum = 1000;
System.out.println(ways1(arr, sum));
System.out.println(ways2(arr, sum));
System.out.println(waysdp1(arr, sum));
System.out.println(waysdp2(arr, sum));
}
}
9.11 剪贴纸
给定一个字符串str,给定一个字符串类型的数组arr.
arr里的每一个字符串,代表一张贴纸,你可以把打个字符剪开使用,目的是拼出str来
返回至少需要多少张贴纸可以完成这个任务,每一张贴纸可以重复使用
例子: str="babac", arr={"ba","c","abcd"}
至少需要两张贴纸"ba"和"abcd",因为使用这两张贴纸,把每一个字符单独剪开,含有2个a,2个b,1个c.是可以拼出str的.所以返回2.
import java.util.HashMap;
import java.util.Map;
public class Main{
public static int minStickers1(String[] stickers, String target) {
int n = stickers.length;
int[][] arr = new int[n][26];
Map<String, Integer> map = new HashMap<>();
for (int i = 0; i < n; i++) {
char[] chars = stickers[i].toCharArray();
for (char c : chars) {
arr[i][c - 'a']++;
}
}
map.put("", 0);
return process1(arr, map, target);
}
private static int process1(int[][] arr, Map<String, Integer> map, String s) {
if (map.containsKey(s)) {
return map.get(s);
}
int ans = Integer.MAX_VALUE;
int n = arr.length;
int[] tmap = new int[26];
char[] chars = s.toCharArray();
for (char c : chars) {
tmap[c - 'a']++;
}
for (int i = 0; i < n; i++) {
if (arr[i][chars[0] - 'a'] == 0) {
continue;
}
StringBuilder builder = new StringBuilder();
for (int j = 0; j < 26; j++) {
if (tmap[j] > 0) {
for (int k = 0; k < Math.max(0, tmap[j] - arr[i][j]); k++) {
builder.append((char) ('a' + j));
}
}
}
String s1 = builder.toString();
int tmp = process1(arr, map, s1);
if (tmp != -1) {
ans = Math.min(ans, 1 + tmp);
}
}
map.put(s, ans == Integer.MAX_VALUE ? 0 : ans);
return map.get(s);
}
public static void main(String[] args) {
String str="babac";
String[] arr = {"ba", "c", "abcd"};
System.out.println(minStickers1(arr, str));
}
}
多样本位置全对应的尝试模型
9.12 两个字符串的最长公共子序列问题
public class Main{
public static int longestCommonSubsequence(String text1, String text2) {
char[] str1 = text1.toCharArray();
char[] str2 = text2.toCharArray();
int[][] dp = new int[str1.length][str2.length];
dp[0][0] = str1[0] == str2[0] ? 1 : 0;
for (int i = 1; i < str1.length; i++) {
dp[i][0] = Math.max(dp[i - 1][0], str1[i] == str2[0] ? 1 : 0);
}
for (int i = 1; i < str2.length; i++) {
dp[0][i] = Math.max(dp[0][i - 1], str1[0] == str2[i] ? 1 : 0);
}
for (int i = 1; i < str1.length; i++) {
for (int j = 1; j < str2.length; j++) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
if (str1[i] == str2[j]) {
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - 1] + 1);
}
}
}
return dp[str1.length - 1][str2.length - 1];
}
public static void main(String[] args) {
String s1 = "wks";
String s2 = "awkysw";
System.out.println(longestCommonSubsequence(s1, s2));
}
}
寻找业务限制的尝试模型
9.13 洗咖啡杯
给定一个数组,代表每个人喝完咖啡准备洗杯子的时间
只有一台咖啡机,一次只能洗一个杯子,时间消耗a,洗完才能洗下一个杯子
每个咖啡杯也可以自己挥发干净,时间消耗b,咖啡杯可以并行挥发
返回让所有咖啡杯变干净的最早完成时间
三个参数: int[] arr,int a,int b
public class Main {
public static int process1(int[] drinks, int a, int b, int idx, int washLine) {
if (idx == drinks.length - 1) {
return Math.min(Math.max(drinks[idx], washLine) + a, drinks[idx] + b);
}
int wash = Math.max(drinks[idx], washLine) + a;
int next1 = process1(drinks, a, b, idx + 1, wash);
int p1 = Math.max(wash, next1);
int dir = drinks[idx] + b;
int next2 = process1(drinks, a, b, idx + 1, washLine);
int p2 = Math.max(dir, next2);
return Math.min(p1, p2);
}
public static int dp(int[] drinks, int a, int b) {
if (a >= b) {
return drinks[drinks.length - 1] + b;
}
int n = drinks.length;
int limit = 0;
for (int i = 0; i < n; i++) {
limit = Math.max(limit, drinks[i] + a);
}
int[][] dp = new int[n][limit + 1];
for (int washLine = 0; washLine <= limit; washLine++) {
dp[n - 1][washLine] = Math.min(Math.max(drinks[n - 1], washLine) + a, drinks[n - 1] + b);
}
for (int idx = n - 2; idx >= 0; idx--) {
for (int washLine = 0; washLine <= limit; washLine++) {
int wash = Math.max(drinks[idx], washLine) + a;
int p1 = Integer.MAX_VALUE;
if (wash <= limit) {
p1 = Math.max(wash, dp[idx + 1][wash]);
}
int p2 = Math.max(drinks[idx] + b, dp[idx + 1][washLine]);
dp[idx][washLine] = Math.min(p1, p2);
}
}
return dp[0][0];
}
public static void main(String[] args) {
int[] arr = {1, 1, 5, 5, 7, 10, 12, 12, 12, 12, 12, 12, 15};
int a = 3;
int b = 10;
System.out.println(process1(arr, a, b, 0, 0));
System.out.println(dp(arr, a, b));
}
}