code[c7-c12]

目录

 

class07

class08

class09

class10

class11

class12


class07

package class07;

public class Code01_RecursiveTraversalBT {

	public static class Node {
		public int value;
		public Node left;
		public Node right;

		public Node(int v) {
			value = v;
		}
	}

	public static void f(Node head) {
		if (head == null) {
			return;
		}
		/**此处打印是先序遍历*/
//		System.out.println(head.value);
		f(head.left);
		/**此处打印是中序遍历*/
//		System.out.println(head.value);
		f(head.right);
		/**此处打印是后序遍历*/
//		System.out.println(head.value);
	}

	public static void pre(Node head) {
		if (head == null) {
			return;
		}
		System.out.println(head.value);
		pre(head.left);
		pre(head.right);
	}

	public static void in(Node head) {
		if (head == null) {
			return;
		}
		in(head.left);
		System.out.println(head.value);
		in(head.right);
	}

	public static void pos(Node head) {
		if (head == null) {
			return;
		}
		pos(head.left);
		pos(head.right);
		System.out.println(head.value);
	}

	public static void main(String[] args) {
		Node head = new Node(1);
		head.left = new Node(2);
		head.right = new Node(3);
		head.left.left = new Node(4);
		head.left.right = new Node(5);
		head.right.left = new Node(6);
		head.right.right = new Node(7);

		pre(head);
		System.out.println("========");
		in(head);
		System.out.println("========");
		pos(head);
		System.out.println("========");

	}

}
package class07;

import java.util.Stack;

public class Code02_UnRecursiveTraversalBT {

    public static class Node {
        public int value;
        public Node left;
        public Node right;

        public Node(int v) {
            value = v;
        }
    }

    /**
     * 先序遍历
     *
     * @param head
     */
    public static void pre(Node head) {
        System.out.print("pre-order: ");
        if (head != null) {
            Stack<Node> stack = new Stack<Node>();
            stack.add(head);
            while (!stack.isEmpty()) {
                head = stack.pop();
                System.out.print(head.value + " ");
                if (head.right != null) {
                    stack.push(head.right);
                }
                if (head.left != null) {
                    stack.push(head.left);
                }
            }
        }
        System.out.println();
    }

    /**
     * 中序遍历
     *
     * @param head
     */
    public static void in(Node head) {
        System.out.print("in-order: ");
        if (head != null) {
            Stack<Node> stack = new Stack<Node>();
            while (!stack.isEmpty() || head != null) {
                if (head != null) {
                    //节点存在->将节点入栈-跳左子树
                    stack.push(head);
                    head = head.left;
                } else {
                    //左节点不存在->弹出栈顶并打印-跳栈顶元素的右节点
                    head = stack.pop();
                    System.out.print(head.value + " ");
                    head = head.right;
                }
            }
        }
        System.out.println();
    }

    /**
     * 后续遍历
     * 使用2个栈实现
     * 通过s2这个栈存储中间结果
     *
     * @param head
     */
    public static void pos1(Node head) {
        System.out.print("pos-order: ");
        if (head != null) {
            Stack<Node> s1 = new Stack<Node>();
            Stack<Node> s2 = new Stack<Node>();
            s1.push(head);
            while (!s1.isEmpty()) {
                head = s1.pop();
                s2.push(head);
                if (head.left != null) {
                    s1.push(head.left);
                }
                if (head.right != null) {
                    s1.push(head.right);
                }
            }
            while (!s2.isEmpty()) {
                System.out.print(s2.pop().value + " ");
            }
        }
        System.out.println();
    }

    /**
     * 后续遍历 炫技版
     * 使用1个栈实现
     */
    public static void pos2(Node h) {
        System.out.print("pos-order: ");
        if (h != null) {
            Stack<Node> stack = new Stack<Node>();
            stack.push(h);
            Node c = null;
            while (!stack.isEmpty()) {
                c = stack.peek();
                /**
                 * h记录上一次打印的点
                 * 如果当前节点c的左树被处理过,h是当前节点的左孩子
                 * 如果当前节点c的右树被处理过,h是当前节点的右孩子,并且左树在之前已经被处理过了
                 */
                // 上次打印的不是当前节点的左孩子和右孩子,说明当前节点之前没被处理过,处理左树
                // 判断左树有没有处理过:当上一次打印的点是当前节点的右孩子时,即h==c.right,左树肯定处理过了,因为是左右中的后续遍历
                if (c.left != null && h != c.left && h != c.right) {
                    stack.push(c.left);
                }
                // 左树处理完了,h不是当前节点的右孩子,说明右树没被处理过,处理右树
                else if (c.right != null && h != c.right) {
                    stack.push(c.right);
                }
                // 左右树都处理完了,处理自己即打印
                else {
                    System.out.print(stack.pop().value + " ");
                    // h记录上一次打印的点
                    h = c;
                }
            }
        }
        System.out.println();
    }

    public static void main(String[] args) {
        Node head = new Node(1);
        head.left = new Node(2);
        head.right = new Node(3);
        head.left.left = new Node(4);
        head.left.right = new Node(5);
        head.right.left = new Node(6);
        head.right.right = new Node(7);

        pre(head);
        System.out.println("========");
        in(head);
        System.out.println("========");
        pos1(head);
        System.out.println("========");
        pos2(head);
        System.out.println("========");
    }

}
package class07;

import java.util.LinkedList;
import java.util.Queue;

public class Code03_LevelTraversalBT {

	public static class Node {
		public int value;
		public Node left;
		public Node right;

		public Node(int v) {
			value = v;
		}
	}

	public static void level(Node head) {
		if (head == null) {
			return;
		}
		Queue<Node> queue = new LinkedList<>();
		queue.add(head);
		while (!queue.isEmpty()) {
			Node cur = queue.poll();
			System.out.println(cur.value);
			if (cur.left != null) {
				queue.add(cur.left);
			}
			if (cur.right != null) {
				queue.add(cur.right);
			}
		}
	}

	public static void main(String[] args) {
		Node head = new Node(1);
		head.left = new Node(2);
		head.right = new Node(3);
		head.left.left = new Node(4);
		head.left.right = new Node(5);
		head.right.left = new Node(6);
		head.right.right = new Node(7);

		level(head);
		System.out.println("========");
	}

}
package class07;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

public class Code04_SerializeAndReconstructTree {

    public static class Node {
        public int value;
        public Node left;
        public Node right;

        public Node(int data) {
            this.value = data;
        }
    }

    /**
     * 先序遍历的序列化
     */
    public static Queue<String> preSerial(Node head) {
        Queue<String> ans = new LinkedList<>();
        pres(head, ans);
        return ans;
    }

    public static void pres(Node head, Queue<String> ans) {
        if (head == null) {
            ans.add(null);// 不存在则放入null
        } else {
            // 中-左-右
            ans.add(String.valueOf(head.value));//存在则放入节点的值
            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);
    }

    public static Node preb(Queue<String> prelist) {
        String value = prelist.poll();
        if (value == null) {
            return null;
        }
        Node head = new Node(Integer.valueOf(value));
        head.left = preb(prelist);//prelist已经弹出一个了,当前prelist头部是head的左节点的序列化值
        head.right = preb(prelist);
        return head;
    }

    /**
     * 后续遍历(左右中)的反序列化
     */
    public static Node buildByPostQueue(Queue<String> postList){
        if (postList==null||postList.size()==0){
            return null;
        }
        // 左右中-》栈-》栈中顺序 中右左
        Stack<String> stack = new Stack<>();
        while (!postList.isEmpty()){
            stack.push(postList.poll());
        }
        return posb(stack);
    }

    public static Node posb(Stack<String> postStack){
        String value = postStack.pop();
        if (value==null){
            return null;
        }
        Node head = new Node(Integer.parseInt(value));// 中
        head.right = posb(postStack);// 右
        head.left = posb(postStack);// 左
        return head;
    }

    /**
     * 按层序列化
     */
    public static Queue<String> levelSerial(Node head) {
        Queue<String> syncResult = new LinkedList<>();//保存序列化结果
        if (head == null) {
            syncResult.add(null);//节点为空,序列化保存为null
            // 节点不存在,不入辅助队列queue
        } else {
            syncResult.add(String.valueOf(head.value));//节点不为空,序列化结果为节点值
            Queue<Node> queue = new LinkedList<Node>();
            queue.add(head);//节点存在则入队列[不存在则不入队列]
            while (!queue.isEmpty()) {
                head = queue.poll();
                // 左/右节点存在,同时入 序列化结果队列 和 辅助队列
                // 左/右节点不存在,只入 序列化结果队列,不入 辅助队列
                if (head.left != null) {
                    syncResult.add(String.valueOf(head.left.value));
                    queue.add(head.left);
                } else {
                    syncResult.add(null);
                }
                if (head.right != null) {
                    syncResult.add(String.valueOf(head.right.value));
                    queue.add(head.right);
                } else {
                    syncResult.add(null);
                }
            }
        }
        return syncResult;
    }

    /**
     * 按层反序列化
     */
    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<Node>();
        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;
    }

    public static Node generateNode(String val) {
        if (val == null) {
            return null;
        }
        return new Node(Integer.valueOf(val));
    }

    // 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;
    }

    // for test
    public static boolean isSameValueStructure(Node head1, Node head2) {
        if (head1 == null && head2 != null) {
            return false;
        }
        if (head1 != null && head2 == null) {
            return false;
        }
        if (head1 == null && head2 == null) {
            return true;
        }
        if (head1.value != head2.value) {
            return false;
        }
        return isSameValueStructure(head1.left, head2.left) && isSameValueStructure(head1.right, head2.right);
    }

    public static void main(String[] args) {
        int maxLevel = 5;
        int maxValue = 100;
        int testTimes = 1000000;
        for (int i = 0; i < testTimes; i++) {
            Node head = generateRandomBST(maxLevel, maxValue);
            Queue<String> pre = preSerial(head);
            Queue<String> level = levelSerial(head);
            Node preBuild = buildByPreQueue(pre);
            Node levelBuild = buildByLevelQueue(level);
            if (!isSameValueStructure(preBuild, levelBuild)) {
                System.out.println("Oops!");
            }
        }
        System.out.println("finish!");
    }
}
package class07;

public class Code05_PrintBinaryTree {

	public static class Node {
		public int value;
		public Node left;
		public Node right;

		public Node(int data) {
			this.value = data;
		}
	}

	public static void printTree(Node head) {
		System.out.println("Binary Tree:");
		printInOrder(head, 0, "H", 17);
		System.out.println();
	}

	/**
	 *
	 * @param head
	 * @param height 当前节点在第几层
	 * @param to 值两边的符号,如v36v代表36从直观的结果在某个节点的上面,在二叉树上即某个节点的右节点
	 * @param len 数值部分所占的长度,固定17
	 */
	public static void printInOrder(Node head, int height, String to, int len) {
		if (head == null) {
			return;
		}
		//处理右节点
		printInOrder(head.right, height + 1, "v", len);

		//处理自己,即如何打印当前节点
		String val = to + head.value + to;// 给当前值前后加符号
		int lenM = val.length();//值的长度[在中间]
		int lenL = (len - lenM) / 2;//值首位的前面需要补多少空格
		int lenR = len - lenM - lenL;//值末位的后面需要补多少空格
		val = getSpace(lenL) + val + getSpace(lenR);// 空格+值+空格,总长度17
		// 在整个长度17的值前面需要根据节点层数再补空格
		// 头节点左边不需要补,因为头节点长度17,所以第一层节点左边补1*17,又第一层节点+第二层节点长度=17*2,所以第二层节点左边补2*17...
		System.out.println(getSpace(height * len) + val);

		// 处理左节点
		printInOrder(head.left, height + 1, "^", len);
	}

	public static String getSpace(int num) {
		String space = " ";
		StringBuffer buf = new StringBuffer("");
		for (int i = 0; i < num; i++) {
			buf.append(space);
		}
		return buf.toString();
	}

	public static void main(String[] args) {
		Node head = new Node(1);
		head.left = new Node(-222222222);
		head.right = new Node(3);
		head.left.left = new Node(Integer.MIN_VALUE);
		head.right.left = new Node(55555555);
		head.right.right = new Node(66);
		head.left.left.right = new Node(777);
		printTree(head);

		head = new Node(1);
		head.left = new Node(2);
		head.right = new Node(3);
		head.left.left = new Node(4);
		head.right.left = new Node(5);
		head.right.right = new Node(6);
		head.left.left.right = new Node(7);
		printTree(head);

		head = new Node(1);
		head.left = new Node(1);
		head.right = new Node(1);
		head.left.left = new Node(1);
		head.right.left = new Node(1);
		head.right.right = new Node(1);
		head.left.left.right = new Node(1);
		printTree(head);

	}

}
package class07;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Queue;

public class Code06_TreeMaxWidth {

	public static class Node {
		public int value;
		public Node left;
		public Node right;

		public Node(int data) {
			this.value = data;
		}
	}

	/**
     * 求数的最大宽度
	 * 即求树的节点数最多的层有多少节点
	 * 队列 + hashmap 实现
	 */
	public static int maxWidthUseMap(Node head) {
		if (head == null) {
			return 0;
		}
		Queue<Node> queue = new LinkedList<>();
		queue.add(head);
		// <节点,节点层数>
		HashMap<Node, Integer> levelMap = new HashMap<>();
		levelMap.put(head, 1);
		int curLevel = 1;// 记录当前是第几层
		int curLevelNodes = 0;// 记录当前层节点数
		int max = 0;// 节点最多的层的节点数
		while (!queue.isEmpty()) {
			Node cur = queue.poll();
			int curNodeLevel = levelMap.get(cur);
			if (cur.left != null) {
				levelMap.put(cur.left, curNodeLevel + 1);
				queue.add(cur.left);
			}
			if (cur.right != null) {
				levelMap.put(cur.right, curNodeLevel + 1);
				queue.add(cur.right);
			}
			// 没有跳层,当前层节点数++
			if (curNodeLevel == curLevel) {
				curLevelNodes++;
			} else {
				// 来到新层时结算上一层、由于是首次来到新层,层数+1,新层节点数=1
				max = Math.max(max, curLevelNodes);
				curLevel++;
				curLevelNodes = 1;
			}
		}
		// 结算最后一层
		max = Math.max(max, curLevelNodes);
		return max;
	}

	public static int maxWidthNoMap(Node head) {
		if (head == null) {
			return 0;
		}
		Queue<Node> queue = new LinkedList<>();
		queue.add(head);
		Node curEnd = head;
		Node nextEnd = null;
		int max = 0;
		int curLevelNodes = 0;
		while (!queue.isEmpty()) {
			Node cur = queue.poll();
			if (cur.left != null) {
				queue.add(cur.left);
				nextEnd = cur.left;
			}
			if (cur.right != null) {
				queue.add(cur.right);
				nextEnd = cur.right;
			}
			curLevelNodes++;
			if (cur == curEnd) {
				max = Math.max(max, curLevelNodes);
				curLevelNodes = 0;
				curEnd = nextEnd;
			}
		}
		return max;
	}

	// 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!");

	}

}
package class07;

public class Code07_SuccessorNode {

	public static class Node {
		public int value;
		public Node left;
		public Node right;
		public Node parent;// 指向父节点的指针!

		public Node(int data) {
			this.value = data;
		}
	}

	public static Node getSuccessorNode(Node node) {
		if (node == null) {
			return node;
		}
		if (node.right != null) {
			return getLeftMost(node.right);
		} else { // 无右子树
			Node parent = node.parent;
			while (parent != null && parent.left != node) { // 当前节点是其父亲节点右孩子
				node = parent;
				parent = node.parent;
			}
			// 当给定的node是整棵树最右节点时,它一直向上找都找不到自己是谁的左孩子,直到上面while循环头节点的parent==null跳出循环返回空
			return parent;
		}
	}

	public static Node getLeftMost(Node node) {
		if (node == null) {
			return node;
		}
		while (node.left != null) {
			node = node.left;
		}
		return node;
	}

	public static void main(String[] args) {
		Node head = new Node(6);
		head.parent = null;
		head.left = new Node(3);
		head.left.parent = head;
		head.left.left = new Node(1);
		head.left.left.parent = head.left;
		head.left.left.right = new Node(2);
		head.left.left.right.parent = head.left.left;
		head.left.right = new Node(4);
		head.left.right.parent = head.left;
		head.left.right.right = new Node(5);
		head.left.right.right.parent = head.left.right;
		head.right = new Node(9);
		head.right.parent = head;
		head.right.left = new Node(8);
		head.right.left.parent = head.right;
		head.right.left.left = new Node(7);
		head.right.left.left.parent = head.right.left;
		head.right.right = new Node(10);
		head.right.right.parent = head.right;

		Node test = head.left.left;
		System.out.println(test.value + " next: " + getSuccessorNode(test).value);
		test = head.left.left.right;
		System.out.println(test.value + " next: " + getSuccessorNode(test).value);
		test = head.left;
		System.out.println(test.value + " next: " + getSuccessorNode(test).value);
		test = head.left.right;
		System.out.println(test.value + " next: " + getSuccessorNode(test).value);
		test = head.left.right.right;
		System.out.println(test.value + " next: " + getSuccessorNode(test).value);
		test = head;
		System.out.println(test.value + " next: " + getSuccessorNode(test).value);
		test = head.right.left.left;
		System.out.println(test.value + " next: " + getSuccessorNode(test).value);
		test = head.right.left;
		System.out.println(test.value + " next: " + getSuccessorNode(test).value);
		test = head.right;
		System.out.println(test.value + " next: " + getSuccessorNode(test).value);
		test = head.right.right; // 10's next is null
		System.out.println(test.value + " next: " + getSuccessorNode(test));
	}

}
package class07;

public class Code08_PaperFolding {

	public static void printAllFolds(int N) {
		printProcess(1, N, true);
	}

	/**
	 * 递归过程,来到了某一个节点
	 * @param i 节点的层数
	 * @param N 对折次数,也是折痕映射成树的层数
	 * @param down down == true  凹    down == false 凸
	 */
	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);
	}

	public static void main(String[] args) {
		int N = 3;
		printAllFolds(N);
	}
}

class08

package class08;

public class Code01_IsBalanced {

	public static class Node {
		public int value;
		public Node left;
		public Node right;

		public Node(int data) {
			this.value = data;
		}
	}

	public static boolean isBalanced1(Node head) {
		boolean[] ans = new boolean[1];
		ans[0] = true;
		process1(head, ans);
		return ans[0];
	}

	public static int process1(Node head, boolean[] ans) {
		if (!ans[0] || head == null) {
			return -1;
		}
		int leftHeight = process1(head.left, ans);
		int rightHeight = process1(head.right, ans);
		if (Math.abs(leftHeight - rightHeight) > 1) {
			ans[0] = false;
		}
		return Math.max(leftHeight, rightHeight) + 1;
	}

	public static boolean isBalanced2(Node head) {
		return process2(head).isBalaced;
	}

	// 需要左右节点返回的信息集合
	public static class Info {
		public boolean isBalaced;
		public int height;

		public Info(boolean b, int h) {
			isBalaced = b;
			height = h;
		}
	}
	// 给定节点,返回该节点携带的信息
	public static Info process2(Node head) {
		if (head == null) {
			return new Info(true, 0);
		}
		Info leftInfo = process2(head.left);
		Info rightInfo = process2(head.right);
		int height = Math.max(leftInfo.height, rightInfo.height) + 1;
		boolean isBalanced = true;
		/**
		 * 平衡:左树平衡+右树平衡+左右树高度差<=1
		 */
		if (!leftInfo.isBalaced || !rightInfo.isBalaced || Math.abs(leftInfo.height - rightInfo.height) > 1) {
			isBalanced = false;
		}
		return new Info(isBalanced, height);
	}

	// 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 = 5;
		int maxValue = 100;
		int testTimes = 1000000;
		for (int i = 0; i < testTimes; i++) {
			Node head = generateRandomBST(maxLevel, maxValue);
			if (isBalanced1(head) != isBalanced2(head)) {
				System.out.println("Oops!");
			}
		}
		System.out.println("finish!");
	}

}
package class08;

public class Code02_IsFull {

	public static class Node {
		public int value;
		public Node left;
		public Node right;

		public Node(int data) {
			this.value = data;
		}
	}

	public static boolean isFull1(Node head) {
		if (head == null) {
			return true;
		}
		int height = h(head);
		int nodes = n(head);
		return (1 << height) - 1 == nodes;
	}

	public static int h(Node head) {
		if (head == null) {
			return 0;
		}
		return Math.max(h(head.left), h(head.right)) + 1;
	}

	public 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 (1 << all.height) - 1 == all.nodes;
	}

	public static class Info {
		public int height;
		public int nodes;

		public Info(int h, int n) {
			height = h;
			nodes = n;
		}
	}

	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 height = Math.max(leftInfo.height, rightInfo.height) + 1;
		int nodes = leftInfo.nodes + rightInfo.nodes + 1;
		return new Info(height, nodes);
	}

	// 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 = 5;
		int maxValue = 100;
		int testTimes = 1000000;
		for (int i = 0; i < testTimes; i++) {
			Node head = generateRandomBST(maxLevel, maxValue);
			if (isFull1(head) != isFull2(head)) {
				System.out.println("Oops!");
			}
		}
		System.out.println("finish!");
	}

}
package class08;

import java.util.ArrayList;

public class Code03_IsBST {

	public static class Node {
		public int value;
		public Node left;
		public Node right;

		public Node(int data) {
			this.value = data;
		}
	}

	public static boolean isBST1(Node head) {
		if (head == null) {
			return true;
		}
		ArrayList<Node> arr = new ArrayList<>();
		in(head, arr);
		for (int i = 1; i < arr.size(); i++) {
			if (arr.get(i).value <= arr.get(i - 1).value) {
				return false;
			}
		}
		return true;
	}

	public static void in(Node head, ArrayList<Node> arr) {
		if (head == null) {
			return;
		}
		in(head.left, arr);
		arr.add(head);
		in(head.right, arr);
	}

	public static boolean isBST2(Node head) {
		if (head == null) {
			return true;
		}
		return process(head).isBST;
	}

	public static class Info {
		boolean isBST;
		public int min;
		public int max;

		public Info(boolean is, int mi, int ma) {
			isBST = is;
			min = mi;
			max = ma;
		}
	}

	public static Info process(Node head) {
		if (head == null) {
			return null;
		}
		Info leftInfo = process(head.left);
		Info rightInfo = process(head.right);
		int min = head.value;
		int max = head.value;
		if (leftInfo != null) {
			min = Math.min(min, leftInfo.min);
			max = Math.max(max, leftInfo.max);
		}
		if (rightInfo != null) {
			min = Math.min(min, rightInfo.min);
			max = Math.max(max, rightInfo.max);
		}
		boolean isBST = false;
		if (
			(leftInfo == null ? true : (leftInfo.isBST && leftInfo.max < head.value))
			&&
		    (rightInfo == null ? true : (rightInfo.isBST && rightInfo.min > head.value))
		    		) {
			isBST = true;
		}
		return new Info(isBST, min, max);
	}

	// 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 = 4;
		int maxValue = 100;
		int testTimes = 1000000;
		for (int i = 0; i < testTimes; i++) {
			Node head = generateRandomBST(maxLevel, maxValue);
			if (isBST1(head) != isBST2(head)) {
				System.out.println("Oops!");
			}
		}
		System.out.println("finish!");
	}

}
package class08;

import java.util.ArrayList;

/**
 * 给定一棵二叉树的头节点head,
 * 返回这颗二叉树中最大的二叉搜索子树的大小
 */
public class Code04_MaxSubBSTSize {

	public static class Node {
		public int value;
		public Node left;
		public Node right;

		public Node(int data) {
			this.value = data;
		}
	}

	public static int getBSTSize(Node head) {
		if (head == null) {
			return 0;
		}
		ArrayList<Node> arr = new ArrayList<>();
		in(head, arr);
		for (int i = 1; i < arr.size(); i++) {
			if (arr.get(i).value <= arr.get(i - 1).value) {
				return 0;
			}
		}
		return arr.size();
	}

	public static void in(Node head, ArrayList<Node> arr) {
		if (head == null) {
			return;
		}
		in(head.left, arr);
		arr.add(head);
		in(head.right, arr);
	}

	public static int maxSubBSTSize1(Node head) {
		if (head == null) {
			return 0;
		}
		int h = getBSTSize(head);
		if (h != 0) {
			return h;
		}
		return Math.max(maxSubBSTSize1(head.left), maxSubBSTSize1(head.right));
	}

	public static int maxSubBSTSize2(Node head) {
		if (head == null) {
			return 0;
		}
		return process(head).maxSubBSTSize;
	}

	public static class Info {
		public boolean isBST;// 是不是搜索二叉树
		public int maxSubBSTSize;// 最大搜索子树的大小
		public int min;// 树上的最大值
		public int max;// 树上的最小值

		public Info(boolean is, int size, int mi, int ma) {
			isBST = is;
			maxSubBSTSize = size;
			min = mi;
			max = ma;
		}
	}

	public static Info process(Node head) {
		if (head == null) {
			return null;
		}
		Info leftInfo = process(head.left);
		Info rightInfo = process(head.right);

		// 组织自己的4个数据
		int min = head.value;
		int max = head.value;
		int maxSubBSTSize = 0;
		boolean isBST = false;

		if (leftInfo != null) {
			min = Math.min(min, leftInfo.min);
			max = Math.max(max, leftInfo.max);
			maxSubBSTSize = Math.max(maxSubBSTSize, leftInfo.maxSubBSTSize);
		}
		if (rightInfo != null) {
			min = Math.min(min, rightInfo.min);
			max = Math.max(max, rightInfo.max);
			maxSubBSTSize = Math.max(maxSubBSTSize, rightInfo.maxSubBSTSize);
		}
		if (
				(leftInfo == null ? true : (leftInfo.isBST && leftInfo.max < head.value))
				&&
				(rightInfo == null ? true : (rightInfo.isBST && rightInfo.min > head.value))
		) {
			isBST = true;
			maxSubBSTSize = (leftInfo == null ? 0 : leftInfo.maxSubBSTSize)
					+ (rightInfo == null ? 0 : rightInfo.maxSubBSTSize) + 1;
		}
		return new Info(isBST, maxSubBSTSize, min, max);
	}

	// 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 = 4;
		int maxValue = 100;
		int testTimes = 1000000;
		for (int i = 0; i < testTimes; i++) {
			Node head = generateRandomBST(maxLevel, maxValue);
			if (maxSubBSTSize1(head) != maxSubBSTSize2(head)) {
				System.out.println("Oops!");
			}
		}
		System.out.println("finish!");
	}

}
package class08;

import java.util.ArrayList;

/**
 * 给定一棵二叉树的头节点head,
 * 返回这颗二叉树中最大的二叉搜索子树的头节点
 */
public class Code05_MaxSubBSTHead {

	public static class Node {
		public int value;
		public Node left;
		public Node right;

		public Node(int data) {
			this.value = data;
		}
	}

	public static int getBSTSize(Node head) {
		if (head == null) {
			return 0;
		}
		ArrayList<Node> arr = new ArrayList<>();
		in(head, arr);
		for (int i = 1; i < arr.size(); i++) {
			if (arr.get(i).value <= arr.get(i - 1).value) {
				return 0;
			}
		}
		return arr.size();
	}

	public static void in(Node head, ArrayList<Node> arr) {
		if (head == null) {
			return;
		}
		in(head.left, arr);
		arr.add(head);
		in(head.right, arr);
	}

	public static Node maxSubBSTHead1(Node head) {
		if (head == null) {
			return null;
		}
		if (getBSTSize(head) != 0) {
			return head;
		}
		Node leftAns = maxSubBSTHead1(head.left);
		Node rightAns = maxSubBSTHead1(head.right);
		return getBSTSize(leftAns) >= getBSTSize(rightAns) ? leftAns : rightAns;
	}

	public static Node maxSubBSTHead2(Node head) {
		if (head == null) {
			return null;
		}
		return process(head).maxSubBSTHead;
	}

	/**
	 * Info中不用包含左树整体是否时搜索二叉树的条件 boolean isAllBST
	 * 因为可以通过maxSubBSTHead是否是X节点的左节点/右节点 来判断 X的左树/右树是否是搜索二叉树
	 * 如果X左孩子的maxSubBSTHead就是X左孩子自己,那么以X左孩子为头节点的树整体就是搜索二叉树
	 */
	public static class Info {
		public Node maxSubBSTHead;// 树中最大搜索二叉树的头节点
		public int maxSubBSTSize;// 树中最大搜索二叉树的大小
		public int min;// 树中最小值
		public int max;// 树中最大值

		public Info(Node h, int size, int mi, int ma) {
			maxSubBSTHead = h;
			maxSubBSTSize = size;
			min = mi;
			max = ma;
		}
	}

	public static Info process(Node head) {
		if (head == null) {
			return null;
		}
		Info leftInfo = process(head.left);
		Info rightInfo = process(head.right);

		int min = head.value;
		int max = head.value;
		Node maxSubBSTHead = null;
		int maxSubBSTSize = 0;

		if (leftInfo != null) {
			min = Math.min(min, leftInfo.min);
			max = Math.max(max, leftInfo.max);
			maxSubBSTHead = leftInfo.maxSubBSTHead;
			maxSubBSTSize = leftInfo.maxSubBSTSize;
		}
		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;
			}
		}
		/**
		 * leftInfo.maxSubBSTHead == head.left
		 * X左孩子的maxSubBSTHead就是X左孩子自己,那么以X左孩子为头节点的树整体就是搜索二叉树
		 */
		if ((leftInfo == null ? true : (leftInfo.maxSubBSTHead == head.left && leftInfo.max < head.value))
				&& (rightInfo == null ? true : (rightInfo.maxSubBSTHead == head.right && rightInfo.min > head.value))) {
			maxSubBSTHead = head;
			maxSubBSTSize = (leftInfo == null ? 0 : leftInfo.maxSubBSTSize)
					+ (rightInfo == null ? 0 : rightInfo.maxSubBSTSize) + 1;
		}
		return new Info(maxSubBSTHead, maxSubBSTSize, min, max);
	}

	// 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 = 4;
		int maxValue = 100;
		int testTimes = 1000000;
		for (int i = 0; i < testTimes; i++) {
			Node head = generateRandomBST(maxLevel, maxValue);
			if (maxSubBSTHead1(head) != maxSubBSTHead2(head)) {
				System.out.println("Oops!");
			}
		}
		System.out.println("finish!");
	}

}
package class08;

import java.util.LinkedList;

public class Code06_IsCBT {

	public static class Node {
		public int value;
		public Node left;
		public Node right;

		public Node(int data) {
			this.value = data;
		}
	}

	public static boolean isCBT1(Node head) {
		if (head == null) {
			return true;
		}
		LinkedList<Node> queue = new LinkedList<>();
		// 是否遇到过左右两个孩子不双全的节点
		boolean leaf = false;
		Node l = null;
		Node r = null;
		queue.add(head);
		while (!queue.isEmpty()) {
			head = queue.poll();
			l = head.left;
			r = head.right;
			if (leaf) {
				// 遇到过左右不双全的节点后,后面的节点必须是叶节点
				if (l != null || r != null) {
					return false;
				}
			} else {
				// 没遇到过左右不双全的节点
				// 存在右节点不存在左节点->必不是完全二叉树
				if (l == null && r != null) {
					return false;
				}
				// 左右节点有一个不存在,则修改标志位,后续的节点改为判断是否是叶节点
				if (l == null || r == null) {
					leaf = true;
				}
			}
			if (l != null) {
				queue.add(l);
			}
			if (r != null) {
				queue.add(r);
			}
		}
		return true;
	}

	public static boolean isCBT2(Node head) {
		if (head == null) {
			return true;
		}
		return process(head).isCBT;
	}

	public static class Info {
		public boolean isFull;
		public boolean isCBT;
		public int height;

		public Info(boolean full, boolean cbt, int h) {
			isFull = full;
			isCBT = cbt;
			height = h;
		}
	}

	public static Info process(Node head) {
		if (head == null) {
			return new Info(true, true, 0);
		}
		Info leftInfo = process(head.left);
		Info rightInfo = process(head.right);
		int height = Math.max(leftInfo.height, rightInfo.height) + 1;
		boolean isFull = leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height;
		boolean isCBT = false;
		if (isFull) {
			isCBT = true;
		} else {
			if (leftInfo.isCBT && rightInfo.isCBT) {
				if (leftInfo.isCBT && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) {
					isCBT = true;
				}
				if (leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) {
					isCBT = true;
				}
				if (leftInfo.isFull && rightInfo.isCBT && leftInfo.height == rightInfo.height) {
					isCBT = true;
				}
			}
		}
		return new Info(isFull, isCBT, height);
	}

	// 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 = 5;
		int maxValue = 100;
		int testTimes = 1000000;
		for (int i = 0; i < testTimes; i++) {
			Node head = generateRandomBST(maxLevel, maxValue);
			if (isCBT1(head) != isCBT2(head)) {
				System.out.println("Oops!");
			}
		}
		System.out.println("finish!");
	}

}
package class08;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

public class Code07_lowestAncestor {

	public static class Node {
		public int value;
		public Node left;
		public Node right;

		public Node(int data) {
			this.value = data;
		}
	}

	public static Node lowestAncestor1(Node head, Node o1, Node o2) {
		if (head == null) {
			return null;
		}
		// <节点,节点的父节点>
		HashMap<Node, Node> parentMap = new HashMap<>();
		parentMap.put(head, null);
		fillParentMap(head, parentMap);
		HashSet<Node> o1Set = new HashSet<>();
		Node cur = o1;
		o1Set.add(cur);
		// cur一直向上跳,将沿途的父节点添加到set
		while (parentMap.get(cur) != null) {
			cur = parentMap.get(cur);
			o1Set.add(cur);
		}
		// o2也一直向上跳,每次跳的时候判断o1是否经过过这个点
		cur = o2;
		while (!o1Set.contains(cur)) {
			cur = parentMap.get(cur);
		}
		return cur;
	}

	public static void fillParentMap(Node head, HashMap<Node, Node> parentMap) {
		if (head.left != null) {
			parentMap.put(head.left, head);
			fillParentMap(head.left, parentMap);
		}
		if (head.right != null) {
			parentMap.put(head.right, head);
			fillParentMap(head.right, parentMap);
		}
	}

	public static Node lowestAncestor2(Node head, Node o1, Node o2) {
		return process(head, o1, o2).ans;
	}

	public static class Info {
		public Node ans;// o1o2的最初交汇点是谁,如果不是在此树上交汇的,则是null
		public boolean findO1;// 此树上找到o1
		public boolean findO2;// 此树上找到o2

		public Info(Node a, boolean f1, boolean f2) {
			ans = a;
			findO1 = f1;
			findO2 = f2;
		}
	}

	public static Info process(Node X, Node o1, Node o2) {
		if (X == null) {
			return new Info(null, false, false);
		}
		Info leftInfo = process(X.left, o1, o2);
		Info rightInfo = process(X.right, o1, o2);

		// X本身是o1/左树发现o1/右树发现o1
		boolean findO1 = X == o1 || leftInfo.findO1 || rightInfo.findO1;
		boolean findO2 = X == o2 || leftInfo.findO2 || rightInfo.findO2;

		// o1o2的最初交汇点在哪?
		Node ans = null;
		// 若左树已发现交汇点,以左树交汇点为准
		if (leftInfo.ans != null) {
			ans = leftInfo.ans;
		}
		// 若右树已发现交汇点,以右树交汇点为准
		if (rightInfo.ans != null) {
			ans = rightInfo.ans;
		}
		// 左右树都没发现交汇点,自己为头的这棵树上又找到了o1和o2->自己就是最初交汇点
		if (ans == null) {
			if (findO1 && findO2) {
				ans = X;
			}
		}
		return new Info(ans, findO1, findO2);
	}

	// 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;
	}

	// for test
	public static Node pickRandomOne(Node head) {
		if (head == null) {
			return null;
		}
		ArrayList<Node> arr = new ArrayList<>();
		fillPrelist(head, arr);
		int randomIndex = (int) (Math.random() * arr.size());
		return arr.get(randomIndex);
	}

	// for test
	public static void fillPrelist(Node head, ArrayList<Node> arr) {
		if (head == null) {
			return;
		}
		arr.add(head);
		fillPrelist(head.left, arr);
		fillPrelist(head.right, arr);
	}

	public static void main(String[] args) {
		int maxLevel = 4;
		int maxValue = 100;
		int testTimes = 1000000;
		for (int i = 0; i < testTimes; i++) {
			Node head = generateRandomBST(maxLevel, maxValue);
			Node o1 = pickRandomOne(head);
			Node o2 = pickRandomOne(head);
			if (lowestAncestor1(head, o1, o2) != lowestAncestor2(head, o1, o2)) {
				System.out.println("Oops!");
			}
		}
		System.out.println("finish!");
	}

}
package class08;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

public class Code08_MaxDistance {

	public static class Node {
		public int value;
		public Node left;
		public Node right;

		public Node(int data) {
			this.value = data;
		}
	}

	public static int maxDistance1(Node head) {
		if (head == null) {
			return 0;
		}
		ArrayList<Node> arr = getPrelist(head);
		HashMap<Node, Node> parentMap = getParentMap(head);
		int max = 0;
		for (int i = 0; i < arr.size(); i++) {
			for (int j = i; j < arr.size(); j++) {
				max = Math.max(max, distance(parentMap, arr.get(i), arr.get(j)));
			}
		}
		return max;
	}

	public static ArrayList<Node> getPrelist(Node head) {
		ArrayList<Node> arr = new ArrayList<>();
		fillPrelist(head, arr);
		return arr;
	}

	public static void fillPrelist(Node head, ArrayList<Node> arr) {
		if (head == null) {
			return;
		}
		arr.add(head);
		fillPrelist(head.left, arr);
		fillPrelist(head.right, arr);
	}

	public static HashMap<Node, Node> getParentMap(Node head) {
		HashMap<Node, Node> map = new HashMap<>();
		map.put(head, null);
		fillParentMap(head, map);
		return map;
	}

	public static void fillParentMap(Node head, HashMap<Node, Node> parentMap) {
		if (head.left != null) {
			parentMap.put(head.left, head);
			fillParentMap(head.left, parentMap);
		}
		if (head.right != null) {
			parentMap.put(head.right, head);
			fillParentMap(head.right, parentMap);
		}
	}

	public static int distance(HashMap<Node, Node> parentMap, Node o1, Node o2) {
		HashSet<Node> o1Set = new HashSet<>();
		Node cur = o1;
		o1Set.add(cur);
		while (parentMap.get(cur) != null) {
			cur = parentMap.get(cur);
			o1Set.add(cur);
		}
		cur = o2;
		while (!o1Set.contains(cur)) {
			cur = parentMap.get(cur);
		}
		Node lowestAncestor = cur;
		cur = o1;
		int distance1 = 1;
		while (cur != lowestAncestor) {
			cur = parentMap.get(cur);
			distance1++;
		}
		cur = o2;
		int distance2 = 1;
		while (cur != lowestAncestor) {
			cur = parentMap.get(cur);
			distance2++;
		}
		return distance1 + distance2 - 1;
	}

	public static int maxDistance2(Node head) {
		return process(head).maxDistance;
	}

	public static class Info {
		public int maxDistance;
		public int height;

		public Info(int dis, int h) {
			maxDistance = dis;
			height = h;
		}
	}

	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 height = Math.max(leftInfo.height, rightInfo.height) + 1;
		int maxDistance = Math.max(Math.max(leftInfo.maxDistance, rightInfo.maxDistance),
				leftInfo.height + rightInfo.height + 1);
		return new Info(maxDistance, height);
	}

	// 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 = 4;
		int maxValue = 100;
		int testTimes = 1000000;
		for (int i = 0; i < testTimes; i++) {
			Node head = generateRandomBST(maxLevel, maxValue);
			if (maxDistance1(head) != maxDistance2(head)) {
				System.out.println("Oops!");
			}
		}
		System.out.println("finish!");
	}

}
package class08;

import java.util.ArrayList;
import java.util.List;

public class Code09_MaxHappy {

	public static class Employee {
		public int happy;
		public List<Employee> nexts;

		public Employee(int h) {
			happy = h;
			nexts = new ArrayList<>();
		}

	}

	public static int maxHappy1(Employee boss) {
		if (boss == null) {
			return 0;
		}
		return process1(boss, false);
	}

	public static int process1(Employee cur, boolean up) {
		if (up) {
			int ans = 0;
			for (Employee next : cur.nexts) {
				ans += process1(next, false);
			}
			return ans;
		} else {
			int p1 = cur.happy;
			int p2 = 0;
			for (Employee next : cur.nexts) {
				p1 += process1(next, true);
				p2 += process1(next, false);
			}
			return Math.max(p1, p2);
		}
	}

	public static int maxHappy2(Employee boss) {
		if (boss == null) {
			return 0;
		}
		Info all = process2(boss);
		return Math.max(all.yes, all.no);
	}

	public static class Info {
		public int yes;//头节点在来的情况下整棵树的最大快乐值
		public int no;//头节点在不来的情况下整棵树的最大快乐值

		public Info(int y, int n) {
			yes = y;
			no = n;
		}
	}

	public static Info process2(Employee x) {
		if (x.nexts.isEmpty()) {
			return new Info(x.happy, 0);
		}
		int yes = x.happy;
		int no = 0;
		for (Employee next : x.nexts) {
			Info nextInfo = process2(next);
			yes += nextInfo.no;
			no += Math.max(nextInfo.yes, nextInfo.no);
		}
		return new Info(yes, no);
	}

	// for test
	public static Employee genarateBoss(int maxLevel, int maxNexts, int maxHappy) {
		if (Math.random() < 0.02) {
			return null;
		}
		Employee boss = new Employee((int) (Math.random() * (maxHappy + 1)));
		genarateNexts(boss, 1, maxLevel, maxNexts, maxHappy);
		return boss;
	}

	// for test
	public static void genarateNexts(Employee e, int level, int maxLevel, int maxNexts, int maxHappy) {
		if (level > maxLevel) {
			return;
		}
		int nextsSize = (int) (Math.random() * (maxNexts + 1));
		for (int i = 0; i < nextsSize; i++) {
			Employee next = new Employee((int) (Math.random() * (maxHappy + 1)));
			e.nexts.add(next);
			genarateNexts(next, level + 1, maxLevel, maxNexts, maxHappy);
		}
	}

	public static void main(String[] args) {
		int maxLevel = 4;
		int maxNexts = 7;
		int maxHappy = 100;
		int testTimes = 100000;
		for (int i = 0; i < testTimes; i++) {
			Employee boss = genarateBoss(maxLevel, maxNexts, maxHappy);
			if (maxHappy1(boss) != maxHappy2(boss)) {
				System.out.println("Oops!");
			}
		}
		System.out.println("finish!");
	}

}

class09

package class09;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;

/**
 * 给定一个由字符串组成的数组strs,
 * 必须把所有的字符串拼接起来,
 * 返回所有可能的拼接结果中,字典序最小的结果
 */

public class Code01_LowestLexicography {
    /**
     * 暴力解法
     */
	public static String lowestString1(String[] strs) {
		if (strs == null || strs.length == 0) {
			return "";
		}
		ArrayList<String> all = new ArrayList<>();
		HashSet<Integer> use = new HashSet<>();
		process(strs, use, "", all);
		// 遍历比较所有排列组合结果
		String lowest = all.get(0);
		for (int i = 1; i < all.size(); i++) {
			if (all.get(i).compareTo(lowest) < 0) {
				lowest = all.get(i);
			}
		}
		return lowest;
	}

    /**
     * 优化解法
     */
    public static String lowestString2(String[] strs) {
        if (strs == null || strs.length == 0) {
            return "";
        }
        Arrays.sort(strs, new MyComparator());
        String res = "";
        for (int i = 0; i < strs.length; i++) {
            res += strs[i];
        }
        return res;
    }

	/**
     * 排列组合:
	 * 将str数组中的元素进行全排列,全排列结果放到all中
	 * 比如str=[a,b,c],全排列结果为:abc,acb,bac,bca,cab,cba
	 * @param strs 原始字符串组成的数组
	 * @param use 已经使用过的字符串在str中的下标
	 * @param path 单次排列组合结果
	 * @param all 所有排列组合的结果集
	 */
	public static void process(String[] strs, HashSet<Integer> use, String path, ArrayList<String> all) {
		if (use.size() == strs.length) {
			all.add(path);
		} else {
			for (int i = 0; i < strs.length; i++) {
				if (!use.contains(i)) {
					use.add(i);
					process(strs, use, path + strs[i], all);
					use.remove(i);
				}
			}
		}
	}

	public static class MyComparator implements Comparator<String> {
		@Override
		public int compare(String a, String b) {
			return (a + b).compareTo(b + a);
		}
	}

	// for test
	public static String generateRandomString(int strLen) {
		char[] ans = new char[(int) (Math.random() * strLen) + 1];
		for (int i = 0; i < ans.length; i++) {
			int value = (int) (Math.random() * 5);
			ans[i] = (char) (97 + value);
		}
		return String.valueOf(ans);
	}

	// for test
	public static String[] generateRandomStringArray(int arrLen, int strLen) {
		String[] ans = new String[(int) (Math.random() * arrLen) + 1];
		for (int i = 0; i < ans.length; i++) {
			ans[i] = generateRandomString(strLen);
		}
		return ans;
	}

	// for test
	public static String[] copyStringArray(String[] arr) {
		String[] ans = new String[arr.length];
		for (int i = 0; i < ans.length; i++) {
			ans[i] = String.valueOf(arr[i]);
		}
		return ans;
	}

	public static void main(String[] args) {
		int arrLen = 6;
		int strLen = 5;
		int testTimes = 100000;
		for (int i = 0; i < testTimes; i++) {
			String[] arr1 = generateRandomStringArray(arrLen, strLen);
			String[] arr2 = copyStringArray(arr1);
			if (!lowestString1(arr1).equals(lowestString2(arr2))) {
				System.out.println("Oops!");
			}
		}
		System.out.println("finish!");
	}

}
package class09;

import java.util.HashSet;

public class Code02_Light {

	public static int minLight1(String road) {
		if (road == null || road.length() == 0) {
			return 0;
		}
		return process(road.toCharArray(), 0, new HashSet<>());
	}

	/**
	 * 暴力解法
	 * str[index]位置自由选择放灯/不放灯
	 * str[0...index-1]范围内已经做完决定了,放灯的位置记录在lights中
	 * 要求选出所有能照亮.的方案,并返回lights最小的方案中放了几盏灯
	 * @param str
	 * @param index
	 * @param lights
	 * @return
	 */
	public static int process(char[] str, int index, HashSet<Integer> lights) {
		if (index == str.length) {// 已经到最后一个位置了,遍历判断str中所有.是否都被照亮
			for (int i = 0; i < str.length; i++) {
				if (str[i] == '.') {
					if (!lights.contains(i - 1) && !lights.contains(i) && !lights.contains(i + 1)) {
						return Integer.MAX_VALUE;
					}
				}
			}
			return lights.size();
		} else {// 后面还有位置待确定放不放灯,分2种情况分别进行
			// 情况一:index位置不放灯时,整个方案放灯数量
			int nolight = process(str, index + 1, lights);
			int yeslight = Integer.MAX_VALUE;
			if (str[index] == '.') {
				// 情况二:index位置放灯时,整个方案放灯数量
				lights.add(index);
				yeslight = process(str, index + 1, lights);
				lights.remove(index);// 恢复现场
			}
			// 返回index位置放不放灯两种情况下灯数少的
			return Math.min(nolight, yeslight);
		}
	}

	/**
	 * 贪心解法
	 *
	 * @param road
	 * @return
	 */
	public static int minLight2(String road) {
		char[] str = road.toCharArray();
		int index = 0;
		int light = 0;
		while (index < str.length) {
			if (str[index] == 'X') {
				index++;
			} else {
				light++;
				if (index + 1 == str.length) {
					break;
				} else {
					if (str[index + 1] == 'X') {
						index = index + 2;
					} else {
						index = index + 3;
					}
				}
			}
		}
		return light;
	}

	// 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!");
	}
}
package class09;

import java.util.PriorityQueue;

public class Code03_LessMoneySplitGold {
	/**
	 * 暴力实现
	 */
	public static int lessMoney1(int[] arr) {
		if (arr == null || arr.length == 0) {
			return 0;
		}
		return process(arr, 0);
	}

	public 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++) {
				ans = Math.min(ans, process(copyAndMergeTwo(arr, i, j), pre + arr[i] + arr[j]));
			}
		}
		return ans;
	}

	public static int[] copyAndMergeTwo(int[] arr, int i, int j) {
		// 新数组将arr中i和j位置的数相加合并,变成一个位置,因此总长度少1
		int[] ans = new int[arr.length - 1];
		int ansi = 0;
		// 依次将arr中非i和j位置的数据设置到ans数组中
		for (int arri = 0; arri < arr.length; arri++) {
			if (arri != i && arri != j) {
				ans[ansi++] = arr[arri];
			}
		}
		// ans数组最后一位设为arr中i和j位置的和
		ans[ansi] = arr[i] + arr[j];
		return ans;
	}

	/**
	 * 贪心实现
	 * 为什么这么做没讲,不必纠结,记住就好
	 * @param arr
	 * @return
	 */
	public static int lessMoney2(int[] arr) {
		// 遍历arr放至小根堆
		PriorityQueue<Integer> pQ = new PriorityQueue<>();
		for (int i = 0; i < arr.length; i++) {
			pQ.add(arr[i]);
		}
		int sum = 0;
		int cur = 0;
		while (pQ.size() > 1) {
			// 弹出大根堆数组的头2个元素相加(每次弹出都会重新调整堆结构),将当前结果累计到总结果中,再将当前结果放回大根堆(也会重新调整堆结构)
			cur = pQ.poll() + pQ.poll();
			sum += cur;
			pQ.add(cur);
		}
		return sum;
	}

	// for test
	public static int[] generateRandomArray(int maxSize, int maxValue) {
		int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = (int) (Math.random() * (maxValue + 1));
		}
		return arr;
	}

	public static void main(String[] args) {
		int testTime = 100000;
		int maxSize = 6;
		int maxValue = 1000;
		for (int i = 0; i < testTime; i++) {
			int[] arr = generateRandomArray(maxSize, maxValue);
			if (lessMoney1(arr) != lessMoney2(arr)) {
				System.out.println("Oops!");
			}
		}
		System.out.println("finish!");
	}

}
package class09;

import java.util.Arrays;
import java.util.Comparator;

/**
 * 一些项目要占用一个会议室宣讲,会议室不能同时容纳两个项目的宣讲。
 * 给你每一个项目开始的时间和结束的时间
 * 你来安排宣讲的日程,要求会议室进行的宣讲的场次最多。
 * 返回最多的宣讲场次。
 */
public class Code04_BestArrange {

	public static class Program {
		public int start;
		public int end;

		public Program(int start, int end) {
			this.start = start;
			this.end = end;
		}
	}

	public static int bestArrange1(Program[] programs) {
		if (programs == null || programs.length == 0) {
			return 0;
		}
		return process(programs, 0, 0);
	}

	/**
	 * 暴力枚举解法
	 *
	 * @param programs 待安排的会议
	 * @param done 目前已经安排的会议数
	 * @param timeLine 当前时间点
	 * @return 最大安排数
	 */
	public static int process(Program[] programs, int done, int timeLine) {
		if (programs.length == 0) {
			return done;
		}
		int max = done;
		// 遍历所有会议,判断所有会议都分别进行安排的情况
		for (int i = 0; i < programs.length; i++) {
			// i会议未超过当前时间点,安排i会议
			if (programs[i].start >= timeLine) {
				// i位置会议已安排,剔除,done+1
				Program[] next = copyButExcept(programs, i);
				// i安排完了,递归判断除了i以外的会议分别进行安排的情况
				// 递归处理待安排会议,i位置会议的结束时间即下面会议安排判断时的时间点
				max = Math.max(max, process(next, done + 1, programs[i].end));
			}
		}
		return max;
	}

	/**
	 * 贪心解法
	 *
	 * @param programs
	 * @return
	 */
	public static int bestArrange2(Program[] programs) {
		Arrays.sort(programs, new ProgramComparator());
		int timeLine = 0;
		int result = 0;
		// programs已经按照结束时间排好序,直接顺序遍历安排会议
		for (int i = 0; i < programs.length; i++) {
			if (timeLine <= programs[i].start) {
				result++;
				timeLine = programs[i].end;
			}
		}
		return result;
	}

	// 贪心策略比较器,谁的结束时间早谁在前
	public static class ProgramComparator implements Comparator<Program> {
		@Override
		public int compare(Program o1, Program o2) {
			return o1.end - o2.end;
		}
	}

	// 剔除会议数组中下标i的会议
	public static Program[] copyButExcept(Program[] programs, int i) {
		Program[] ans = new Program[programs.length - 1];
		int index = 0;
		for (int k = 0; k < programs.length; k++) {
			if (k != i) {
				ans[index++] = programs[k];
			}
		}
		return ans;
	}



	// for test
	public static Program[] generatePrograms(int programSize, int timeMax) {
		Program[] ans = new Program[(int) (Math.random() * (programSize + 1))];
		for (int i = 0; i < ans.length; i++) {
			int r1 = (int) (Math.random() * (timeMax + 1));
			int r2 = (int) (Math.random() * (timeMax + 1));
			if (r1 == r2) {
				ans[i] = new Program(r1, r1 + 1);
			} else {
				ans[i] = new Program(Math.min(r1, r2), Math.max(r1, r2));
			}
		}
		return ans;
	}

	public static void main(String[] args) {
		int programSize = 12;
		int timeMax = 20;
		int timeTimes = 1000000;
		for (int i = 0; i < timeTimes; i++) {
			Program[] programs = generatePrograms(programSize, timeMax);
			if (bestArrange1(programs) != bestArrange2(programs)) {
				System.out.println("Oops!");
			}
		}
		System.out.println("finish!");
	}

}
package class09;

import java.util.Comparator;
import java.util.PriorityQueue;

public class Code05_IPO {

    /**
     * @param K 表示你只能串行的最多做k个项目
     * @param W 表示你初始的资金
     * @param Profits 表示i号项目在扣除花费之后还能挣到的钱(利润)
     * @param Capital 表示i号项目的花费
     * @return 你最后获得的最大钱数。
     */
	public static int findMaximizedCapital(int K, int W, int[] Profits, int[] Capital) {
	    // 按项目花费从小到大组织小根堆,花费小的在头部
		PriorityQueue<Program> minCostQ = new PriorityQueue<>(new MinCostComparator());
        // 按项目利润从大到小组织大根堆,利润大的在头部
		PriorityQueue<Program> maxProfitQ = new PriorityQueue<>(new MaxProfitComparator());
		// 组织花费小根堆
		for (int i = 0; i < Profits.length; i++) {
			minCostQ.add(new Program(Profits[i], Capital[i]));
		}
		for (int i = 0; i < K; i++) {
		    // 组织利润大根堆:遍历花费小根堆,取出所有花费能被当前资金hold的项目,放到利润大根堆
			while (!minCostQ.isEmpty() && minCostQ.peek().cost <= W) {
				maxProfitQ.add(minCostQ.poll());
			}
			// 利润大根堆为空,说明当前资金hold不了任何一个项目,无项目可作,直接返回当前资金
			if (maxProfitQ.isEmpty()) {
                return W;
            }
            // 贪心策略:取利润最大的项目做,获得的利润加到当前资金中,继续启动后续项目
            W += maxProfitQ.poll().priority;
		}
		return W;
	}

	public static class Program {
		public int priority;
		public int cost;

		public Program(int priority, int cost) {
			this.priority = priority;
			this.cost = cost;
		}
	}

	/**
	 * 小根堆比较策略:花费少的在前
	 */
	public static class MinCostComparator implements Comparator<Program> {

		@Override
		public int compare(Program o1, Program o2) {
			return o1.cost - o2.cost;
		}

	}

	/**
	 * 大根堆比较策略:利润大的在前
	 */
	public static class MaxProfitComparator implements Comparator<Program> {

		@Override
		public int compare(Program o1, Program o2) {
			return o2.priority - o1.priority;
		}

	}

}

class10

package class10;

import java.util.HashMap;
import java.util.List;
import java.util.Stack;

/**
 * 并查集:
 *
 * 有若干个样本a、b、c、d…类型假设是V
 * 在并查集中一开始认为每个样本都在单独的集合里
 * 用户可以在任何时候调用如下两个方法:
 *   boolean isSameSet(V x, V y) : 查询样本x和样本y是否属于一个集合
 *   void union(V x, V y) : 把x和y各自所在集合的所有样本合并成一个集合
 * isSameSet和union方法的代价越低越好
 * ----------------------------------------------------------------------
 * 1)每个节点都有一条往上指的指针
 * 2)节点a往上找到的头节点,叫做a所在集合的代表节点
 * 3)查询x和y是否属于同一个集合,就是看看找到的代表节点是不是一个
 * 4)把x和y各自所在集合的所有点合并成一个集合,只需要小集合的代表点挂在大集合的代表点的下方即可
 */

public class Code01_UnionFind {
    // 只是将value包一层成为节点
    public static class Node<V> {
        V value;
        public Node(V value) {
            this.value = value;
        }
    }

    public static class UnionSet<V> {
        public HashMap<V, Node<V>> nodes = new HashMap<>();// 记录值和包装后的节点对应的关系
        public HashMap<Node<V>, Node<V>> parents = new HashMap<>();// <节点,父节点>
        public HashMap<Node<V>, Integer> sizeMap = new HashMap<>();// <代表点,代表点为头节点的树的结点数>,代表点即父节点是自己的节点
        // 构造时即完成map初始化
        public UnionSet(List<V> values) {
            for (V cur : values) {
                Node<V> node = new Node<>(cur);
                nodes.put(cur, node);
                parents.put(node, node);
                sizeMap.put(node, 1);// 开始时每个点都指向自己,每个点都是代表点
            }
        }

        /**
         * 查询:ab是否在同一个集合
         */
        public boolean isSameSet(V a, V b) {
            if (!nodes.containsKey(a) || !nodes.containsKey(b)) {
                return false;
            }
            // a和b向上找,直到找到某个点的父节点是它自己,即各自的代表点,如果各自代表点是同一个,则ab在同一个集合中
            return findFather(nodes.get(a)) == findFather(nodes.get(b));
        }

        /**
         * 合并:将ab各自所在的集合进行合并
         */
        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) {
                Node<V> bigHead = sizeMap.get(aHead) >= sizeMap.get(bHead) ? aHead : bHead;
                Node<V> smallHead = bigHead == aHead ? bHead : aHead;
                parents.put(smallHead, bigHead);// 认贼作父
                sizeMap.put(bigHead, sizeMap.get(bigHead) + sizeMap.get(smallHead));// 合并人数
                sizeMap.remove(smallHead);// 解散帮派
            }
        }

        /**
         * 查询节点的代表节点
         */
        public Node<V> findFather(Node<V> cur) {
            /**
             * 原始findFather逻辑,每次找代表点都要经历一次遍历过程O(N),复杂度过高
             */
            /*
            while(cur != parents.get(cur)){
                cur = parents.get(cur);
            }
            return cur;
            */
            /**
             * 优化后的findFather逻辑,在首次找到某个点的代表点时【消耗O(N)】,即将这个点的父节点指向代表点,后面再调用findFather找这个点的代表点时直接从parents中取就可以了【消耗O(1)】
             */
            Stack<Node<V>> path = new Stack<>();// 记录cur向上找代表点这条路径上的所有点
            while (cur != parents.get(cur)) {
                path.push(cur);
                cur = parents.get(cur);// cur向上跳
            }
            // cur已经来到不能再往上的位置,即代表点
            // 将沿途所有节点的父全部指向当前代表点
            while (!path.isEmpty()) {
                parents.put(path.pop(), cur);
            }
            return cur;
        }

    }
}
package class10;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;

/**
 * 宽度优先遍历
 */
public class Code02_BFS {

	// 从node出发,进行宽度优先遍历
	public static void bfs(Node node) {
		if (node == null) {
			return;
		}
		Queue<Node> queue = new LinkedList<>();
		HashSet<Node> set = new HashSet<>();// 保证每个节点只进一次队列,避免死环
		queue.add(node);
		set.add(node);
		while (!queue.isEmpty()) {
			Node cur = queue.poll();
			System.out.println(cur.value);
			for (Node next : cur.nexts) {
				if (!set.contains(next)) {
					set.add(next);
					queue.add(next);
				}
			}
		}
	}

}
package class10;

import java.util.HashSet;
import java.util.Stack;

/**
 * 深度优先遍历
 */
public class Code02_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.value);
		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.value);
					break;
				}
			}
		}
	}

}
package class10;

import java.util.*;

/**
 * 拓扑排序
 */
public class Code03_TopologySort {

	// directed graph and no loop
	public static List<Node> sortedTopology(Graph graph) {
		// 维护节点与其剩余入度的关系
		HashMap<Node, Integer> inMap = new HashMap<>();
		// 剩余入度为0的点,才能进这个队列
		Queue<Node> zeroInQueue = new LinkedList<>();
		for (Node node : graph.nodes.values()) {
			inMap.put(node, node.in);
			if (node.in == 0) {
				zeroInQueue.add(node);
			}
		}
		// 拓扑排序的结果,即将入度为0的点依次加入result
		List<Node> result = new ArrayList<>();
		while (!zeroInQueue.isEmpty()) {
			Node cur = zeroInQueue.poll();
			result.add(cur);
			for (Node next : cur.nexts) {
				inMap.put(next, inMap.get(next) - 1);
				if (inMap.get(next) == 0) {
					zeroInQueue.add(next);
				}
			}
		}
		return result;
	}
}
package class10;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.Stack;

// 仅适用于无向图
public class Code04_Kruskal {

	public static Set<Edge> kruskalMST(Graph graph) {
		UnionFind unionFind = new UnionFind();
		unionFind.makeSets(graph.nodes.values());
		PriorityQueue<Edge> priorityQueue = new PriorityQueue<>(new EdgeComparator());
		for (Edge edge : graph.edges) { // M 条边
			priorityQueue.add(edge);  // O(logM)
		}
		Set<Edge> result = new HashSet<>();
		while (!priorityQueue.isEmpty()) { // M 条边
			// 按权重小->大的顺序处理边
			Edge edge = priorityQueue.poll(); // O(logM)
			if (!unionFind.isSameSet(edge.from, edge.to)) { // O(1)
				result.add(edge);
				unionFind.union(edge.from, edge.to);
			}
		}
		return result;
	}

	// 并查集
	public static class UnionFind {
		// key 某一个节点, value key节点往上的节点
		private HashMap<Node, Node> fatherMap;
		// key 某一个集合的代表节点, value key所在集合的节点个数
		private HashMap<Node, Integer> sizeMap;

		public UnionFind() {
			fatherMap = new HashMap<Node, Node>();
			sizeMap = new HashMap<Node, Integer>();
		}
		
		public void makeSets(Collection<Node> nodes) {
			fatherMap.clear();
			sizeMap.clear();
			for (Node node : nodes) {
				fatherMap.put(node, node);
				sizeMap.put(node, 1);
			}
		}

		private Node findFather(Node n) {
			Stack<Node> path = new Stack<>();
			while(n != fatherMap.get(n)) {
				path.add(n);
				n = fatherMap.get(n);
			}
			while(!path.isEmpty()) {
				// 沿途节点的父节点都变成代表节点
				fatherMap.put(path.pop(), n);
			}
			return n;
		}

		public boolean isSameSet(Node a, Node b) {
			return findFather(a) == findFather(b);
		}

		public void union(Node a, Node b) {
			if (a == null || b == null) {
				return;
			}
			Node aDai = findFather(a);
			Node bDai = findFather(b);
			if (aDai != bDai) {
				int aSetSize = sizeMap.get(aDai);
				int bSetSize = sizeMap.get(bDai);
				if (aSetSize <= bSetSize) {
					fatherMap.put(aDai, bDai);
					sizeMap.put(bDai, aSetSize + bSetSize);
					sizeMap.remove(aDai);
				} else {
					fatherMap.put(bDai, aDai);
					sizeMap.put(aDai, aSetSize + bSetSize);
					sizeMap.remove(bDai);
				}
			}
		}
	}

	/**
	 * 权重小的边在前
	 */
	public static class EdgeComparator implements Comparator<Edge> {
		@Override
		public int compare(Edge o1, Edge o2) {
			return o1.weight - o2.weight;
		}
	}

	public static class MySets{
		public HashMap<Node, List<Node>>  setMap;
		public MySets(List<Node> nodes) {
			for(Node cur : nodes) {
				List<Node> set = new ArrayList<Node>();
				set.add(cur);
				setMap.put(cur, set);
			}
		}


		public boolean isSameSet(Node from, Node to) {
			List<Node> fromSet  = setMap.get(from);
			List<Node> toSet = setMap.get(to);
			return fromSet == toSet;
		}


		public void union(Node from, Node to) {
			List<Node> fromSet  = setMap.get(from);
			List<Node> toSet = setMap.get(to);
			for(Node toNode : toSet) {
				fromSet.add(toNode);
				setMap.put(toNode, fromSet);
			}
		}
	}

}
package class10;

import java.util.Comparator;
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Set;

// undirected graph only
public class Code05_Prim {

	public static class EdgeComparator implements Comparator<Edge> {

		@Override
		public int compare(Edge o1, Edge o2) {
			return o1.weight - o2.weight;
		}

	}

	public static Set<Edge> primMST(Graph graph) {
		// 解锁的边进入小根堆
		PriorityQueue<Edge> priorityQueue = new PriorityQueue<>(new EdgeComparator());
		HashSet<Node> set = new HashSet<>();
		Set<Edge> handledEdges = new HashSet<>();// 存放已经处理过的边
		Set<Edge> result = new HashSet<>(); // 依次挑选的的边在result里
		for (Node node : graph.nodes.values()) { // 随便挑了一个点
			// node 是开始点
			if (!set.contains(node)) {
				set.add(node);
				for (Edge edge : node.edges) { // 由一个点,解锁所有相连的边
					if (!handledEdges.contains(edge)){
						priorityQueue.add(edge);
						handledEdges.add(edge);
					}
				}
				while (!priorityQueue.isEmpty()) {
					Edge edge = priorityQueue.poll(); // 弹出目前解锁的边中,最小的边
					Node toNode = edge.to; // 可能的一个新的点
					// 只有当指向点没有被遍历过时,对应的边才会被选中
					if (!set.contains(toNode)) {
						set.add(toNode);
						result.add(edge);
						for (Edge nextEdge : toNode.edges) {// 继续解锁新的点连通的边
							if (!handledEdges.contains(nextEdge)) {
								priorityQueue.add(nextEdge);
								handledEdges.add(nextEdge);
							}
						}
					}
				}
			}
			break;// 目的是为了防止数据森林,只对一个联通区域进行处理
		}
		return result;
	}

	// 请保证graph是连通图
	// graph[i][j]表示点i到点j的距离,如果是系统最大值代表无路
	// 返回值是最小连通图的路径之和
	public static int prim(int[][] graph) {
		int size = graph.length;
		int[] distances = new int[size];
		boolean[] visit = new boolean[size];
		visit[0] = true;
		for (int i = 0; i < size; i++) {
			distances[i] = graph[0][i];
		}
		int sum = 0;
		for (int i = 1; i < size; i++) {
			int minPath = Integer.MAX_VALUE;
			int minIndex = -1;
			for (int j = 0; j < size; j++) {
				if (!visit[j] && distances[j] < minPath) {
					minPath = distances[j];
					minIndex = j;
				}
			}
			if (minIndex == -1) {
				return sum;
			}
			visit[minIndex] = true;
			sum += minPath;
			for (int j = 0; j < size; j++) {
				if (!visit[j] && distances[j] > graph[minIndex][j]) {
					distances[j] = graph[minIndex][j];
				}
			}
		}
		return sum;
	}

	public static void main(String[] args) {
		System.out.println("hello world!");
	}

}
package class10;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;

// 边的权重都是正数,没有负数
public class Code06_Dijkstra {

	/**
	 * 未改进的dijkstra算法
	 * 每次取当前最小距离的点时都要遍历selectedNodes所有记录,耗时
	 *
	 * @param from
	 * @return
	 */
	public static HashMap<Node, Integer> dijkstra1(Node from) {
		// 从from出发到所有点的最小距离
		// key : 从from出发到达key
		// value : 从from出发到达key的最小距离
		// 如果在表中,没有T的记录,含义是从from出发到T这个点的距离为正无穷
		HashMap<Node, Integer> distanceMap = new HashMap<>();
		distanceMap.put(from, 0);
		// 已经求过距离的节点,存在selectedNodes中,以后再也不碰
		HashSet<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;
	}


	/**
	 * 改进后的dijkstra算法
	 * 每次从小根堆中取当前最小距离的点,但是有时距离需要更新,系统提供的小根堆无法满足
	 * 手写定制化小根堆,支持修改堆中元素,且修改后自动调整堆结构
	 * @param from
	 * @param size
	 * @return
	 */
	public static HashMap<Node, Integer> dijkstra2(Node from, int size) {
		NodeHeap nodeHeap = new NodeHeap(size);
		nodeHeap.addOrUpdateOrIgnore(from, 0);
		HashMap<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;
	}

	public static Node getMinDistanceAndUnselectedNode(HashMap<Node, Integer> distanceMap, HashSet<Node> selectedNodes) {
		Node minNode = null;
		int minDistance = Integer.MAX_VALUE;
		for (Entry<Node, Integer> entry : distanceMap.entrySet()) {
			Node node = entry.getKey();
			int distance = entry.getValue();
			if (!selectedNodes.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 上面数组中的位置,若为-1则表示曾经在堆上,现在不在了
		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)) {// 节点在堆上,更新distance为新旧distance中较小的,然后与父节点比向上冒
				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);// 记录原来在0位置的点,-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) {
				// 返回左右孩子中距离最小的孩子在堆数组中的下标(left+1是右孩子下标)
				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;
		}
	}



}
package class10;

/**
 * 图中的边
 */
public class Edge {
	public int weight;//权重
	public Node from;//边的起点
	public Node to;//边的终点

	public Edge(int weight, Node from, Node to) {
		this.weight = weight;
		this.from = from;
		this.to = to;
	}

}
package class10;

import java.util.HashMap;
import java.util.HashSet;

/**
 * 图
 */
public class Graph {
	public HashMap<Integer, Node> nodes;// <点的id,点> 点集
	public HashSet<Edge> edges;// 边集

	public Graph() {
		nodes = new HashMap<>();
		edges = new HashSet<>();
	}
}
package class10;

public class GraphGenerator {

	// matrix 所有的边
	// N*3 的矩阵
	// [weight, from节点上面的值,to节点上面的值]
	public static Graph createGraph(Integer[][] matrix) {
		Graph graph = new Graph();
		for (int i = 0; i < matrix.length; i++) { // matrix[0][0], matrix[0][1]  matrix[0][2]
			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 newEdge = new Edge(weight, fromNode, toNode);
			fromNode.nexts.add(toNode);
			fromNode.out++;
			toNode.in++;
			fromNode.edges.add(newEdge);
			/**
			 * 处理图的边集
			 */
			graph.edges.add(newEdge);
		}
		return graph;
	}

}




package class10;

import java.util.ArrayList;

/**
 * 图中的点
 */
public class Node {
	public int value;//点的id
	public int in;//入度,直接指向此点的边有几条
	public int out;//出度,此点作为边起点的边有几条
	public ArrayList<Node> nexts;//从自己出发的边 能到达的点有哪些
	public ArrayList<Edge> edges;//从自己出发的边 有哪些

	public Node(int value) {
		this.value = value;
		in = 0;
		out = 0;
		nexts = new ArrayList<>();
		edges = new ArrayList<>();
	}
}

class11

package class11;

public class Code01_Hanoi {

	public static void hanoi1(int n) {
		leftToRight(n);
	}

	public static void leftToRight(int n) {
		if (n == 1) {
			System.out.println("Move 1 from left to right");
			return;
		}
		leftToMid(n - 1);
		System.out.println("Move " + n + " from left to right");
		midToRight(n - 1);
	}

	public static void leftToMid(int n) {
		if (n == 1) {
			System.out.println("Move 1 from left to mid");
			return;
		}
		leftToRight(n - 1);
		System.out.println("Move " + n + " from left to mid");
		rightToMid(n - 1);
	}

	public static void rightToMid(int n) {
		if (n == 1) {
			System.out.println("Move 1 from right to mid");
			return;
		}
		rightToLeft(n - 1);
		System.out.println("Move " + n + " from right to mid");
		leftToMid(n - 1);
	}

	public static void midToRight(int n) {
		if (n == 1) {
			System.out.println("Move 1 from mid to right");
			return;
		}
		midToLeft(n - 1);
		System.out.println("Move " + n + " from mid to right");
		leftToRight(n - 1);
	}

	public static void midToLeft(int n) {
		if (n == 1) {
			System.out.println("Move 1 from mid to left");
			return;
		}
		midToRight(n - 1);
		System.out.println("Move " + n + " from mid to left");
		rightToLeft(n - 1);
	}

	public static void rightToLeft(int n) {
		if (n == 1) {
			System.out.println("Move 1 from right to left");
			return;
		}
		rightToMid(n - 1);
		System.out.println("Move " + n + " from right to left");
		midToLeft(n - 1);
	}

	public static void hanoi2(int n) {
		if (n > 0) {
			func(n, "left", "right", "mid");
		}
	}

	// 1~i 圆盘 目标是from -> to, other是另外一个
	public static void func(int N, String from, String to, String other) {
		if (N == 1) { // base
			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);
		}
	}

	public static void main(String[] args) {
		int n = 3;
		hanoi1(n);
		System.out.println("============");
		hanoi2(n);
		System.out.println("============");
	}

}
package class11;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

public class Code02_PrintAllSubsquences {

	public static List<String> subs(String s) {
		char[] str = s.toCharArray();
		String path = "";
		List<String> ans = new ArrayList<>();
		process1(str, 0, ans, path);
		return ans;
	}

	public static void process1(char[] str, int index, List<String> ans, String path) {
		if (index == str.length) {
			ans.add(path);
			return;
		}
		String no = path;
		process1(str, index + 1, ans, no);
		String yes = path + String.valueOf(str[index]);
		process1(str, index + 1, ans, yes);
	}

	public static List<String> subsNoRepeat(String s) {
		char[] str = s.toCharArray();
		String path = "";
		HashSet<String> set = new HashSet<>();
		process2(str, 0, set, path);
		List<String> ans = new ArrayList<>();
		for (String cur : set) {
			ans.add(cur);
		}
		return ans;
	}

	public static void process2(char[] str, int index, HashSet<String> set, String path) {
		if (index == str.length) {
			set.add(path);
			return;
		}
		String no = path;
		process2(str, index + 1, set, no);
		String yes = path + String.valueOf(str[index]);
		process2(str, index + 1, set, yes);
	}

	public static void main(String[] args) {
		String test = "aacc";
		List<String> ans1 = subs(test);
		List<String> ans2 = subsNoRepeat(test);

		for (String str : ans1) {
			System.out.println(str);
		}
		System.out.println("=================");
		for (String str : ans2) {
			System.out.println(str);
		}
		System.out.println("=================");

	}

}
package class11;

import java.util.ArrayList;
import java.util.List;

public class Code03_PrintAllPermutations {

	public static ArrayList<String> permutation(String str) {
		ArrayList<String> res = new ArrayList<>();
		if (str == null || str.length() == 0) {
			return res;
		}
		char[] chs = str.toCharArray();
		process(chs, 0, res);
		return res;
	}

	/**
	 * str中i位置之前的都已经做好决定放什么字符了,不再动了
	 * str中的i位置后面的字符都有机会来到i位置
	 */
	public static void process(char[] str, int i, ArrayList<String> res) {
		// i来到终止位置时,此时str中所有字符按顺序连接起来组成的字符串就是全排列的一种情况
		if (i == str.length) {
			res.add(String.valueOf(str));
			return;
		}
		// i没到终止位置,i往后的所有位置的字符都可以来到i位置,依次将i后所有位置的字符与i位置的字符交换
		for (int j = i; j < str.length; j++) {
			swap(str, i, j);// 将i位置的字符与j位置的字符交换
			process(str, i + 1, res);// i+1位置上后面的字符人人都有机会
			swap(str, i, j);// 恢复现场,继续for循环,将j+1位置的字符与i位置交换
		}
	}

	public static ArrayList<String> permutationNoRepeat(String str) {
		ArrayList<String> res = new ArrayList<>();
		if (str == null || str.length() == 0) {
			return res;
		}
		char[] chs = str.toCharArray();
		process2(chs, 0, res);
		return res;
	}

	/**
	 * 分支限界,提前杀死无用分支
	 */
	public static void process2(char[] str, int i, ArrayList<String> res) {
		if (i == str.length) {
			res.add(String.valueOf(str));
		}
		// 在当前将i后面位置的字符依次换到i位置的过程中,记录出现过的字符,如a出现过则visit[0]=true; c出现过则visit[2]=true
		// 例如[aba]中0位置有三个分支,00交换/01交换/02交换
		// 处理00交换时,a字符visit[0]=false没出现过;
		// 处理01交换时,b字符visit[1]=false没出现过;
		// 处理02交换时,a字符visit[0]=true出现过,那这个分支直接杀死
		boolean[] visit = new boolean[26]; // visit[0 1 .. 25]
		for (int j = i; j < str.length; j++) {
			// 下一个字符没出现过,才会将它与i位置交换
			if (!visit[str[j] - 'a']) {
				visit[str[j] - 'a'] = true;// 记录此字符出现过
				swap(str, i, j);
				process2(str, i + 1, res);
				swap(str, i, j);
			}
		}
	}

	public static void swap(char[] chs, int i, int j) {
		char tmp = chs[i];
		chs[i] = chs[j];
		chs[j] = tmp;
	}

	public static void main(String[] args) {
		String s = "aac";
		List<String> ans1 = permutation(s);
		for (String str : ans1) {
			System.out.println(str);
		}
		System.out.println("=======");
		List<String> ans2 = permutationNoRepeat(s);
		for (String str : ans2) {
			System.out.println(str);
		}

	}

}
package class11;

import java.util.Stack;

public class Code04_ReverseStackUsingRecursive {

	/**
	 * 依次取出栈底的数,进行压栈保存,直到从底取到顶了,这时原始栈顶部的数据在程序栈的顶部,再将程序栈从顶至下push到栈中,即实现原始栈顶的元素现在到栈底了
	 */
	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 result = stack.pop();
		if (stack.isEmpty()) {
			return result;
		} else {
			int last = f(stack);
			stack.push(result);
			return last;
		}
	}

	public static void main(String[] args) {
		Stack<Integer> test = new Stack<Integer>();
		test.push(1);
		test.push(2);
		test.push(3);
		test.push(4);
		test.push(5);
		reverse(test);
		while (!test.isEmpty()) {
			System.out.println(test.pop());
		}

	}

}
package class11;

/**
 * 规定1和A对应、2和B对应、3和C对应...
 * 那么一个数字字符串比如"111”就可以转化为:
 * "AAA"、"KA"和"AK"
 * 给定一个只有数字字符组成的字符串str,返回有多少种转化结果
 */
public class Code06_ConvertToLetterString {

    public static int number(String str) {
        if (str == null || str.length() == 0) {
            return 0;
        }
        return process(str.toCharArray(), 0);
    }

    /**
     * str中i之前的位置,如何转化已经做过决定了, 不用再关心,可以认为i位置之前啥都没有
     *
     * @param str
     * @param i
     * @return str中i及i位置之后的字符子串 有多少种转化的结果
     */
    public static int process(char[] str, int i) {
        // i到终止位置,说明这条分支整个走下来了,即将此分支从上到下的转化结果拼接后,形成1种有效的转化结果
        if (i == str.length) { // base case
            return 1;
        }
        // i还没有到终止位置
        // i位置是0,i自己不能转化,(i和i+1)组成的'0x'也不能转化 -> i及i位置之后的字符子串无可转化结果
        if (str[i] == '0') {
            return 0;
        }
        // str[i]字符不是‘0’
        if (str[i] == '1') {
            int res = process(str, i + 1); // i自己作为单独的部分,求str中i+1及i+1位置之后的字符子串,有多少种转化的结果
            if (i + 1 < str.length) {
                res += process(str, i + 2); // (i和i+1)作为单独的部分,求str中i+2及i+2位置之后的字符子串,有多少种转化的结果
            }
            return res;
        }
        if (str[i] == '2') {
            int res = process(str, i + 1); // i自己作为单独的部分,后续有多少种方法
            // (i和i+1)作为单独的部分并且没有超过26,后续有多少种方法
            if (i + 1 < str.length && (str[i + 1] >= '0' && str[i + 1] <= '6')) {
                res += process(str, i + 2); // (i和i+1)作为单独的部分,后续有多少种方法
            }
            return res;
        }
        // str[i] == '3' ~ '9',i只能作为单独部分,(i和i+1)不可能作为单独部分
        return process(str, i + 1);
    }

    /**
     * 动态规划
     */
    public static int myDP(String s) {
        if (s == null || s.length() == 0) {
            return 0;
        }
        char[] str = s.toCharArray();
        int N = str.length;
        int[] dp = new int[N + 1];
        dp[N] = 1;
        for (int index = N - 1; index >= 0; index--) {
            if (str[index] == '0') {
                dp[index] = 0;
            } else if (str[index] == '1') {
                dp[index] = dp[index + 1];
                if (index + 1 < N) {
                    dp[index] += dp[index + 2];
                }
            } else if (str[index] == '2') {
                dp[index] = dp[index + 1];
                if (index + 1 < N && (str[index + 1] >= '0' && str[index + 1] <= '6')) {
                    dp[index] += dp[index + 2];
                }
            } else {
                dp[index] = dp[index + 1];
            }
        }
        return dp[0];
    }

    public static void main(String[] args) {
        System.out.println(number("11111"));
        System.out.println(myDP("11111"));
    }

}
package class11;

/**
 * 给定两个长度都为N的数组weights和values,
 * weights[i]和values[i]分别代表 i号物品的重量和价值。
 * 给定一个正数bag,表示一个载重bag的袋子,
 * 你装的物品不能超过这个重量。
 * 返回你能装下最多的价值是多少?
 */
public class Code07_Knapsack {

	public static int getMaxValue(int[] w, int[] v, int bag) {
		return process(w, v, 0, bag);
	}

	public static int getMaxValue2(int[] w, int[] v, int bag) {
		return process2(w, v, 0, 0, bag);
	}

	/**
	 * 方式一:以剩余空间rest为参数
	 *
	 * @param index index之前的货物选与不选已定,不再操心
	 * @param rest bag的剩余空间
	 * @return 对index及其后面的货物自由选择,返回能够获得的最大价值
	 */
	public static int process(int[] w, int[] v, int index, int rest) {
		if (rest < 0) { // base case 1
			return -1;
		}
		if (index == w.length) { // base case 2
			return 0;
		}
		// 有货也有空间
		// 不选index位置的货物,求挑选index+1及其后位置的货物能够获得的最大价值
		int p1 = process(w, v, index + 1, rest);
		// 选择index位置的货物,bag剩余重量需减去index货物重量,求挑选index+1及其后位置的货物能够获得的最大价值
		int p2next = process(w, v, index + 1, rest - w[index]);
		int p2 = -1;
		if (p2next != -1) {
			p2 = v[index] + p2next;
		}
		return Math.max(p1, p2);
	}


	/**
	 * 方式二:以已用多少空间alreadyW为参数
	 *
	 * @param index index之前的货物选与不选已定,不再操心
	 * @param alreadyW bag已经用掉多少空间
	 * @param bag 背包总空间,固定不变
	 * @return 对index及其后面的货物自由选择,返回能够获得的最大价值
	 */
	public static int process2(int[] w, int[] v, int index, int alreadyW, int bag) {
		if (alreadyW > bag) {
			return -1;
		}
		// 重量没超
		if (index == w.length) {
			return 0;
		}
		int p1 = process2(w, v, index + 1, alreadyW, bag);
		int p2next = process2(w, v, index + 1, alreadyW + w[index], bag);
		int p2 = -1;
		if (p2next != -1) {
			p2 = v[index] + p2next;
		}
		return Math.max(p1, p2);

	}

	public static int dpWay(int[] w, int[] v, int bag) {
		int N = w.length;
		int[][] dp = new int[N + 1][bag + 1];
		for (int index = N - 1; index >= 0; index--) {
			for (int rest = 1; rest <= bag; rest++) {
				dp[index][rest] = dp[index + 1][rest];
				if (rest >= w[index]) {
					dp[index][rest] = Math.max(dp[index][rest], v[index] + dp[index + 1][rest - w[index]]);
				}
			}
		}
		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(getMaxValue(weights, values, bag));
		System.out.println(dpWay(weights, values, bag));
	}

}
package class11;

/**
 * 给定一个整型数组arr,代表数值不同的纸牌排成一条线,玩家A和玩家B依次拿走每张纸牌,
 * 规定玩家A先拿,玩家B后拿,但是每个玩家每次只能拿走最左或最右的纸牌,
 * 玩家A和玩家B都绝顶聪明。请返回最后获胜者的分数。
 */
public class Code08_CardsInLine {

	public static int win1(int[] arr) {
		if (arr == null || arr.length == 0) {
			return 0;
		}
		return Math.max(// 最大的分数即获胜者分数
				f(arr, 0, arr.length - 1), // 先手在[0~L-1]获得的分数
				s(arr, 0, arr.length - 1) // 后手在[0~L-1]获得的分数
		);
	}

	/**
	 * 先手,自己有当下这一步的选择权
	 * 从arr的L-R范围上,如果先手拿牌,获得的最大分数
	 */
	public static int f(int[] arr, int L, int R) {
		if (L == R) {
			return arr[L];
		}
		return Math.max(// 自己先手选择,当然选最大的
				arr[L] + s(arr, L + 1, R), // 自己选L,然后自己变后手,从[L+1,R]上获得的分数
				arr[R] + s(arr, L, R - 1)// 自己选R,然后自己变后手,从[L,R-1]上获得的分数
		);
	}

	/**
	 * 后手,自己无当下这一步的选择权,即当下这一步的选择是先手方留给你的,先手方肯定扔给你最差的
	 * 从arr的L-R范围上,如果后手拿牌,获得的最小分数(因为自己是后手,没有选择权,获得的分数由对方决定,而对方肯定给自己留最差的选择)
	 */
	public static int s(int[] arr, int L, int R) {
		if (L == R) {// 仅剩L一张牌,又因为是后手拿牌,所以L肯定被对手拿走了,自己拿个锤子
			return 0;
		}
		return Math.min(// 先手绝顶聪明,肯定知道自己拿完后后手方的两种选择中哪个最差,留给对方
				f(arr, L + 1, R), // 对方拿完后,自己后手拿,若对方拿L,那自己变先手从[L+1.R]上获取主动权去拿最好的
				f(arr, L, R - 1)// 对方拿完后,自己后手拿,若对方拿R,那自己变先手从[L.R-1]上获取主动权去拿最好的
		);
	}

	public static int win2(int[] arr) {
		if (arr == null || arr.length == 0) {
			return 0;
		}
		int[][] f = new int[arr.length][arr.length];
		int[][] s = new int[arr.length][arr.length];
		for (int j = 0; j < arr.length; j++) {
			f[j][j] = arr[j];
			for (int i = j - 1; i >= 0; i--) {
				f[i][j] = Math.max(arr[i] + s[i + 1][j], arr[j] + s[i][j - 1]);
				s[i][j] = Math.min(f[i + 1][j], f[i][j - 1]);
			}
		}
		return Math.max(f[0][arr.length - 1], s[0][arr.length - 1]);
	}

	public static void main(String[] args) {
		int[] arr = { 1, 9, 1 };
		System.out.println(win1(arr));
		System.out.println(win2(arr));
	}

}
package class11;

/**
 * N皇后问题是指在N*N的棋盘上要摆N个皇后,
 * 要求任何两个皇后不同行、不同列, 也不在同一条斜线上
 * 给定一个整数n,返回n皇后的摆法有多少种。  n=1,返回1
 * n=2或3,2皇后和3皇后问题无论怎么摆都不行,返回0
 * n=8,返回92
 */
public class Code09_NQueens {

	/**
	 * 给定一个整数n,返回n皇后的摆法有多少种
	 */
	public static int num1(int n) {
		if (n < 1) {
			return 0;
		}
		// record[0] ?  record[1]  ?  record[2]
		int[] record = new int[n]; // record[i] -> i行的皇后,放在了第几列
		return process1(0, record, n);
	}

	/**
	 * 优化版:
	 * 位运算优化常数
	 * 请不要超过32皇后问题
	 */
	public static int num2(int n) {
		if (n < 1 || n > 32) {
			return 0;
		}
		// (1 << n) - 1的含义:假如n=4,1<<4-1=10000-1=01111,最终即生成一个二进后n位都是1,前面补0的数
		int limit = n == 32 ? -1 : (1 << n) - 1;
		return process2(limit, 0, 0, 0);
	}

	/**
	 * 潜台词:record[0..i-1]的皇后,任何两个皇后一定都不共行、不共列,不共斜线
	 * @param rowIndex 目前来到了第i行
	 * @param record record[0..i-1]表示之前的行,放了皇后的列位置
	 * @param n n代表整体一共有多少行
	 * @return 从第i行开始到第n行结束,在这些行上分别放皇后,满足条件的摆法有多少种
	 */
	public static int process1(int rowIndex, int[] record, int n) {
		if (rowIndex == n) { // 终止行是n-1行,此时rowIndex到达n行,说明已越过最后一行,即此分支的路整个走通了
			return 1;
		}
		int res = 0;
		for (int colIndex = 0; colIndex < n; colIndex++) { // 当前行在rowIndex行,尝试rowIndex行所有的列  -> colIndex
			// 当前rowIndex行的皇后,放在colIndex列,必须满足和之前(0..rowIndex-1)的皇后,不共行、不共列、不共斜线
			if (isValid(record, rowIndex, colIndex)) {
				record[rowIndex] = colIndex;
				res += process1(rowIndex + 1, record, n);
			}
		}
		return res;
	}

	/**
	 * 判断在rowIndex行的第colIndex列放皇后是否可以
	 * 需要根据record[0..rowIndex-1]行上已经放的皇后进行判断,不能同行、同列、对角线
	 * record[0..rowIndex-1]你需要看,record[rowIndex...]不需要看
	 */
	public static boolean isValid(int[] record, int rowIndex, int colIndex) {
		for (int k = 0; k < rowIndex; k++) { // 之前的某个k行的皇后
			if (
					// 2点同列
					colIndex == record[k]
							||
					// 2点在同一斜线上(即满足|列差值|=|行差值|)
					Math.abs(record[k] - colIndex) == Math.abs(rowIndex - k)
			) {
				return false;
			}
		}
		return true;
	}

	public static int process2(
			int limit,// 固定,划定问题规模,后n位都是1
			int colLim,// colLim 列的限制,1的位置不能放皇后,0的位置可以
			int leftDiaLim,// leftDiaLim 左斜线的限制,1的位置不能放皇后,0的位置可以
			int rightDiaLim// rightDiaLim 右斜线的限制,1的位置不能放皇后,0的位置可以
	) {
		if (colLim == limit) { // base case:列限制colLim的二进制形式的所有位上都是1,表示所有位置都放满皇后了,此分支有效
			return 1;
		}
		/**
		 * pos:pos的二进制位上是1的位置才可以放皇后
		 *
		 * colLim | leftDiaLim | rightDiaLim -> 总限制,二进制位上是1的不能放皇后,是0的可以放皇后,但是二进制前面补了很多0造成干扰
		 * ~(colLim | leftDiaLim | rightDiaLim) -> 总限制取反,二进制位上是0的不能放皇后,是1的可以放皇后,但是二进制前面补了很多0变成了1仍造成干扰
		 * limit & (~(colLim | leftDiaLim | rightDiaLim)) -> 按位与,都是1的才取1,过滤掉了二进制前面补足数的干扰,剩下的所有1就是放皇后的位置
		 */
		int pos = limit & (~(colLim | leftDiaLim | rightDiaLim));
		int mostRightOne = 0;
		int res = 0;
		// 将pos二进制位上是1的位置分别放皇后
		while (pos != 0) {
			mostRightOne = pos & (~pos + 1);// 只保留post二进制最右侧的1,其余为全变0
			pos = pos - mostRightOne;// 将post最右侧的1变0,下次再进while循环时提取的mostRightOne只保留post倒数第二位的1
			res += process2(limit, 
					colLim | mostRightOne,// 皇后放在mostRightOne二进制为1的位置,后续列不能放此位置了,此位置+当前对列的限制->对下一行列的限制
					(leftDiaLim | mostRightOne) << 1,// 当前左斜线限制+皇后放的位置,整体左移,即整体向左下角滑动一步,即下一行在这些位置上都不能放皇后
					// >>>无符号右移,高位补0
					(rightDiaLim | mostRightOne) >>> 1);// 当前右斜线限制+皇后放的位置,整体右移,即整体向右下角滑动一步,即下一行在这些位置上都不能放皇后
		}
		return res;
	}

	public static void main(String[] args) {
		int n = 14;

		long start = System.currentTimeMillis();
		System.out.println(num2(n));
		long end = System.currentTimeMillis();
		System.out.println("cost time: " + (end - start) + "ms");

		start = System.currentTimeMillis();
		System.out.println(num1(n));
		end = System.currentTimeMillis();
		System.out.println("cost time: " + (end - start) + "ms");

	}
}

class12

package class12;

public class Code01_RobotWalk {

	/**
	 * 解法1:暴力递归
	 *
	 * @param N 总共N个位置
	 * @param cur 从cur位置出发
	 * @param rest 还剩rest步
	 * @param P 最终要到达的位置
	 * @return 返回最终能达到P的方法数
	 */
	public static int ways1(int N, int cur, int rest, int P) {
		// 参数无效直接返回0
		if (N < 2 || rest < 1 || cur < 1 || cur > N || P < 1 || P > N) {
			return 0;
		}
		// 总共N个位置,从M点出发,还剩K步,返回最终能达到P的方法数
		return walk(N, cur, rest, P);
	}

	/**
	 * 解法2:记忆化搜索
	 */
	public static int waysCache(int N, int cur, int rest, int P) {
		if (N < 2 || rest < 1 || cur < 1 || cur > N || P < 1 || P > N) {
			return 0;
		}
		// 存储从cur位置出发,走rest步,到达P位置的走法。
		// 当前位置必在[0,N]之间,共N+1种可能
		// 剩余步数会逐步递减,必不会超过初始步数,必在[0,rest]之间,共rest+1种可能
		int[][] dp = new int[N + 1][rest + 1];
		// 全部初始化为-1,-1代表该位置之前没有计算结果,需要计算,若为其他值,代表之前算过了,直接返回该位置的值即可
		for (int i = 0; i <= N + 1; i++) {
			for (int j = 0; j <= rest + 1; j++) {
				dp[i][j] = -1;
			}
		}
		return walkCache(N, cur, rest, P, dp);
	}
	public static int walkCache(int N, int cur, int rest, int P,int[][] dp) {
		if (dp[cur][rest] != -1) {// 缓存中有结果,直接返回,减少重复计算
			return dp[cur][rest];
		}
		if (rest == 0) {
			dp[cur][rest] = cur == P ? 1 : 0;// 将结果缓存起来
			return dp[cur][rest];
		}
		if (cur == 1) {
			dp[cur][rest] = walkCache(N, 2, rest - 1, P, dp);// 将结果缓存起来
			return dp[cur][rest];
		}
		if (cur == N) {
			dp[cur][rest] = walkCache(N, N - 1, rest - 1, P, dp);// 将结果缓存起来
			return dp[cur][rest];
		}
		dp[cur][rest] = walkCache(N, cur + 1, rest - 1, P, dp) + walkCache(N, cur - 1, rest - 1, P, dp);// 将结果缓存起来
		return dp[cur][rest];
	}

	// N : 位置为1 ~ N,固定参数
	// cur : 当前在cur位置,可变参数
	// rest : 还剩res步没有走,可变参数
	// P : 最终目标位置是P,固定参数
	// 该函数的含义:只能在1~N这些位置上移动,当前在cur位置,走完rest步之后,停在P位置的方法数作为返回值返回
	public static int walk(int N, int cur, int rest, int P) {
		// 如果没有剩余步数了,当前的cur位置就是最后的位置
		// 如果最后的位置停在P上,那么之前做的移动是有效的
		// 如果最后的位置没在P上,那么之前做的移动是无效的
		if (rest == 0) {
			return cur == P ? 1 : 0;
		}
		// 如果还有rest步要走,而当前的cur位置在1位置上,那么当前这步只能从1走向2
		// 后续的过程就是,来到2位置上,还剩rest-1步要走
		if (cur == 1) {
			return walk(N, 2, rest - 1, P);
		}
		// 如果还有rest步要走,而当前的cur位置在N位置上,那么当前这步只能从N走向N-1
		// 后续的过程就是,来到N-1位置上,还剩rest-1步要走
		if (cur == N) {
			return walk(N, N - 1, rest - 1, P);
		}
		// 如果还有rest步要走,而当前的cur位置在中间位置上,那么当前这步可以走向左,也可以走向右
		// 走向左之后,后续的过程就是,来到cur-1位置上,还剩rest-1步要走
		// 走向右之后,后续的过程就是,来到cur+1位置上,还剩rest-1步要走
		// 走向左、走向右是截然不同的方法,所以总方法数要都算上
		return walk(N, cur + 1, rest - 1, P) + walk(N, cur - 1, rest - 1, P);
	}

	public static int ways2(int N, int M, int K, int P) {
		// 参数无效直接返回0
		if (N < 2 || K < 1 || M < 1 || M > N || P < 1 || P > N) {
			return 0;
		}
		int[][] dp = new int[K + 1][N + 1];
		dp[0][P] = 1;
		for (int i = 1; i <= K; i++) {
			for (int j = 1; j <= N; j++) {
				if (j == 1) {
					dp[i][j] = dp[i - 1][2];
				} else if (j == N) {
					dp[i][j] = dp[i - 1][N - 1];
				} else {
					dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j + 1];
				}
			}
		}
		return dp[K][M];
	}

	public static int ways3(int N, int M, int K, int P) {
		// 参数无效直接返回0
		if (N < 2 || K < 1 || M < 1 || M > N || P < 1 || P > N) {
			return 0;
		}
		int[] dp = new int[N + 1];
		dp[P] = 1;
		for (int i = 1; i <= K; i++) {
			int leftUp = dp[1];// 左上角的值
			for (int j = 1; j <= N; j++) {
				int tmp = dp[j];
				if (j == 1) {
					dp[j] = dp[j + 1];
				} else if (j == N) {
					dp[j] = leftUp;
				} else {
					dp[j] = leftUp + dp[j + 1];
				}
				leftUp = tmp;
			}
		}
		return dp[M];
	}

	// ways4是你的方法
	public static int ways4(int N, int M, int K, int P) {
		if (N < 2 || K < 1 || M < 1 || M > N || P < 1 || P > N) {
			return 0;
		}
		return process(N, 0, P, M, K);
	}

	// 一共N个位置,从M点出发,一共只有K步。返回走到位置j,剩余步数为i的方法数
	public static int process(int N, int i, int j, int M, int K) {
		if (i == K) {
			return j == M ? 1 : 0;
		}
		if (j == 1) {
			return process(N, i + 1, j + 1, M, K);
		}
		if (j == N) {
			return process(N, i + 1, j - 1, M, K);
		}
		return process(N, i + 1, j + 1, M, K) + process(N, i + 1, j - 1, M, K);
	}

	// ways5是你的方法的dp优化
	public static int ways5(int N, int M, int K, int P) {
		if (N < 2 || K < 1 || M < 1 || M > N || P < 1 || P > N) {
			return 0;
		}
		int[][] dp = new int[K + 1][N + 1];
		dp[K][M] = 1;
		for (int i = K - 1; i >= 0; 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[0][P];
	}

	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));
		System.out.println(ways4(7, 4, 9, 5));
		System.out.println(ways5(7, 4, 9, 5));
	}

}
package class12;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;

/**
 * 给定一个字符串str,给定一个字符串类型的数组arr。
 * arr里的每一个字符串,代表一张贴纸,你可以把单个字符剪开使用,目的是拼出str来。
 * 每张贴纸可以使用多次,返回需要至少多少张贴纸可以完成这个任务。
 * 例子:str= "babac",arr = {"ba","c","abcd"}
 * 至少需要两张贴纸"ba"和"abcd",因为使用这两张贴纸,把每一个字符单独剪开,含有2个a、2个b、1个c。是可以拼出str的。所以返回2。
 */
public class Code02_StickersToSpellWord {

	/**
	 * 划分方案1:
	 * 记忆化搜索
	 * @param stickers 都有哪些贴纸
	 * @param target 待拼接的字符串
	 * @return 拼出target至少需要的贴纸数
	 */
	public static int minStickers1(String[] stickers, String target) {
		int n = stickers.length;
		// 将每个贴纸都转成int[26],0位置代表贴纸字符串中有多少个a,1位置代表有多少个b,...,25位置代表有多少个z
		int[][] stickerMap = new int[n][26];
		for (int i = 0; i < n; i++) {
			char[] str = stickers[i].toCharArray();
			for (char c : str) {
				stickerMap[i][c - 'a']++;
			}
		}
		// 记忆化搜索傻缓存 k-字符串 v-拼接k至少需要的贴纸数
		HashMap<String, Integer> dp = new HashMap<>();
		dp.put("", 0);// 空字符串不需要贴纸
		return process1(dp, stickerMap, target);
	}

	/**
	 * @param dp 傻缓存
	 * @param stickerMap 贴纸数组转化而来,固定不变
	 * @param restStr ★★★★唯一可变参数:剩余待拼接字符串
	 * @return 拼出target至少需要的贴纸数
	 *
	 * ★★★★规定return -1代表贴纸品不出字符串
	 */
	public static int process1(HashMap<String, Integer> dp, int[][] stickerMap, String restStr) {
		if (dp.containsKey(restStr)) {
			return dp.get(restStr);
		}
		/**
		 * 将可变参数restStr转成线性结构:
		 * int[26],每位代表restStr中有多少个该位字符
		 */
		int[] restStrMap = new int[26];
		char[] restStrChars = restStr.toCharArray();

		for (char c : restStrChars) {
			restStrMap[c - 'a']++;
		}

		int ans = Integer.MAX_VALUE;// 先假设至少需要无穷多个贴纸
		int n = stickerMap.length;

		for (int i = 0; i < n; i++) {// 循环每个贴纸
			/**
			 * 判断贴纸有效性
			 * 当前贴纸中的字符 和 待拼接字符串的字符 无交集时,跳过当前贴纸
			 * eg: restStr="abc",贴纸=“xyz”,经"xyz"处理后str还是“abc”,"abc"没处理完,后面还要被贴纸“xyz”处理->栈溢出
			 */
			boolean available = false;// 当前贴纸有效性
			for (int k = 0; k < 26; k++) {
				// 待拼接字符串需要当前字符 && 当前贴纸有当前字符,只要帖只中有一个字符可用,该帖之就有用
				if (restStrMap[k] > 0 && stickerMap[i][k] > 0) {
					available = true;
					break;
				}
			}
			if (!available) {
				continue;
			}
			/**
			 * 判断贴纸有效性的另一种方法,感觉没太好
			 * 当前贴纸中 没有 待拼接字符串的第一个字符,贴纸无效
			 */
//			if (stickerMap[i][restStrChars[0] - 'a'] == 0) {
//				continue;
//			}

			// restStr使用当前贴纸后,还剩余的待拼接字符串(顺序不重要)
			StringBuilder restRestStr = new StringBuilder();
			for (int j = 0; j < 26; j++) {// 循环各个位置
				if (restStrMap[j] > 0) { // restStr包含当前位置的字符,才需要从当前贴纸取该字符
					/**
					 * x=使用当前贴纸后,待拼接字符串中还有多少当前字符没有得到拼接:
					 * x=restStrMap[j]-stickerMap[i][j]=待拼接字符串需要多少个当前字符-当前贴纸有多少当前字符
					 * 当前贴纸拥有的当前字符数 有可能多于 待拼接字符串需要的当前字符数,说明使用此贴纸后消去了所有当前字符
					 * eg: "abaabc"使用“a”贴纸后只去掉了一个a,restRestStr=“aabbc”
					 * eg: "abaabc"使用“aaaaaa”贴纸后去掉了三个a,restRestStr=“bbc”
					 */
					for (int k = 0; k < Math.max(0, restStrMap[j] - stickerMap[i][j]); k++) {
						restRestStr.append((char) ('a' + j));
					}
				}
			}
			/**
			 * 递归后续字符串,1 + tmp是使用当前贴纸时,拼出restStr需要的贴纸数
			 * tmp != -1 代表贴纸能拼出restRestStr,ans=1 + tmp
			 * tmp == -1 代表贴纸能拼不出restRestStr,ans 仍等于无穷大
			 */
			int tmp = process1(dp, stickerMap, restRestStr.toString());
			if (tmp != -1) {
				ans = Math.min(ans, 1 + tmp);
			}
		}
		dp.put(restStr, ans == Integer.MAX_VALUE ? -1 : ans);
		return dp.get(restStr);
	}

	public static int minStickers2(String[] stickers, String target) {
		int n = stickers.length;
		int[][] map = new int[n][26];
		for (int i = 0; i < n; i++) {
			char[] str = stickers[i].toCharArray();
			for (char c : str) {
				map[i][c - 'a']++;
			}
		}
		char[] str = target.toCharArray();
		int[] tmap = new int[26];
		for (char c : str) {
			tmap[c - 'a']++;
		}
		HashMap<String, Integer> dp = new HashMap<>();
		int ans = process2(map, 0, tmap, dp);
		return ans;
	}

	public static int process2(int[][] map, int i, int[] tmap, HashMap<String, Integer> dp) {
		StringBuilder keyBuilder = new StringBuilder();
		keyBuilder.append(i + "_");
		for (int asc = 0; asc < 26; asc++) {
			if (tmap[asc] != 0) {
				keyBuilder.append((char) (asc + 'a') + "_" + tmap[asc] + "_");
			}
		}
		String key = keyBuilder.toString();
		if (dp.containsKey(key)) {
			return dp.get(key);
		}
		boolean finish = true;
		for (int asc = 0; asc < 26; asc++) {
			if (tmap[asc] != 0) {
				finish = false;
				break;
			}
		}
		if (finish) {
			dp.put(key, 0);
			return 0;
		}
		if (i == map.length) {
			dp.put(key, -1);
			return -1;
		}
		int maxZhang = 0;
		for (int asc = 0; asc < 26; asc++) {
			if (map[i][asc] != 0 && tmap[asc] != 0) {
				maxZhang = Math.max(maxZhang, (tmap[asc] / map[i][asc]) + (tmap[asc] % map[i][asc] == 0 ? 0 : 1));
			}
		}
		int[] backup = Arrays.copyOf(tmap, tmap.length);
		int min = Integer.MAX_VALUE;
		int next = process2(map, i + 1, tmap, dp);
		tmap = Arrays.copyOf(backup, backup.length);
		if (next != -1) {
			min = next;
		}
		for (int zhang = 1; zhang <= maxZhang; zhang++) {
			for (int asc = 0; asc < 26; asc++) {
				tmap[asc] = Math.max(0, tmap[asc] - (map[i][asc] * zhang));
			}
			next = process2(map, i + 1, tmap, dp);
			tmap = Arrays.copyOf(backup, backup.length);
			if (next != -1) {
				min = Math.min(min, zhang + next);
			}
		}
		int ans = min == Integer.MAX_VALUE ? -1 : min;
		dp.put(key, ans);
		return ans;
	}

	public static void main(String[] args) {
		String str = "aabbacddaaaab";
		String[] arr = {"abddd","bbbbbbabbbb","cannx", "xyz"};

		System.out.println(minStickers1(arr,str));
		System.out.println(minStickers2(arr,str));
	}

}
package class12;

/**
 * 给定两个长度都为N的数组weights和values,
 * weights[i]和values[i]分别代表 i号物品的重量和价值。
 * 给定一个正数bag,表示一个载重bag的袋子,
 * 你装的物品不能超过这个重量。
 * 返回你能装下最多的价值是多少?
 */
public class Code03_Knapsack {

    public static int getMaxValue(int[] w, int[] v, int bag) {
        return process(w, v, 0, bag);
    }

    /**
     * 暴力递归
     *
     * @param index index之前的货物选与不选已定,不再操心
     * @param rest  bag的剩余空间
     * @return 对index及其后面的货物自由选择,返回能够获得的最大价值
     */
    public static int process(int[] w, int[] v, int index, int rest) {
        if (rest < 0) { // base case 1
            return -1;
        }
        if (index == w.length) { // base case 2
            return 0;
        }
        // 有货也有空间
        // 不选index位置的货物,求挑选index+1及其后位置的货物能够获得的最大价值
        int p1 = process(w, v, index + 1, rest);
        // 选择index位置的货物,bag剩余重量需减去index货物重量,求挑选index+1及其后位置的货物能够获得的最大价值
        int p2next = process(w, v, index + 1, rest - w[index]);
        int p2 = -1;
        if (p2next != -1) {
            p2 = v[index] + p2next;
        }
        return Math.max(p1, p2);
    }

    /**
     * 动态规划
     */
    public static int dpWay(int[] w, int[] v, int bag) {
        int N = w.length;
        // int[index][rest] dp 初始化时所有元素的值都是0
        // index范围[0,N]共N+1个数,准备N+1长度;rest范围[0,bag]共bag+1个数,准备bag+1长度
        int[][] dp = new int[N + 1][bag + 1];
        /**
         * 从dp[N][...]行向上逐行填充行
         * 根据暴力递归base case 2可得dp[N][...]这一行全都是0,上步初始化时已经赋0
         */
        // 从下到上,直到0行。为什么要从下到上?因为递归中process(index)是依赖process(index+1)的,所以先把index最大的求出来,小的index才能直接用
        for (int index = N - 1; index >= 0; index--) {
            // 从左到右,直到bag列
            for (int rest = 0; rest <= bag; rest++) {
                // 求dp[index][rest]的值
                int p1 = dp[index + 1][rest];// 当前是index行,因为是从下往上求值,所以index+1行的值都已经求过了
                int p2 = -1;
                if (rest - w[index] >= 0) {
                    p2 = v[index] + dp[index + 1][rest - w[index]];
                }
                dp[index][rest] = Math.max(p1, p2);
            }
        }
        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(getMaxValue(weights, values, bag));
        System.out.println(dpWay(weights, values, bag));
    }

}
package class12;

public class Code04_CardsInLine {

    public static int win1(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        return Math.max(// 最大的分数即获胜者分数
                f(arr, 0, arr.length - 1), // 先手在[0~L-1]获得的分数
                s(arr, 0, arr.length - 1) // 后手在[0~L-1]获得的分数
        );
    }

    /**
     * 先手,自己有当下这一步的选择权
     * 从arr的L-R范围上,如果先手拿牌,获得的最大分数
     */
    public static int f(int[] arr, int L, int R) {
        if (L == R) {
            return arr[L];
        }
        return Math.max(// 自己先手选择,当然选最大的
                arr[L] + s(arr, L + 1, R), // 自己选L,然后自己变后手,从[L+1,R]上获得的分数
                arr[R] + s(arr, L, R - 1)// 自己选R,然后自己变后手,从[L,R-1]上获得的分数
        );
    }

    /**
     * 后手,自己无当下这一步的选择权,即当下这一步的选择是先手方留给你的,先手方肯定扔给你最差的
     * 从arr的L-R范围上,如果后手拿牌,获得的最小分数(因为自己是后手,没有选择权,获得的分数由对方决定,而对方肯定给自己留最差的选择)
     */
    public static int s(int[] arr, int L, int R) {
        if (L == R) {// 仅剩L一张牌,又因为是后手拿牌,所以L肯定被对手拿走了,自己拿个锤子
            return 0;
        }
        return Math.min(// 先手绝顶聪明,肯定知道自己拿完后后手方的两种选择中哪个最差,留给对方
                f(arr, L + 1, R), // 对方拿完后,自己后手拿,若对方拿L,那自己变先手从[L+1.R]上获取主动权去拿最好的
                f(arr, L, R - 1)// 对方拿完后,自己后手拿,若对方拿R,那自己变先手从[L.R-1]上获取主动权去拿最好的
        );
    }


    public static int windp(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        int N = arr.length;
        int[][] f = new int[N][N];
        int[][] s = new int[N][N];
        for (int i = 0; i < N; i++) {
            f[i][i] = arr[i];
            s[i][i] = 0;
        }
        // 0,0 右下方移动
        // 0,1
        // 0,2
        // 0,N-1
        for (int col = 1; col < N; col++) {
            // 对角线出发位置(0,col)
            int L = 0;
            int R = col;
            while (L < N && R < N) {
                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]);
                L++;
                R++;
            }
        }
        return Math.max(f[0][N - 1], s[0][N - 1]);
    }

    /**
     * 我自己写的
     */
    public static int myDP(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        int N = arr.length;
        int[][] f = new int[N][N];
        int[][] s = new int[N][N];

        for (int i = 0; i < N; i++) {
            f[i][i] = arr[i];
            s[i][i] = 0;
        }

        for (int colIndex = 1; colIndex < N; colIndex++) {
            for (int row = 0, col = colIndex; row < N && col < N; row++, col++) {
                f[row][col] = Math.max(arr[row] + s[row + 1][col], arr[col] + s[row][col - 1]);
                s[row][col] = Math.min(f[row + 1][col], f[row][col - 1]);
            }
        }
        return Math.max(f[0][N - 1], s[0][N - 1]);
    }

    public static void main(String[] args) {
        int[] arr = {5, 7, 4, 5, 8, 1, 6, 0, 3, 4, 6, 1, 7};
        System.out.println(win1(arr));
        System.out.println(windp(arr));
        System.out.println(myDP(arr));

    }

}
package class12;

/**
 * 两个字符串的最长公共子序列问题
 */
public class Code05_LongestCommonSubsequence {

	public static int lcse(char[] str1, char[] str2) {
		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 j = 1; j < str2.length; j++) {
			dp[0][j] = Math.max(dp[0][j - 1], str1[0] == str2[j] ? 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) {

	}

}
package class12;

import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;

// 题目
// 数组arr代表每一个咖啡机冲一杯咖啡的时间,每个咖啡机只能串行的制造咖啡。
// 现在有n个人需要喝咖啡,只能用咖啡机来制造咖啡。
// 认为每个人喝咖啡的时间非常短,冲好的时间即是喝完的时间。
// 每个人喝完之后咖啡杯可以选择洗或者自然挥发干净,只有一台洗咖啡杯的机器,只能串行的洗咖啡杯。
// 洗杯子的机器洗完一个杯子时间为a,任何一个杯子自然挥发干净的时间为b。
// 四个参数:arr, n, a, b
// 假设时间点从0开始,返回所有人喝完咖啡并洗完咖啡杯的全部过程结束后,至少来到什么时间点。
public class Code06_Coffee {

	// 方法一:暴力尝试方法
	public static int minTime1(int[] arr, int n, int a, int b) {
		int[] times = new int[arr.length];
		int[] drink = new int[n];
		return forceMake(arr, times, 0, drink, n, a, b);
	}

	// 方法一,每个人暴力尝试用每一个咖啡机给自己做咖啡
	public static int forceMake(int[] arr, int[] times, int kth, int[] drink, int n, int a, int b) {
		if (kth == n) {
			int[] drinkSorted = Arrays.copyOf(drink, kth);
			Arrays.sort(drinkSorted);
			return forceWash(drinkSorted, a, b, 0, 0, 0);
		}
		int time = Integer.MAX_VALUE;
		for (int i = 0; i < arr.length; i++) {
			int work = arr[i];
			int pre = times[i];
			drink[kth] = pre + work;
			times[i] = pre + work;
			time = Math.min(time, forceMake(arr, times, kth + 1, drink, n, a, b));
			drink[kth] = 0;
			times[i] = pre;
		}
		return time;
	}

	// 方法一,暴力尝试洗咖啡杯的方式
	public static int forceWash(int[] drinks, int a, int b, int index, int washLine, int time) {
		if (index == drinks.length) {
			return time;
		}
		// 选择一:当前index号咖啡杯,选择用洗咖啡机刷干净
		int wash = Math.max(drinks[index], washLine) + a;
		int ans1 = forceWash(drinks, a, b, index + 1, wash, Math.max(wash, time));

		// 选择二:当前index号咖啡杯,选择自然挥发
		int dry = drinks[index] + b;
		int ans2 = forceWash(drinks, a, b, index + 1, washLine, Math.max(dry, time));
		return Math.min(ans1, ans2);
	}

	// 方法二:稍微好一点的解法
	public static class Machine {
		public int timePoint;
		public int workTime;

		public Machine(int t, int w) {
			timePoint = t;
			workTime = w;
		}
	}

	public static class MachineComparator implements Comparator<Machine> {

		@Override
		public int compare(Machine o1, Machine o2) {
			return (o1.timePoint + o1.workTime) - (o2.timePoint + o2.workTime);
		}

	}

	// 方法二,每个人暴力尝试用每一个咖啡机给自己做咖啡,优化成贪心
	public static int minTime2(int[] arr, int n, int a, int b) {
		PriorityQueue<Machine> heap = new PriorityQueue<Machine>(new MachineComparator());
		for (int i = 0; i < arr.length; i++) {
			heap.add(new Machine(0, arr[i]));
		}
		int[] drinks = new int[n];
		for (int i = 0; i < n; i++) {
			Machine cur = heap.poll();
			cur.timePoint += cur.workTime;
			drinks[i] = cur.timePoint;
			heap.add(cur);
		}
		return process(drinks, a, b, 0, 0);
	}

	// 方法二,洗咖啡杯的方式和原来一样,只是这个暴力版本减少了一个可变参数
	public static int process(int[] drinks, int a, int b, int index, int washLine) {
		if (index == drinks.length - 1) {
			return Math.min(Math.max(washLine, drinks[index]) + a, drinks[index] + b);
		}
		// wash是我当前的咖啡杯,洗完的时间
		int wash = Math.max(washLine, drinks[index]) + a;
		int next1 = process(drinks, a, b, index + 1, wash);
		int p1 = Math.max(wash, next1);
		int dry = drinks[index] + b;
		int next2 = process(drinks, a, b, index + 1, washLine);
		int p2 = Math.max(dry, next2);
		return Math.min(p1, p2);
	}

	// 方法三:最终版本,把方法二洗咖啡杯的暴力尝试进一步优化成动态规划
	public static int minTime3(int[] arr, int n, int a, int b) {
		PriorityQueue<Machine> heap = new PriorityQueue<Machine>(new MachineComparator());
		for (int i = 0; i < arr.length; i++) {
			heap.add(new Machine(0, arr[i]));
		}
		int[] drinks = new int[n];
		for (int i = 0; i < n; i++) {
			Machine cur = heap.poll();
			cur.timePoint += cur.workTime;
			drinks[i] = cur.timePoint;
			heap.add(cur);
		}
		if (a >= b) {
			return drinks[n - 1] + b;
		}
		int[][] dp = new int[n][drinks[n - 1] + n * a];
		for (int i = 0; i < dp[0].length; i++) {
			dp[n - 1][i] = Math.min(Math.max(i, drinks[n - 1]) + a, drinks[n - 1] + b);
		}
		for (int row = n - 2; row >= 0; row--) { // row 咖啡杯的编号
			int washLine = drinks[row] + (row + 1) * a;
			for (int col = 0; col < washLine; col++) {
				int wash = Math.max(col, drinks[row]) + a;
				dp[row][col] = Math.min(Math.max(wash, dp[row + 1][wash]), Math.max(drinks[row] + b, dp[row + 1][col]));
			}
		}
		return dp[0][0];
	}

	// for test
	public static int[] randomArray(int len, int max) {
		int[] arr = new int[len];
		for (int i = 0; i < len; i++) {
			arr[i] = (int) (Math.random() * max) + 1;
		}
		return arr;
	}

	// for test
	public static void printArray(int[] arr) {
		System.out.print("arr : ");
		for (int j = 0; j < arr.length; j++) {
			System.out.print(arr[j] + ", ");
		}
		System.out.println();
	}

	public static void main(String[] args) {
		int len = 5;
		int max = 9;
		int testTime = 50000;
		for (int i = 0; i < testTime; i++) {
			int[] arr = randomArray(len, max);
			int n = (int) (Math.random() * 5) + 1;
			int a = (int) (Math.random() * 5) + 1;
			int b = (int) (Math.random() * 10) + 1;
			int ans1 = minTime1(arr, n, a, b);
			int ans2 = minTime2(arr, n, a, b);
			int ans3 = minTime3(arr, n, a, b);
			if (ans1 != ans2 || ans2 != ans3) {
				printArray(arr);
				System.out.println("n : " + n);
				System.out.println("a : " + a);
				System.out.println("b : " + b);
				System.out.println(ans1 + " , " + ans2 + " , " + ans3);
				System.out.println("===============");
				break;
			}
		}

	}

}
package class12;

/**
 * 给定一个数组drinkFinishTimePoints,代表每个人喝完咖啡,可以开始刷杯子/自然挥发的时间点,从小到大有序排列
 * 只有一台清洗机,一次只能洗一个杯子,洗一个需要耗费a时间,只能串行洗
 * 每个咖啡杯也可以自己挥发干净,时间耗费b,咖啡杯可以并行挥发
 * 返回让所有咖啡杯变干净的最早完成时间
 * 共提供三个参数:int[] drinkFinishTimePoints、int a、int b
 */
public class Code06_MyCoffee {
    public static int minTime(int[] drinkFinishTimePoints, int a, int b) {
        return process(drinkFinishTimePoints, a, b, 0, 0);
    }

    /**
     * 暴力递归
     *
     * @param drinkFinishTimePoints 每个人喝完咖啡,可以开始刷杯子/自然挥发的时间点,从小到大有序排列
     * @param a                     洗一个杯子需要耗费的时间【固定值】
     * @param b                     挥发一个杯子需要耗费的时间【固定值】
     * @param index                 drinkFinishTimePoints[0...index-1]喝完的咖啡已经决定是洗还是挥发
     * @param washableTimePoint     清洗机下一次可以用的时间点(当前可能正在被占用)
     * @return 将drinks[index ...]中的所有杯子变干净的最早时间点
     */
    public static int process(int[] drinkFinishTimePoints, int a, int b, int index, int washableTimePoint) {
        if (index == drinkFinishTimePoints.length - 1) {// 最后一个杯子
            int dry = drinkFinishTimePoints[index] + b;// 挥发完的时间点
            // 可以开始刷杯子的时间点i vs 清洗机下一次空闲的时间点j: 如果i>j喝完咖啡就可以立即洗,如果i<j喝完咖啡需要等清洗机空闲才能洗
            int wash = Math.max(drinkFinishTimePoints[index] + a, washableTimePoint + a);
            return Math.min(dry, wash);
        }
        // 还有不止一个杯子
        //------------------------------------------------------------------
        // 采用挥发方案,后续杯子全部处理完的时间点:当前杯子不占用清洗机,washableTimePoint不变
        int laterTime = process(drinkFinishTimePoints, a, b, index + 1, washableTimePoint);
        // 采用挥发方案,当前杯子挥发完的时间点
        int curFinishTime = drinkFinishTimePoints[index] + b;
        // 采用挥发方案,当前杯子挥发完,后续杯子也处理完的时间点
        int dryEndTimePoint = Math.max(laterTime, curFinishTime);
        //-------------------------------------------------------------------
        // 采用清洗方案,后续杯子全部处理完的时间点: 当前杯子占用清洗机
        laterTime = process(drinkFinishTimePoints, a, b, index + 1, washableTimePoint + a);
        // 采用清洗方案,当前杯子清洗完的时间点
        curFinishTime = drinkFinishTimePoints[index] + a;
        int washEndTimePoint = Math.max(laterTime, curFinishTime);

        // 两种方案最先结束的
        return Math.min(dryEndTimePoint, washEndTimePoint);
    }

    /**
     * 动态规划
     *
     * @param drinkFinishTimePoints 每个人喝完咖啡,可以开始刷杯子/自然挥发的时间点,从小到大有序排列
     * @param a                     洗一个杯子需要耗费的时间【固定值】
     * @param b                     挥发一个杯子需要耗费的时间【固定值】
     * @return 将drinks[index ...]中的所有杯子变干净的最早时间点
     */
    public static int minTimeDP(int[] drinkFinishTimePoints, int a, int b) {
        int N = drinkFinishTimePoints.length;
        if (a >= b) {// 清洗耗费时间比挥发耗费时间还长,全部杯子都挥发,返回最后一个挥发完的时间点
            return drinkFinishTimePoints[N - 1] + b;
        }
        /**
         * 动态规划
         *
         * 组织dp[index][washableTimePoint]
         * index 0~N-1
         * washableTimePoint 0~washableTimePointEnd 最大值需要根据业务分析,最大值是所有杯子都清洗时最后一个清洗完的时间点
         */
        int washableTimePointEnd = 0;
        for (int index = 0; index < N; index++) {
            washableTimePointEnd = Math.max(washableTimePointEnd + a, drinkFinishTimePoints[index] + a);
        }
        int[][] dp = new int[N][washableTimePointEnd + 1];

        // base case:index=N-1时dp[N-1][...]=?
        for (int washableTimePoint = 0; washableTimePoint <= washableTimePointEnd; washableTimePoint++) {
            int dry = drinkFinishTimePoints[N - 1] + b;
            int wash = Math.max(drinkFinishTimePoints[N - 1] + a, washableTimePoint + a);
            dp[N - 1][washableTimePoint] = Math.min(dry, wash);
        }
        // dp[N-2 ...][0 ... ]
        for (int index = N - 2; index >= 0; index--) {
            for (int washableTimePoint = 0; washableTimePoint <= washableTimePointEnd; washableTimePoint++) {
                // 挥发
                int laterTime = dp[index + 1][washableTimePoint];
                int curFinishTime = drinkFinishTimePoints[index] + b;
                int dp1 = Math.max(laterTime, curFinishTime);
                // 清洗
                int dp2 = Integer.MAX_VALUE;
                if (washableTimePoint + a <= washableTimePointEnd) {// 洗完时间点不越界时,清洗方案才成立
                    laterTime = dp[index + 1][washableTimePoint + a];// 保证washableTimePoint + a不越界
                    curFinishTime = drinkFinishTimePoints[index] + a;
                    dp2 = Math.max(laterTime, curFinishTime);
                }
                dp[index][washableTimePoint] = Math.min(dp1, dp2);
            }
        }
        return dp[0][0];
    }

    public static void main(String[] args) {
        int[] drinkFinishTimePoints = new int[]{1,3,4,4,4,6,7,7,7,9,11};
        int a = 1;
        int b = 5;
        System.out.println(minTime(drinkFinishTimePoints,a,b));
        System.out.println(minTimeDP(drinkFinishTimePoints,a,b));
    }
}
package class12;

public class Code07_HorseJump {

	// 10*9
	// 0~9 y
	// 0~8 x
	public static int ways(int a, int b, int step) {
		return f(0, 0, step, a, b);
	}

	// 马在(i,j)位置,还有step步要去跳
	// 返回最终来到(a,b)的方法数
	public static int f(int i, int j, int step, int a, int b) {
		if (i < 0 || i > 9 || j < 0 || j > 8) {
			return 0;
		}
		if (step == 0) {
			return (i == a && j == b) ? 1 : 0;
		}
		return f(i - 2, j + 1, step - 1, a, b) 
				+ f(i - 1, j + 2, step - 1, a, b) 
				+ f(i + 1, j + 2, step - 1, a, b)
				+ f(i + 2, j + 1, step - 1, a, b) 
				+ f(i + 2, j - 1, step - 1, a, b) 
				+ f(i + 1, j - 2, step - 1, a, b)
				+ f(i - 1, j - 2, step - 1, a, b) 
				+ f(i - 2, j - 1, step - 1, a, b);

	}
	
	
	public static int waysdp(int a, int b, int s) {
		// (i,j,0~ step)
		int[][][] dp = new int[10][9][s+1];
		dp[a][b][0] = 1;
		for(int step = 1 ; step <= s;step++ ) { // 按层来
			for(int i = 0 ; i < 10;i++) {
				for(int j = 0 ; j < 9; j++) {
					dp[i][j][step] = getValue(dp,i - 2, j + 1, step - 1) 
							+ getValue(dp,i - 1, j + 2, step - 1) 
							+ getValue(dp,i + 1, j + 2, step - 1)
							+ getValue(dp,i + 2, j + 1, step - 1) 
							+ getValue(dp,i + 2, j - 1, step - 1) 
							+ getValue(dp,i + 1, j - 2, step - 1)
							+ getValue(dp,i - 1, j - 2, step - 1) 
							+ getValue(dp,i - 2, j - 1, step - 1);
				}
			}
		}
		return dp[0][0][s];
	}

	// 在dp表中,得到dp[i][j][step]的值,但如果(i,j)位置越界的话,返回0;
	public static int getValue(int[][][] dp, int i, int j, int step) {
		if (i < 0 || i > 9 || j < 0 || j > 8) {
			return 0;
		}
		return dp[i][j][step];
	}

	public static void main(String[] args) {
		int x = 7;
		int y = 7;
		int step = 10;
		System.out.println(ways(x, y, step));
		System.out.println(waysdp(x, y, step));
	}
}
package class12;

public class Code08_MinPathSum {

	public static int minPathSum1(int[][] m) {
		if (m == null || m.length == 0 || m[0] == null || m[0].length == 0) {
			return 0;
		}
		int row = m.length;
		int col = m[0].length;
		int[][] dp = new int[row][col];
		dp[0][0] = m[0][0];
		for (int i = 1; i < row; i++) {
			dp[i][0] = dp[i - 1][0] + m[i][0];
		}
		for (int j = 1; j < col; j++) {
			dp[0][j] = dp[0][j - 1] + m[0][j];
		}
		for (int i = 1; i < row; i++) {
			for (int j = 1; j < col; j++) {
				dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + m[i][j];
			}
		}
		return dp[row - 1][col - 1];
	}

	public static int minPathSum2(int[][] m) {
		if (m == null || m.length == 0 || m[0] == null || m[0].length == 0) {
			return 0;
		}
		int more = Math.max(m.length, m[0].length); // �����������ϴ���Ǹ�Ϊmore
		int less = Math.min(m.length, m[0].length); // ������������С���Ǹ�Ϊless
		boolean rowmore = more == m.length; // �����Dz��Ǵ��ڵ�������
		int[] arr = new int[less]; // ��������ij��Ƚ�Ϊ�����������е���Сֵ
		arr[0] = m[0][0];
		for (int i = 1; i < less; i++) {
			arr[i] = arr[i - 1] + (rowmore ? m[0][i] : m[i][0]);
		}
		for (int i = 1; i < more; i++) {
			arr[0] = arr[0] + (rowmore ? m[i][0] : m[0][i]);
			for (int j = 1; j < less; j++) {
				arr[j] = Math.min(arr[j - 1], arr[j])
						+ (rowmore ? m[i][j] : m[j][i]);
			}
		}
		return arr[less - 1];
	}

	// for test
	public static int[][] generateRandomMatrix(int rowSize, int colSize) {
		if (rowSize < 0 || colSize < 0) {
			return null;
		}
		int[][] result = new int[rowSize][colSize];
		for (int i = 0; i != result.length; i++) {
			for (int j = 0; j != result[0].length; j++) {
				result[i][j] = (int) (Math.random() * 10);
			}
		}
		return result;
	}

	// for test
	public static void printMatrix(int[][] matrix) {
		for (int i = 0; i != matrix.length; i++) {
			for (int j = 0; j != matrix[0].length; j++) {
				System.out.print(matrix[i][j] + " ");
			}
			System.out.println();
		}
	}

	public static void main(String[] args) {
		// int[][] m = generateRandomMatrix(3, 4);
		int[][] m = { { 1, 3, 5, 9 }, { 8, 1, 3, 4 }, { 5, 0, 6, 1 },
				{ 8, 8, 4, 0 } };
		printMatrix(m);
		System.out.println(minPathSum1(m));
		System.out.println(minPathSum2(m));

	}
}
package class12;

/**
 * 给定数组arr,arr中所有的值都为正数且不重复 每个值代表一种面值的货币,每种面值的货币可以使用任意张
 * 再给定一个整数 aim,代表要找的钱数 求组成 aim 的方法数
 * e.g: arr[5,10] aim=20  方法有3种:[5*4,5*2+10,10*2]
 * <p>
 * {@link #ways1}.暴力递归
 * {@link #ways2}.记忆化搜索
 * {@link #ways3}.经典动态规划
 * {@link #ways4}.优化经典动态规划枚举操作
 */
public class Code09_CoinsWay {

    /**
     * 1. 暴力递归
     * arr中都是正数且无重复值,返回组成aim的方法数
     */
    public static int ways1(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return 0;
        }
        return process1(arr, 0, aim);
    }

    /**
     * 使用arr[index ...]中的面额凑出rest这么多钱,有多少种凑法
     *
     * @param index 可以使用arr的index位置之后的所有面值
     * @param rest  需要凑的剩余钱数
     */
    public static int process1(int[] arr, int index, int rest) {
        if (index == arr.length) {// base case
            return rest == 0 ? 1 : 0;// 面额到头了,钱凑齐了则代表一种有效凑法
        }
        int ways = 0;
        // index位置的面额,可选[0~x]次,只要rest还没凑够
        for (int chooseTimes = 0; arr[index] * chooseTimes <= rest; chooseTimes++) {
            ways += process1(arr, index + 1, rest - arr[index] * chooseTimes);
        }
        return ways;
    }

    /**
     * 2.暴力递归 改 记忆化搜索
     */
    public static int ways2(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return 0;
        }
        // 缓存表 index范围[0~arr.length] aim可能值范围[0~aim]
        // 缓存表也可用HashMap替换,key可以是"index_rest"组合,可节省二维数组中有些用不到的空间,可省去二维数组初始化-1的步骤
        int[][] dp = new int[arr.length + 1][aim + 1];
        for (int i = 0; i < arr.length + 1; i++) {
            for (int j = 0; j < aim + 1; j++) {
                dp[i][j] = -1;
            }
        }
        return process2(arr, 0, aim, dp);
    }

    public static int process2(int[] arr, int index, int rest, int[][] dp) {
        if (dp[index][rest] != -1) {
            return dp[index][rest];
        }
        if (index == arr.length) {
            dp[index][rest] = rest == 0 ? 1 : 0;
            return dp[index][rest];// 面额到头了,钱凑齐了则代表一种有效凑法
        }
        int ways = 0;
        // index位置的面额,可选[0~x]次,只要rest还没凑够
        for (int chooseTimes = 0; arr[index] * chooseTimes <= rest; chooseTimes++) {
            dp[index + 1][rest - arr[index] * chooseTimes] =
                    process2(arr, index + 1, rest - arr[index] * chooseTimes, dp);
            ways += dp[index + 1][rest - arr[index] * chooseTimes];
        }
        return ways;
    }

    /**
     * 3.暴力递归 改 经典动态规划
     */
    public static int ways3(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return 0;
        }
        int N = arr.length;
        // 缓存表 index范围[0~arr.length] aim可能值范围[0~aim]
        int[][] dp = new int[N + 1][aim + 1];
        dp[N][0] = 1;// base case
        // 二维表 从下往上 从左往右 填充
        for (int index = N - 1; index >= 0; index--) {
            for (int rest = 0; rest <= aim; rest++) {
                dp[index][rest] = 0;
                // index位置的面额,可选[0~x]次,只要rest还没凑够
                for (int chooseTimes = 0; arr[index] * chooseTimes <= rest; chooseTimes++) {
                    dp[index][rest] += dp[index + 1][rest - arr[index] * chooseTimes];
                }
            }
        }
        return dp[0][aim];
    }

    /**
     * 4. 优化经典动态规划的枚举操作
     */
    public static int ways4(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return 0;
        }
        int N = arr.length;
        // 缓存表 index范围[0~arr.length] aim可能值范围[0~aim]
        int[][] dp = new int[N + 1][aim + 1];
        dp[N][0] = 1;// base case
        // 二维表 从下往上 从左往右 填充
        for (int index = N - 1; index >= 0; index--) {
            for (int rest = 0; rest <= aim; rest++) {
                /**
                 * 通过实际距离分析得到如下规律:
                 * 当前位置 = 下面的位置 + 向左边跳当前面额即arr[index]的位置
                 * 可运用该规律替换存在重复计算的枚举过程
                 */
                if (rest - arr[index] >= 0) {// 向左边跳的位置还不越界
                    dp[index][rest] = dp[index + 1][rest] + dp[index][rest - arr[index]];
                } else {// 向左边跳无可跳,当前位置 = 下面的位置
                    dp[index][rest] = dp[index + 1][rest];
                }
            }
        }
        return dp[0][aim];
    }

    public static void main(String[] args) {
        int[] arr = {5, 2, 3, 1};
        int sum = 350;
        System.out.println(ways1(arr, sum));
        System.out.println(ways2(arr, sum));
        System.out.println(ways3(arr, sum));
        System.out.println(ways4(arr, sum));
    }

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值