初级---算法

目录

 

1. 堆结构和堆排序

 2. 前缀树

3. 链表常见面试题

3.1 小于等于大于分区

3.2 复制带随机指针的链表

3.3 给定两个可能有环也可能没有环的单链表,如果相交,返回第一个交点

4.二叉树的基本算法

4.1 二叉树的前中后序遍历,层次遍历

4.2 获取树的最大宽度

4.3 二叉树的序列化和反序列化

4.4 二叉树的后继节点

4.5 打印纸带的凹凸折痕

5. 二叉树的递归套路

 5.1 给定一颗二叉树的头结点head,返回这棵树是不是平衡二叉树

5.2 判断一颗二叉树是不是满二叉树

5.3 判断是否是完全二叉树

5.4 给定一颗二叉树的头结点head,任何两个节点之间都存在距离,返回整颗二叉树的最大距离

5.5 返回二叉树中最大二叉搜索子树的节点数量

5.6 派对的最大快乐值

5.7 返回二叉树中最大二叉搜索子树的头结点

5.8 返回二叉树中两个节点a和b的最低公共祖先

6. 贪心算法

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

6.2 会议安排问题

6.3 放灯,给定一个字符串str,只由X和.两种字符组成,X表示墙,"."表示居民点,可以放灯,如果i位置放灯,,可以让i1,i和i+1位置被点亮,问至少需要几盏灯

6.4 分隔金条,求最小代价

6.5 花费数组costs,利润数组profits,最多能做的项目k,启动资金m,输出最后获得的最大钱数

7. 并查集

7.1 模板

7.2 如果user,如果a,b,c其中一个字段一样,就可以认为是一个人,请合并users,返回合并后的用户数量

8. 图

8.1 图结构模板

8.2 图的DFS

8.3 图的BFS

8.4 拓扑排序

8.5 图的最小生成树 克鲁斯卡算法(kruskal)

8.6 图的最小生成树 普利姆算法(Prim)

8.7 图的最短距离,迪杰斯特拉算法(dijkstra)

9. 暴力递归

9.1 汉罗塔问题

9.2 逆序一个栈,不能申请额外的数据结构

从左往右的尝试模型

9.3 打印一个字符串的全部子序列

9.4 打印一个字符串的全部排列

9.5 编码转化

9.6 背包问题: 给定两个长度为n的数组weight和values,给定一个载重为正数的袋子bag,最多能装的价值为多少

范围上尝试模型

9.7 给定一个数组arr,玩家A和玩家B每次只能拿最左或者最右的纸牌,玩家A和B都绝顶聪明,返回最后获胜者的分数

9.8 n皇后求最多数量

9.9 机器人走法

9.10 硬币合并问题,一共有多少种方式可以凑齐一个数

9.11 剪贴纸

多样本位置全对应的尝试模型

9.12 两个字符串的最长公共子序列问题

寻找业务限制的尝试模型

9.13 洗咖啡杯


1. 堆结构和堆排序

import java.util.Arrays;

public class MyMaxHeap {
    private int[] heap;
    private final int limit;
    private int heapSize;

    public MyMaxHeap(int limit) {
        this.limit = limit;
        heap = new int[limit];
        heapSize = 0;
    }

    public boolean isEmpty() {
        return heapSize == 0;
    }

    public boolean isFull() {
        return heapSize == limit;
    }

    public void push(int value) {
        if (heapSize == limit) {
            throw new RuntimeException("heap is full");
        }
        heap[heapSize] = value;
        heapInsert(heap, heapSize++);
    }

    public int pop() {
        int ans = heap[0];
        swap(heap, 0, --heapSize);
        heapify(heap, 0, heapSize);
        return ans;
    }

    private void heapInsert(int[] arr, int index) {
        //当arr[index]不比arr[index父]大了,或者index来到0位置了
        while (arr[index] > arr[(index - 1) / 2]) {
            swap(arr, index, (index - 1) / 2);
            index = (index - 1) / 2;
        }
    }

    //从index位置,往下看,不断的下沉
    private void heapify(int[] arr, int index, int heapSize) {
        int left = index * 2 + 1;
        while (left < heapSize) {
            int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
            largest = arr[largest] > arr[index] ? largest : index;
            if (largest == index) {
                break;
            }
            swap(arr, largest, index);
            index = largest;
            left = index * 2 + 1;
        }
    }

    private void swap(int[] arr, int l, int r) {
        int temp = arr[l];
        arr[l] = arr[r];
        arr[r] = temp;
    }

    //从小到大进行排序
    public void heapSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        // O(N*log(N))
//        for (int i = 0; i < arr.length; i++) {
//            heapInsert(arr, i);
//        }
        // O(N)
        for (int i = arr.length - 1; i >= 0; i--) {
            heapify(arr, i, arr.length);
        }
        int size = arr.length;
        swap(arr, 0, --size);
        // O(N*logN)
        while (size > 0) {
            heapify(arr, 0, size);
            swap(arr, 0, --size);
        }
    }

    public static void main(String[] args) {
        int arr[] = {3, 6, 4, 8, 4, 8, 4, 9, 4, 76, 2, 5, 1};
        MyMaxHeap heap = new MyMaxHeap(arr.length);
        for (int i = 0; i < arr.length; i++) {
            heap.push(arr[i]);
        }
        for (int i = 0; i < arr.length; i++) {
            int val = heap.pop();
            System.out.print(val + " ");
        }
        System.out.println();
        heap.heapSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

 2. 前缀树

public class TrieTree {
    private Node root;

    public TrieTree() {
        root = new Node();
    }

    //增加
    public void insert(String word) {
        if (word == null) {
            return;
        }
        char[] chars = word.toCharArray();
        Node node = root;
        node.pass++;
        int idx = 0;
        for (int i = 0; i < chars.length; i++) {
            idx = chars[i] - 'a';
            if (node.nexts[idx] == null) {
                node.nexts[idx] = new Node();
            }
            node = node.nexts[idx];
            node.pass++;
        }
        node.end++;
    }

    //删除
    public void delete(String word) {
        if (search(word) != 0) {
            char[] chars = word.toCharArray();
            Node node = root;
            node.pass--;
            int idx = 0;
            for (int i = 0; i < chars.length; i++) {
                idx = chars[i] - 'a';
                if (--node.nexts[idx].pass == 0) {
                    node.nexts[idx] = null;
                    return;
                }
            }
            node.end--;
        }
    }

    //查询
    public int search(String word) {
        if (word == null) {
            return 0;
        }
        char[] chars = word.toCharArray();
        Node node = root;
        int idx = 0;
        for (int i = 0; i < chars.length; i++) {
            idx = chars[i] - 'a';
            if (node.nexts[idx] == null) {
                return 0;
            }
            node = node.nexts[idx];
        }
        return node.end;
    }

    //按前缀查
    public int searchPre(String word) {
        if (word == null) {
            return 0;
        }
        char[] chars = word.toCharArray();
        Node node = root;
        int idx = 0;
        for (int i = 0; i < chars.length; i++) {
            idx = chars[i] - 'a';
            if (node.nexts[idx] == null) {
                return 0;
            }
            node = node.nexts[idx];
        }
        return node.pass;
    }
}
class Node{
    int pass;
    int end;
    Node[] nexts;

    public Node() {
        nexts = new Node[26];
    }
}

3. 链表常见面试题

3.1 小于等于大于分区

    public static Node listPartition(Node head, int proit) {
        Node sH = null;
        Node sT = null;
        Node eH = null;
        Node eT = null;
        Node bH = null;
        Node bT = null;
        Node next = null;
        while (head != null) {
            next = head.next;
            head.next = null;
            if (head.val < proit) {
                if (sH == null) {
                    sH = head;
                    sT = head;
                } else {
                    sT.next = head;
                    sT = head;
                }
            } else if (head.val == proit) {
                if (eH == null) {
                    eH = head;
                    eT = head;
                } else {
                    eT.next = head;
                    eT = head;
                }
            } else {
                if (bH == null) {
                    bH = head;
                    bT = head;
                } else {
                    bT.next = head;
                    bT = head;
                }
            }
            head = next;
        }
        if (sT != null) {
            sT.next = eH;
            eT = eT == null ? sT : eT;
        }
        if (eT != null) {
            eT.next = bH;
        }
        return sH != null ? sH : (eH != null ? eH : bH);
    }

3.2 复制带随机指针的链表

    //方法一,使用map集合存储所有的节点
    public Node copyRandomList(Node head) {
        Map<Node, Node> map = new HashMap<>();
        Node p = head;
        while (p != null) {
            map.put(p, new Node(p.val));
            p = p.next;
        }
        p = head;
        while (p != null) {
            map.get(p).next = map.get(p.next);
            map.get(p).random = map.get(p.random);
            p = p.next;
        }
        return map.get(head);
    }
    //方法二,先将要插入的节点插入原链表,再分割
    public Node copyRandomList(Node head) {
        if (head == null) {
            return head;
        }
        Node curr = head;
        Node next = null;
        while (curr != null) {
            next = curr.next;
            Node newNode = new Node(curr.val);
            newNode.next = next;
            curr.next = newNode;
            curr = next;
        }
        curr = head;
        Node copyNode = null;
        while (curr != null) {
            next = curr.next.next;
            copyNode = curr.next;
            copyNode.random = curr.random == null ? null : curr.random.next;
            curr = next;
        }
        Node res = head.next;
        curr = head;
        while (curr != null) {
            next = curr.next.next;
            copyNode = curr.next;
            curr.next = next;
            copyNode.next = next != null ? next.next : null;
            curr = next;
        }
        return res;
    }

3.3 给定两个可能有环也可能没有环的单链表,如果相交,返回第一个交点

public class Main {

    // 给定两个可能有环也可能没有环的单链表,如果相交,返回第一个交点
    public static Node getIntersectNode(Node head1, Node head2) {
        if (head1 == null || head2 == null) {
            return null;
        }
        Node loop1 = getLoopNode(head1);
        Node loop2 = getLoopNode(head2);
        if (loop1 == null && loop2 == null) {
            return noLoop(head1, head2);
        }
        if (loop1 != null && loop2 != null) {
            return bothLoop(head1, loop1, head2, loop2);
        }
        return null;
    }

    //如果有环,返回入环节点,否则返回null
    public static Node getLoopNode(Node head) {
        if (head == null || head.next == null) {
            return null;
        }
        Node slow = head.next;
        Node fast = head.next.next;
        while (fast != slow) {
            if (fast == null || fast.next == null) {
                return null;
            }
            slow = slow.next;
            fast = fast.next.next;
            if (fast == slow) {
                break;
            }
        }
        fast = head;
        while (slow != fast) {
            fast = fast.next;
            slow = slow.next;
        }
        return fast;
    }

    //如果两个链表都无环,返回第一个相交节点,如果不想交,返回null
    public static Node noLoop(Node head1, Node head2) {
        if (head1 == null || head2 == null) {
            return null;
        }
        Node p1 = head1;
        Node p2 = head2;
        int n = 0;
        while (p1.next != null) {
            p1 = p1.next;
            n++;
        }
        while (p2.next != null) {
            p2 = p2.next;
            n--;
        }
        if (p1 != p2) {
            return null;
        }
        p1 = n > 0 ? head1 : head2;
        p2 = p1 == head1 ? head2 : head1;
        n = Math.abs(n);
        while (n > 0) {
            n--;
            p1 = p1.next;
        }
        while (p1 != p2) {
            p1 = p1.next;
            p2 = p2.next;
        }
        return p1;
    }

    // 两个有环链表,返回第一个相交节点,如果不想交返回null
    public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
        Node cur1 = null;
        Node cur2 = null;
        if (loop1 == loop2) {
            cur1 = head1;
            cur2 = head2;
            int n = 0;
            while (cur1 != loop1) {
                n++;
                cur1 = cur1.next;
            }
            while (cur2 != loop2) {
                n--;
                cur2 = cur2.next;
            }
            cur1 = n > 0 ? head1 : head2;
            cur2 = cur1 == head1 ? head2 : head1;
            n = Math.abs(n);
            while (n != 0) {
                n--;
                cur1 = cur1.next;
            }
            while (cur1 != cur2) {
                cur1 = cur1.next;
                cur2 = cur2.next;
            }
            return cur1;
        } else {
            cur1 = loop1.next;
            while (cur1 != loop1) {
                if (cur1 == loop2) {
                    return loop1;
                }
                cur1 = cur1.next;
            }
            return null;
        }
    }

    public static void main(String[] args) {
        // 1->2->3->4->5->6->7->null
        Node head1 = new Node(1);
        head1.next = new Node(2);
        head1.next.next = new Node(3);
        head1.next.next.next = new Node(4);
        head1.next.next.next.next = new Node(5);
        head1.next.next.next.next.next = new Node(6);
        head1.next.next.next.next.next.next = new Node(7);

        // 0->9->8->6->7->null
        Node head2 = new Node(0);
        head2.next = new Node(9);
        head2.next.next = new Node(8);
        head2.next.next.next = head1.next.next.next.next.next; // 8->6
        System.out.println(getIntersectNode(head1, head2).val);

        // 1->2->3->4->5->6->7->4...
        head1 = new Node(1);
        head1.next = new Node(2);
        head1.next.next = new Node(3);
        head1.next.next.next = new Node(4);
        head1.next.next.next.next = new Node(5);
        head1.next.next.next.next.next = new Node(6);
        head1.next.next.next.next.next.next = new Node(7);
        head1.next.next.next.next.next.next = head1.next.next.next; // 7->4

        // 0->9->8->2...
        head2 = new Node(0);
        head2.next = new Node(9);
        head2.next.next = new Node(8);
        head2.next.next.next = head1.next; // 8->2
        System.out.println(getIntersectNode(head1, head2).val);

        // 0->9->8->6->4->5->6..
        head2 = new Node(0);
        head2.next = new Node(9);
        head2.next.next = new Node(8);
        head2.next.next.next = head1.next.next.next.next.next; // 8->6
        System.out.println(getIntersectNode(head1, head2).val);

    }
}

class Node {
    int val;
    Node next;

    public Node(int val) {
        this.val = val;
    }
}

4.二叉树的基本算法

4.1 二叉树的前中后序遍历,层次遍历

import java.util.*;

public class Main {
    //二叉树的前序遍历
    public static void pre(Node root) {
        if (root == null) {
            return;
        }
        Stack<Node> stack = new Stack<>();
        stack.add(root);
        while (!stack.isEmpty()) {
            root = stack.pop();
            System.out.print(root.val + " ");
            if (root.right != null) {
                stack.add(root.right);
            }
            if (root.left != null) {
                stack.add(root.left);
            }
        }
        System.out.println();
    }

    //二叉树的中序遍历
    public static void in(Node root) {
        if (root == null) {
            return;
        }
        Stack<Node> stack = new Stack<>();
        while (!stack.isEmpty() || root != null) {
            while (root != null) {
                stack.add(root);
                root = root.left;
            }
            root = stack.pop();
            System.out.print(root.val + " ");
            root = root.right;
        }
        System.out.println();
    }

    //二叉树的后序遍历
    public static void pos(Node root) {
        if (root == null) {
            return;
        }
        Stack<Node> stack1 = new Stack<>();
        Stack<Node> stack2 = new Stack<>();
        stack1.add(root);
        while (!stack1.isEmpty()) {
            root = stack1.pop();
            stack2.add(root);
            if (root.left != null) {
                stack1.add(root.left);
            }
            if (root.right != null) {
                stack1.add(root.right);
            }
        }
        while (!stack2.isEmpty()) {
            System.out.print(stack2.pop().val + " ");
        }
        System.out.println();
    }

    //二叉树的层次遍历
    public List<List<Integer>> levelOrder(Node root) {
        List<List<Integer>> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        Queue<Node> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            int size = queue.size();
            List<Integer> list = new ArrayList<>();
            for (int i = 0; i < size; i++) {
                root = queue.poll();
                list.add(root.val);
                if (root.left != null) {
                    queue.add(root.left);
                }
                if (root.right != null) {
                    queue.add(root.right);
                }
            }
            res.add(list);
        }
        return res;
    }
}

class Node {
    int val;
    Node left;
    Node right;

    public Node(int val) {
        this.val = val;
    }
}

4.2 获取树的最大宽度

public class Main {
    public static int maxWidthUseMap(Node head) {
        if (head == null) {
            return 0;
        }
        Queue<Node> queue = new LinkedList<>();
        queue.add(head);
        Map<Node, Integer> map = new HashMap<>();
        map.put(head, 1);
        int currLevel = 1;
        int currLevelNodes = 0;
        int ans = 0;
        while (!queue.isEmpty()) {
            Node curr = queue.poll();
            int currNodeLevel = map.get(curr);
            if (curr.left != null) {
                map.put(curr.left, currNodeLevel + 1);
                queue.add(curr.left);
            }
            if (curr.right != null) {
                map.put(curr.right, currNodeLevel + 1);
                queue.add(curr.right);
            }
            if (currNodeLevel == currLevel) {
                currLevelNodes++;
            } else {
                ans = Math.max(ans, currLevelNodes);
                currLevel++;
                currLevelNodes = 1;
            }
        }
        ans = Math.max(ans, currLevelNodes);
        return ans;
    }

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


    // for test
    public static Node generateRandomBST(int maxLevel, int maxValue) {
        return generate(1, maxLevel, maxValue);
    }
    // for test
    public static Node generate(int level, int maxLevel, int maxValue) {
        if (level > maxLevel || Math.random() < 0.5) {
            return null;
        }
        Node head = new Node((int) (Math.random() * maxValue));
        head.left = generate(level + 1, maxLevel, maxValue);
        head.right = generate(level + 1, maxLevel, maxValue);
        return head;
    }
    public static void main(String[] args) {
        int maxLevel = 10;
        int maxValue = 100;
        int testTimes = 1000000;
        for (int i = 0; i < testTimes; i++) {
            Node head = generateRandomBST(maxLevel, maxValue);
            if (maxWidthUseMap(head) != maxWidthNoMap(head)) {
                System.out.println("Oops!");
            }
        }
        System.out.println("finish!");
    }
}

class Node {
    int val;
    Node left;
    Node right;

    public Node(int val) {
        this.val = val;
    }
}

4.3 二叉树的序列化和反序列化

public class Main {
    //先序遍历的序列化和反序列化
    public static Queue<String> preSerial(Node head) {
        Queue<String> ans = new LinkedList<>();
        pres(head, ans);
        return ans;
    }
    private static void pres(Node head, Queue<String> ans) {
        if (head == null) {
            ans.add(null);
        } else {
            ans.add(String.valueOf(head.val));
            pres(head.left, ans);
            pres(head.right, ans);
        }
    }
    public static Node buildByPreQueue(Queue<String> preList) {
        if (preList == null || preList.size() == 0) {
            return null;
        }
        return preb(preList);
    }
    private static Node preb(Queue<String> preList) {
        String val = preList.poll();
        if (val == null) {
            return null;
        }
        Node head = new Node(Integer.valueOf(val));
        head.left = preb(preList);
        head.right = preb(preList);
        return head;
    }

    //通过队列的方式实现序列化和反序列化
    public static Queue<String> levelSerial(Node head) {
        Queue<String> ans = new LinkedList<>();
        if (head == null) {
            ans.add(null);
        } else {
            ans.add(String.valueOf(head.val));
            Queue<Node> queue = new LinkedList<>();
            queue.add(head);
            while (!queue.isEmpty()) {
                head = queue.poll();
                if (head.left != null) {
                    ans.add(String.valueOf(head.left.val));
                    queue.add(head.left);
                } else {
                    ans.add(null);
                }
                if (head.right != null) {
                    ans.add(String.valueOf(head.right.val));
                    queue.add(head.right);
                } else {
                    ans.add(null);
                }
            }
        }
        return ans;
    }
    public static Node buildByLevelQueue(Queue<String> levelList) {
        if (levelList == null || levelList.size() == 0) {
            return null;
        }
        Node head = generateNode(levelList.poll());
        Queue<Node> queue = new LinkedList<>();
        if (head != null) {
            queue.add(head);
        }
        Node node = null;
        while (!queue.isEmpty()) {
            node = queue.poll();
            node.left = generateNode(levelList.poll());
            node.right = generateNode(levelList.poll());
            if (node.left != null) {
                queue.add(node.left);
            }
            if (node.right != null) {
                queue.add(node.right);
            }
        }
        return head;
    }
    private static Node generateNode(String val) {
        if (val == null) {
            return null;
        }
        return new Node(Integer.valueOf(val));
    }

    //后序遍历序列化和反序列化
    public static Queue<String> posSerial(Node head) {
        Queue<String> ans = new LinkedList<>();
        poss(head, ans);
        return ans;
    }
    private static void poss(Node head, Queue<String> ans) {
        if (head == null) {
            ans.add(null);
        } else {
            poss(head.left, ans);
            poss(head.right, ans);
            ans.add(String.valueOf(head.val));
        }
    }
    public static Node buildByPostQueue(Queue<String> poslist) {
        if (poslist == null || poslist.size() == 0) {
            return null;
        }
        Stack<String> stack = new Stack<>();
        while (!poslist.isEmpty()) {
            stack.push(poslist.poll());
        }
        return posb(stack);
    }
    private static Node posb(Stack<String> posStack) {
        String val = posStack.pop();
        if (val == null) {
            return null;
        }
        Node node = new Node(Integer.parseInt(val));
        node.right = posb(posStack);
        node.left = posb(posStack);
        return node;
    }
}
class Node {
    int val;
    Node left;
    Node right;

    public Node(int val) {
        this.val = val;
    }
}

4.4 二叉树的后继节点

public class Main {
    //获取一个节点的后继节点
    public static Node getSuccessorNode(Node node) {
        if (node == null) {
            return null;
        }
        if (node.right != null) {
            return getLeftMost(node.right);
        } else {
            Node parent = node.parent;
            while (parent != null && parent.left != node) {
                node = parent;
                parent = node.parent;
            }
            return parent;
        }
    }
    private static Node getLeftMost(Node node) {
        if (node == null) {
            return null;
        }
        while (node.left != null) {
            node = node.left;
        }
        return node;
    }
}
class Node {
    int val;
    Node left;
    Node right;
    Node parent;

    public Node(int val) {
        this.val = val;
    }
}

4.5 打印纸带的凹凸折痕

public class Main {
    private static void printAllFolds(int n) {
        printProcess(1, n, true);
    }
    public static void printProcess(int i, int n, boolean down) {
        if (i > n) {
            return;
        }
        printProcess(i + 1, n, true);
        System.out.println(down ? "凹" : "凸");
        printProcess(i + 1, n, false);
    }
}

5. 二叉树的递归套路

用于解决大多数二叉树尤其是书信dp的问题

1) 假设以X节点为头,假设可以向X左树和X右树要任何信息

2) 在上一步的假设下,讨论以X为头结点的树,得到答案的可能性(最重要)

常见分类:和X有关信息,和X无关信息

3) 列出所有可能性后,确定到底需要向左树和右树要什么样的信息

4) 把左树信息和右树信息求全集,就是任何一颗子树都需要返回的信息S

5) 递归函数都返回S,每一颗子树都这么要求

6) 写代码,在代码中考虑如何把左树信息和右树信息整合出整棵树的信息

 5.1 给定一颗二叉树的头结点head,返回这棵树是不是平衡二叉树

class Solution {
    public boolean isBalanced(TreeNode root) {
        return process(root).isBanance;
    }
    public Info process(TreeNode node) {
        if (node == null) {
            return new Info(true, 0);
        }
        Info leftInfo = process(node.left);
        Info rightInfo = process(node.right);
        int hight = Math.max(leftInfo.hight, rightInfo.hight) + 1;
        boolean isBalance = true;
        if (!leftInfo.isBanance || !rightInfo.isBanance || Math.abs(leftInfo.hight - rightInfo.hight) > 1) {
            isBalance = false;
        }
        return new Info(isBalance, hight);
    }
}
class Info {
    boolean isBanance;
    int hight;

    public Info(boolean isBanance, int hight) {
        this.isBanance = isBanance;
        this.hight = hight;
    }
}

5.2 判断一颗二叉树是不是满二叉树

public class Solution {
    public static boolean isFull1(Node head) {
        if (head == null) {
            return true;
        }
        int height = h(head);
        int nodes = n(head);
        return 2 * height - 1 == nodes;
    }
    private static int h(Node head) {
        if (head == null) {
            return 0;
        }
        return Math.max(h(head.left), h(head.right)) + 1;
    }
    private static int n(Node head) {
        if (head == null) {
            return 0;
        }
        return n(head.left) + n(head.right) + 1;
    }

    public static boolean isFull2(Node head) {
        if (head == null) {
            return true;
        }
        Info all = process(head);
        return all.height * 2 - 1 == all.nodes;
    }
    private static Info process(Node head) {
        if (head == null) {
            return new Info(0, 0);
        }
        Info leftInfo = process(head.left);
        Info rightInfo = process(head.right);
        int height = Math.max(leftInfo.height, rightInfo.height) + 1;
        int nodes = leftInfo.nodes + rightInfo.nodes + 1;
        return new Info(height, nodes);
    }
}
class Info {
    int height;
    int nodes;

    public Info(int height, int nodes) {
        this.height = height;
        this.nodes = nodes;
    }
}

5.3 判断是否是完全二叉树

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

public class Solution{
    public static boolean isCST(Node head) {
        if (head == null) {
            return true;
        }
        Queue<Node> queue = new LinkedList<>();
        queue.add(head);
        boolean flag = false;
        while (!queue.isEmpty()) {
            head = queue.poll();
            Node l = head.left;
            Node r = head.right;
            if ((r != null && l == null) || (flag && !(l == null && r == null))) {
                return false;
            }
            if (l != null) {
                queue.add(l);
            }
            if (r != null) {
                queue.add(r);
            }
            if (l == null || r == null) {
                flag = true;
            }
        }
        return true;
    }

    public static boolean isCBT2(Node head) {
        if (head == null) {
            return true;
        }
        Info all = process(head);
        return all.isCBT;
    }
    private static Info process(Node head) {
        if (head == null) {
            return new Info(true, true, 0);
        }
        Info leftInfo = process(head.left);
        Info rightInfo = process(head.right);
        boolean isFull = leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height;
        int height = Math.max(leftInfo.height, rightInfo.height) + 1;
        boolean isCBT = false;
        if (isFull) {
            isCBT = true;
        } else {
            if (leftInfo.isCBT && rightInfo.isCBT) {
                //左满,右满,高度差1
                if (leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) {
                    isCBT = true;
                //左完全,右满,高度差1
                } else if (leftInfo.isCBT && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) {
                    isCBT = true;
                //左满,右完全,高度相同
                } else if (leftInfo.isFull && rightInfo.isCBT && leftInfo.height == rightInfo.height) {
                    isCBT = true;
                }
            }
        }
        return new Info(isFull, isCBT, height);
    }

    public static Node generateRandomBST(int maxLevel, int maxValue) {
        return generate(1, maxLevel, maxValue);
    }
    public static Node generate(int level, int maxLevel, int maxValue) {
        if (level > maxLevel || Math.random() < 0.5) {
            return null;
        }
        Node head = new Node((int) (Math.random() * maxValue));
        head.left = generate(level + 1, maxLevel, maxValue);
        head.right = generate(level + 1, maxLevel, maxValue);
        return head;
    }
    public static void main(String[] args) {
        int maxLevel = 5;
        int maxValue = 100;
        int testTimes = 10000000;
        for (int i = 0; i < testTimes; i++) {
            Node head = generateRandomBST(maxLevel, maxValue);
            if (isCST(head) != isCBT2(head)) {
                System.out.println("Oops!");
            }
        }
        System.out.println("finish");
    }
}
class Info{
    boolean isFull;
    boolean isCBT;
    int height;

    public Info(boolean isFull, boolean isCBT, int height) {
        this.isFull = isFull;
        this.isCBT = isCBT;
        this.height = height;
    }
}
class Node{
    int val;
    Node left;
    Node right;

    public Node(int val) {
        this.val = val;
    }
}

5.4 给定一颗二叉树的头结点head,任何两个节点之间都存在距离,返回整颗二叉树的最大距离

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

    public static Info process(Node head) {
        if (head == null) {
            return new Info(0, 0);
        }
        Info leftInfo = process(head.left);
        Info rightInfo = process(head.right);
        int hight = Math.max(leftInfo.hight, rightInfo.hight) + 1;
        int maxDistance = Math.max(Math.max(leftInfo.maxDistance, rightInfo.maxDistance), leftInfo.hight + rightInfo.hight + 1);
        return new Info(maxDistance, hight);
    }
    
}
class Info{
    int maxDistance;
    int hight;

    public Info(int maxDistance, int hight) {
        this.maxDistance = maxDistance;
        this.hight = hight;
    }
}

5.5 返回二叉树中最大二叉搜索子树的节点数量

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

    public static Info process(Node head) {
        if (head == null) {
            return null;
        }
        Info leftInfo = process(head.left);
        Info rightInfo = process(head.right);
        int max = head.val;
        int min = head.val;
        int maxSubBSTSize = 0;
        if (leftInfo != null) {
            max = Math.max(max, leftInfo.max);
            min = Math.min(min, leftInfo.min);
            maxSubBSTSize = Math.max(maxSubBSTSize, leftInfo.maxSubBSTSize);
        }
        if (rightInfo != null) {
            max = Math.max(max, rightInfo.max);
            min = Math.min(min, rightInfo.min);
            maxSubBSTSize = Math.max(maxSubBSTSize, rightInfo.maxSubBSTSize);
        }
        boolean isBST = false;
        //左树整体是二叉搜索树
        if ((leftInfo == null ? true : (leftInfo.isBST && leftInfo.max < head.val)) &&
                //右树整体是二叉搜索树
                (rightInfo == null ? true : (rightInfo.isBST && rightInfo.min > head.val))) {
            isBST = true;
            maxSubBSTSize = (leftInfo == null ? 0 : leftInfo.maxSubBSTSize) + (rightInfo == null ? 0 : rightInfo.maxSubBSTSize) + 1;
        }
        return new Info(isBST, maxSubBSTSize, max, min);
    }
}

class Info {
    boolean isBST;
    int maxSubBSTSize;
    int max;
    int min;

    public Info(boolean isBST, int maxSubBSTSize, int max, int min) {
        this.isBST = isBST;
        this.maxSubBSTSize = maxSubBSTSize;
        this.max = max;
        this.min = min;
    }
}

5.6 派对的最大快乐值

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

public class Solution{
    public static int maxHappy(Employee boss) {
        if (boss == null) {
            return 0;
        }
        Info all = process(boss);
        return Math.max(all.yes, all.no);
    }

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

    public Info(int yes, int no) {
        this.yes = yes;
        this.no = no;
    }
}
class Employee{
    int happy;
    List<Employee> nexts;

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

5.7 返回二叉树中最大二叉搜索子树的头结点

public class Solution {
    public static Node maxSubBSTHead(Node head) {
        if (head == null) {
            return null;
        }
        return process(head).maxSubBSTHead;
    }
    public static Info process(Node head) {
        if (head == null) {
            return null;
        }
        Info leftInfo = process(head.left);
        Info rightInfo = process(head.right);
        Node maxSubBSTHead = null;
        int maxSubBSTSize = 0;
        int max = head.val;
        int min = head.val;
        if (leftInfo != null) {
            min = Math.min(min, leftInfo.min);
            max = Math.max(max, leftInfo.max);
            maxSubBSTSize = leftInfo.maxSubBSTSize;
            maxSubBSTHead = leftInfo.maxSubBSTHead;
        }
        if (rightInfo != null) {
            min = Math.min(min, rightInfo.min);
            max = Math.max(max, rightInfo.max);
            if (rightInfo.maxSubBSTSize > maxSubBSTSize) {
                maxSubBSTHead = rightInfo.maxSubBSTHead;
                maxSubBSTSize = rightInfo.maxSubBSTSize;
            }
        }
        if ((leftInfo == null ? true : (leftInfo.maxSubBSTHead == head.left && leftInfo.max < head.val)) &&
                (rightInfo == null ? true : (rightInfo.maxSubBSTHead == head.right && rightInfo.min > head.val))) {
            maxSubBSTHead = head;
            maxSubBSTSize = (leftInfo == null ? 0 : leftInfo.maxSubBSTSize) + (rightInfo == null ? 0 : rightInfo.maxSubBSTSize) + 1;
        }
        return new Info(maxSubBSTHead, maxSubBSTSize, max, min);
    }
}
class Info {
    Node maxSubBSTHead;
    int maxSubBSTSize;
    int max;
    int min;

    public Info(Node maxSubBSTHead, int maxSubBSTSize, int max, int min) {
        this.maxSubBSTHead = maxSubBSTHead;
        this.maxSubBSTSize = maxSubBSTSize;
        this.max = max;
        this.min = min;
    }
}

5.8 返回二叉树中两个节点a和b的最低公共祖先

import java.util.*;

class Solution {
    public static TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) {
            return null;
        }
        Info all = process(root, p, q);
        return all.ans;
    }
    private static Info process(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) {
            return new Info(false, false, null);
        }
        Info leftInfo = process(root.left, p, q);
        Info rightInfo = process(root.right, p, q);
        boolean findP = leftInfo.findP || rightInfo.findP || root.val == p.val;
        boolean findQ = leftInfo.findQ || rightInfo.findQ || root.val == q.val;
        TreeNode ans = null;
        if (leftInfo.ans != null) {
            ans = leftInfo.ans;
        } else if (rightInfo.ans != null) {
            ans = rightInfo.ans;
        } else if (findP && findQ) {
            ans = root;
        }
        return new Info(findP, findQ, ans);
    }

    //使用集合,效率低
    public static TreeNode lowestCommonAncestor2(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) {
            return null;
        }
        Map<TreeNode, TreeNode> parentMap = new HashMap<>();
        Set<TreeNode> set = new HashSet<>();
        parentMap.put(root, null);
        fillMap(parentMap, root);
        while (p != null) {
            set.add(p);
            p = parentMap.get(p);
        }
        while (q != null && !set.contains(q)) {
            q = parentMap.get(q);
        }
        return q;
    }
    private static void fillMap(Map<TreeNode, TreeNode> parentMap, TreeNode root) {
        if (root.left != null) {
            parentMap.put(root.left, root);
            fillMap(parentMap, root.left);
        }
        if (root.right != null) {
            parentMap.put(root.right, root);
            fillMap(parentMap, root.right);
        }
    }
}
class Info {
    boolean findP;
    boolean findQ;
    TreeNode ans;

    public Info(boolean findP, boolean findQ, TreeNode ans) {
        this.findP = findP;
        this.findQ = findQ;
        this.ans = ans;
    }
}

6. 贪心算法

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

import java.util.*;
public class Solution {
    public static String lowestString1(String[] strs) {
        if (strs == null || strs.length == 0) {
            return "";
        }
        List<String> list = new ArrayList<>();
        Set<Integer> set = new HashSet<>();
        dfs(strs, list, "", set);
        String lowest = list.get(0);
        for (int i = 1; i < list.size(); i++) {
            if (list.get(i).compareTo(lowest) < 0) {
                lowest = list.get(i);
            }
        }
        return lowest;
    }
    private static void dfs(String[] strs, List<String> list, String path, Set<Integer> set) {
        if (set.size() == strs.length) {
            list.add(path);
        } else {
            for (int i = 0; i < strs.length; i++) {
                if (!set.contains(i)) {
                    set.add(i);
                    dfs(strs, list, path + strs[i], set);
                    set.remove(i);
                }
            }
        }
    }

    public static String lowestString2(String[] strs) {
        if (strs == null || strs.length == 0) {
            return "";
        }
        Arrays.sort(strs, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return (o1 + o2).compareTo(o2 + o1);
            }
        });
        String res = "";
        for (String str : strs) {
            res += str;
        }
        return res;
    }
}

6.2 会议安排问题

import java.util.*;

public class Solution {
    //贪心
    public static int bestArrange1(Program[] programs) {
        if (programs == null || programs.length == 0) {
            return 0;
        }
        Arrays.sort(programs, new Comparator<Program>() {
            @Override
            public int compare(Program o1, Program o2) {
                return o1.end - o2.end;
            }
        });
        int timeLine = 0;
        int res = 0;
        for (Program program : programs) {
            if (timeLine <= program.start) {
                timeLine = program.end;
                res++;
            }
        }
        return res;
    }

    //回溯
    public static int bestArrange2(Program[] programs) {
        if (programs == null || programs.length == 0) {
            return 0;
        }
        return dfs(programs, 0, 0);
    }
    //done  已经安排的会议数量
    //timeLine目前来到的时间点
    private static int dfs(Program[] programs, int done, int timeLine) {
        if (programs.length == 0) {
            return done;
        }
        int max = done;
        for (int i = 0; i < programs.length; i++) {
            if (timeLine <= programs[i].start) {
                Program[] nexts = copyAndExcept(programs, i);
                max = Math.max(max, dfs(nexts, done + 1, programs[i].end));
            }
        }
        return max;
    }
    private static Program[] copyAndExcept(Program[] programs, int i) {
        Program[] res = new Program[programs.length - 1];
        int k = 0;
        for (int j = 0; j < programs.length; j++) {
            if (j != i) {
                res[k++] = programs[j];
            }
        }
        return res;
    }
}
class Program {
    int start;
    int end;

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

6.3 放灯,给定一个字符串str,只由X和.两种字符组成,X表示墙,"."表示居民点,可以放灯,如果i位置放灯,,可以让i1,i和i+1位置被点亮,问至少需要几盏灯

import java.util.*;

public class Solution {
    public static int minLight1(String str) {
        if (str == null || str.length() == 0) {
            return 0;
        }
        return process(str.toCharArray(), 0, new HashSet<>());
    }
    private static int process(char[] chars, int idx, Set<Integer> set) {
        if (idx == chars.length) {
            for (int i = 0; i < chars.length; i++) {
                if (chars[i] != 'X') {
                    if (!set.contains(i - 1) && !set.contains(i) && !set.contains(i + 1)) {
                        return Integer.MAX_VALUE;
                    }
                }
            }
            return set.size();
        } else {
            int no = process(chars, idx + 1, set);
            int yes = Integer.MAX_VALUE;
            if (chars[idx] != 'X') {
                set.add(idx);
                yes = process(chars, idx + 1, set);
                set.remove(idx);
            }
            return Math.min(yes, no);
        }
    }

    public static int minLight2(String str) {
        if (str == null || str.length() == 0) {
            return 0;
        }
        char[] chars = str.toCharArray();
        int lights = 0;
        int idx = 0;
        while (idx < chars.length) {
            if (chars[idx] == 'X') {
                idx++;
            } else {
                lights++;
                if (idx + 1 == str.length()) {
                    break;
                } else {
                    if (chars[idx + 1] == 'X') {
                        idx += 2;
                    } else {
                        idx += 3;
                    }
                }
            }
        }
        return lights;
    }

    // for test
    public static String randomString(int len) {
        char[] res = new char[(int) (Math.random() * len) + 1];
        for (int i = 0; i < res.length; i++) {
            res[i] = Math.random() < 0.5 ? 'X' : '.';
        }
        return String.valueOf(res);
    }
    public static void main(String[] args) {
        int len = 20;
        int testTime = 100000;
        for (int i = 0; i < testTime; i++) {
            String test = randomString(len);
            int ans1 = minLight1(test);
            int ans2 = minLight2(test);
            if (ans1 != ans2) {
                System.out.println("oops!");
            }
        }
        System.out.println("finish!");
    }
}

6.4 分隔金条,求最小代价

import java.util.PriorityQueue;

public class Solution {
    //堆+贪心
    public static int lessMoney1(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        PriorityQueue<Integer> heap = new PriorityQueue<>();
        for (int i = 0; i < arr.length; i++) {
            heap.add(arr[i]);
        }
        int sum = 0;
        int cur = 0;
        while (heap.size() > 1) {
            cur = heap.poll() + heap.poll();
            sum += cur;
            heap.add(cur);
        }
        return sum;
    }

    //回溯暴力
    public static int lessMoney2(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        return process(arr, 0);
    }
    private static int process(int[] arr, int pre) {
        if (arr.length == 1) {
            return pre;
        }
        int ans = Integer.MAX_VALUE;
        for (int i = 0; i < arr.length; i++) {
            for (int j = i + 1; j < arr.length; j++) {
                int[] copyAndMergeArr = copyAndMerge(arr, i, j);
                ans = Math.min(ans, process(copyAndMergeArr, pre + arr[i] + arr[j]));
            }
        }
        return ans;
    }
    private static int[] copyAndMerge(int[] arr, int i, int j) {
        int[] ans = new int[arr.length - 1];
        int k = 0;
        for (int l = 0; l < arr.length; l++) {
            if (l != i && l != j) {
                ans[k++] = arr[l];
            }
        }
        ans[k] = arr[i] + arr[j];
        return ans;
    }
}

6.5 花费数组costs,利润数组profits,最多能做的项目k,启动资金m,输出最后获得的最大钱数

import java.util.*;

public class Solution {
    //k: 一共可以做多少个项目,w: 初始资金
    public static int findMaxCapital(int k, int w, int[] profits, int[] costs) {
        PriorityQueue<Program> minCost = new PriorityQueue<>(new Comparator<Program>() {
            @Override
            public int compare(Program o1, Program o2) {
                return o1.c - o2.c;
            }
        });
        PriorityQueue<Program> maxProfit = new PriorityQueue<>(new Comparator<Program>() {
            @Override
            public int compare(Program o1, Program o2) {
                return o2.p - o1.p;
            }
        });
        for (int i = 0; i < profits.length; i++) {
            minCost.add(new Program(profits[i], costs[i]));
        }
        for (int i = 0; i < k; i++) {
            while (!minCost.isEmpty() && w >= minCost.poll().c) {
                maxProfit.add(minCost.poll());
            }
            if (maxProfit.isEmpty()) {
                return w;
            }
            w += maxProfit.poll().p;
        }
        return w;
    }
}
class Program{
    int p;
    int c;

    public Program(int p, int c) {
        this.p = p;
        this.c = c;
    }
}

7. 并查集

7.1 模板

class UnionFind<V> {
    Map<V, Node<V>> nodes;
    Map<Node<V>, Node<V>> parents;
    Map<Node<V>, Integer> sizeMap;

    public UnionFind(List<V> values) {
        for (V value : values) {
            Node<V> node = new Node<>(value);
            nodes.put(value, node);
            parents.put(node, node);
            sizeMap.put(node, 1);
        }
    }

    public Node<V> findFather(Node<V> cur) {
        Stack<Node<V>> path = new Stack<>();
        while (cur != parents.get(cur)) {
            path.push(cur);
            cur = parents.get(cur);
        }
        //cur头结点
        while (!path.isEmpty()) {
            parents.put(path.pop(), cur);
        }
        return cur;
    }

    public void union(V a, V b) {
        if (!nodes.containsKey(a) || !nodes.containsKey(b)) {
            return;
        }
        Node<V> aHead = findFather(nodes.get(a));
        Node<V> bHead = findFather(nodes.get(b));
        if (aHead != bHead) {
            int aSetSize = sizeMap.get(aHead);
            int bSetSize = sizeMap.get(bHead);
            if (aSetSize >= bSetSize) {
                parents.put(bHead, aHead);
                sizeMap.put(aHead, aSetSize + bSetSize);
                sizeMap.remove(bHead);
            } else {
                parents.put(aHead, bHead);
                sizeMap.put(bHead, aSetSize + bSetSize);
                sizeMap.remove(aHead);
            }
        }
    }

    public int getSetNum() {
        return sizeMap.size();
    }
}
class Node<V> {
    V val;

    public Node(V val) {
        this.val = val;
    }
}

7.2 如果user,如果a,b,c其中一个字段一样,就可以认为是一个人,请合并users,返回合并后的用户数量

public class Solution {
    public static int mergeUser(List<User> users) {
        UnionFind<User> unionFind = new UnionFind<>(users);
        Map<String, User> mapA = new HashMap<>();
        Map<String, User> mapB = new HashMap<>();
        Map<String, User> mapC = new HashMap<>();
        for (User user : users) {
            if (mapA.containsKey(user.a)) {
                unionFind.union(user, mapA.get(user.a));
            } else {
                mapA.put(user.a, user);
            }
            if (mapB.containsKey(user.b)) {
                unionFind.union(user, mapB.get(user.b));
            } else {
                mapB.put(user.b, user);
            }
            if (mapC.containsKey(user.c)) {
                unionFind.union(user, mapC.get(user.c));
            } else {
                mapC.put(user.c, user);
            }
        }
        return unionFind.getSetNum();
    }
}
class User {
    String a;
    String b;
    String c;

    public User(String a, String b, String c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }
}

8. 图

8.1 图结构模板

import java.util.*;

public class Solution{
    //[weight,from节点上面的值,to节点上面的值]    matrix所有的边    N*3的矩阵
    public static Graph createGraph(Integer[][] matrix) {
        Graph graph = new Graph();
        for (int i = 0; i < matrix.length; i++) {
            Integer weight = matrix[i][0];
            Integer from = matrix[i][1];
            Integer to = matrix[i][2];
            if (!graph.nodes.containsKey(from)) {
                graph.nodes.put(from, new Node(from));
            }
            if (!graph.nodes.containsKey(to)) {
                graph.nodes.put(to, new Node(to));
            }
            Node fromNode = graph.nodes.get(from);
            Node toNode = graph.nodes.get(to);
            Edge edge = new Edge(weight, fromNode, toNode);
            fromNode.nexts.add(toNode);
            fromNode.out++;
            toNode.in++;
            fromNode.edges.add(edge);
            graph.edges.add(edge);
        }
        return graph;
    }
}
class Node{//节点
    int val;
    int in;//入度
    int out;//出度
    List<Node> nexts;//直接指向的节点
    List<Edge> edges;//直接连接的边

    public Node(int val) {
        this.val = val;
        nexts = new ArrayList<>();
        edges = new ArrayList<>();
    }
}
class Edge{//边
    int weight;
    Node from;
    Node to;

    public Edge(int weight, Node from, Node to) {
        this.weight = weight;
        this.from = from;
        this.to = to;
    }
}
class Graph{//图
    Map<Integer, Node> nodes;
    Set<Edge> edges;

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

8.2 图的DFS

    public static void dfs(Node node) {
        if (node == null) {
            return;
        }
        Stack<Node> stack = new Stack<>();
        HashSet<Node> set = new HashSet<>();
        stack.add(node);
        set.add(node);
        System.out.println(node.val);
        while (!stack.isEmpty()) {
            Node cur = stack.pop();
            for (Node next : cur.nexts) {
                if (!set.contains(next)) {
                    stack.push(cur);
                    stack.push(next);
                    set.add(next);
                    System.out.println(next.val);
                    break;
                }
            }
        }
    }

8.3 图的BFS

    public static void bfs(Node node) {
        if (node == null) {
            return;
        }
        Queue<Node> queue = new LinkedList<>();
        Set<Node> set = new HashSet<>();
        queue.add(node);
        set.add(node);
        while (!queue.isEmpty()) {
            Node cur = queue.poll();
            System.out.println(cur.val);
            for (Node next : cur.nexts) {
                if (!set.contains(next)) {
                    set.add(next);
                    queue.add(next);
                }
            }
        }
    }

8.4 拓扑排序

    public static List<Node> topologySort(Graph graph) {
        //key:某一个node,value:剩余的入度
        Map<Node, Integer> inMap = new HashMap<>();
        //入度为0的节点
        Queue<Node> zeroNode = new LinkedList<>();
        for (Node node : graph.nodes.values()) {
            inMap.put(node, node.in);
            if (node.in == 0) {
                zeroNode.add(node);
            }
        }
        List<Node> res = new LinkedList<>();
        while (!zeroNode.isEmpty()) {
            Node cur = zeroNode.poll();
            res.add(cur);
            for (Node next : cur.nexts) {
                inMap.put(next, inMap.get(next) - 1);
                if (inMap.get(next) == 0) {
                    zeroNode.add(next);
                }
            }
        }
        return res;
    }

8.5 图的最小生成树 克鲁斯卡算法(kruskal)

public class Solution {
    //克鲁斯卡 最小生成树
    public static Set<Edge> kruskalMst(Graph graph) {
        UnionFind unionFind = new UnionFind();
        unionFind.makeSets(graph.nodes.values());
        PriorityQueue<Edge> heap = new PriorityQueue<>(new Comparator<Edge>() {
            @Override
            public int compare(Edge o1, Edge o2) {
                return o1.weight - o2.weight;
            }
        });
        for (Edge edge : graph.edges) {
            heap.add(edge);
        }
        Set<Edge> res = new HashSet<>();
        while (!heap.isEmpty()) {
            Edge edge = heap.poll();
            if (!unionFind.isSameSet(edge.from, edge.to)) {
                res.add(edge);
                unionFind.union(edge.from, edge.to);
            }
        }
        return res;
    }
}
//并查集
class UnionFind{
    Map<Node, Node> parentMap;
    Map<Node, Integer> sizeMap;
    public UnionFind() {
        parentMap = new HashMap<>();
        sizeMap = new HashMap<>();
    }
    public void makeSets(Collection<Node> nodes) {
        parentMap.clear();
        sizeMap.clear();
        for (Node node : nodes) {
            parentMap.put(node, node);
            sizeMap.put(node, 1);
        }
    }
    private Node findParent(Node node) {
        Stack<Node> stack = new Stack<>();
        while (node != parentMap.get(node)) {
            stack.add(node);
            node = parentMap.get(node);
        }
        while (!stack.isEmpty()) {
            parentMap.put(stack.pop(), node);
        }
        return node;
    }
    public boolean isSameSet(Node a, Node b) {
        return findParent(a) == findParent(b);
    }
    public void union(Node a, Node b) {
        if (a == null || b == null) {
            return;
        }
        Node headA = findParent(a);
        Node headB = findParent(b);
        if (headA != headB) {
            int sizeA = sizeMap.get(headA);
            int sizeB = sizeMap.get(headB);
            if (sizeA <= sizeB) {
                parentMap.put(headA, headB);
                sizeMap.put(headB, sizeA + sizeB);
                sizeMap.remove(sizeA);
            } else {
                parentMap.put(headB, headA);
                sizeMap.put(headA, sizeA + sizeB);
                sizeMap.remove(sizeB);
            }
        }
    }
}

8.6 图的最小生成树 普利姆算法(Prim)

    public static Set<Edge> primMST(Graph graph) {
        PriorityQueue<Edge> heap = new PriorityQueue<>(new Comparator<Edge>() {
            @Override
            public int compare(Edge o1, Edge o2) {
                return o1.weight - o2.weight;
            }
        });
        Set<Node> set = new HashSet<>();
        Set<Edge> res = new HashSet<>();
        for (Node node : graph.nodes.values()) {//防森林
            if (!set.contains(node)) {
                set.add(node);
                for (Edge edge : node.edges) {
                    heap.add(edge);
                }
                while (!heap.isEmpty()) {
                    Edge edge = heap.poll();
                    Node toNode = edge.to;
                    if (!set.contains(toNode)) {
                        set.add(toNode);
                        res.add(edge);
                        for (Edge nextEdge : toNode.edges) {
                            heap.add(nextEdge);
                        }
                    }
                }
            }
        }
        return res;
    }

8.7 图的最短距离,迪杰斯特拉算法(dijkstra)

import java.util.*;

// no negative weight
public class Main {
    public static Map<Node, Integer> dijkstra1(Node head) {
        // 从head出发到所有点的最小距离
        // key : 从head出发到达key
        // value : 从head出发到达key的最小距离
        // 如果在表中,没有T的记录,含义是从head出发到T这个点的距离为正无穷
        Map<Node, Integer> distanceMap = new HashMap<>();
        distanceMap.put(head, 0);
        // 已经求过距离的节点,存在selectedNodes中,以后再也不碰
        Set<Node> selectedNodes = new HashSet<>();
        Node minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes);
        while (minNode != null) {
            int distance = distanceMap.get(minNode);
            for (Edge edge : minNode.edges) {
                Node toNode = edge.to;
                if (!distanceMap.containsKey(toNode)) {
                    distanceMap.put(toNode, distance + edge.weight);
                } else {
                    distanceMap.put(edge.to, Math.min(distanceMap.get(toNode), distance + edge.weight));
                }
            }
            selectedNodes.add(minNode);
            minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes);
        }
        return distanceMap;
    }
    public static Node getMinDistanceAndUnselectedNode(Map<Node, Integer> distanceMap, Set<Node> touchedNodes) {
        Node minNode = null;
        int minDistance = Integer.MAX_VALUE;
        for (Entry<Node, Integer> entry : distanceMap.entrySet()) {
            Node node = entry.getKey();
            int distance = entry.getValue();
            if (!touchedNodes.contains(node) && distance < minDistance) {
                minNode = node;
                minDistance = distance;
            }
        }
        return minNode;
    }

    public static class NodeRecord {
        public Node node;
        public int distance;

        public NodeRecord(Node node, int distance) {
            this.node = node;
            this.distance = distance;
        }
    }

    public static class NodeHeap {
        private Node[] nodes; // 实际的堆结构
        // key 某一个node, value 上面数组中的位置
        private HashMap<Node, Integer> heapIndexMap;
        // key 某一个节点, value 从源节点出发到该节点的目前最小距离
        private HashMap<Node, Integer> distanceMap;
        private int size; // 堆上有多少个点

        public NodeHeap(int size) {
            nodes = new Node[size];
            heapIndexMap = new HashMap<>();
            distanceMap = new HashMap<>();
            size = 0;
        }

        public boolean isEmpty() {
            return size == 0;
        }

        // 有一个点叫node,现在发现了一个从源节点出发到达node的距离为distance
        // 判断要不要更新,如果需要的话,就更新
        public void addOrUpdateOrIgnore(Node node, int distance) {
            if (inHeap(node)) {
                distanceMap.put(node, Math.min(distanceMap.get(node), distance));
                insertHeapify(node, heapIndexMap.get(node));
            }
            if (!isEntered(node)) {
                nodes[size] = node;
                heapIndexMap.put(node, size);
                distanceMap.put(node, distance);
                insertHeapify(node, size++);
            }
        }

        public NodeRecord pop() {
            NodeRecord nodeRecord = new NodeRecord(nodes[0], distanceMap.get(nodes[0]));
            swap(0, size - 1);
            heapIndexMap.put(nodes[size - 1], -1);
            distanceMap.remove(nodes[size - 1]);
            // free C++同学还要把原本堆顶节点析构,对java同学不必
            nodes[size - 1] = null;
            heapify(0, --size);
            return nodeRecord;
        }

        private void insertHeapify(Node node, int index) {
            while (distanceMap.get(nodes[index]) < distanceMap.get(nodes[(index - 1) / 2])) {
                swap(index, (index - 1) / 2);
                index = (index - 1) / 2;
            }
        }

        private void heapify(int index, int size) {
            int left = index * 2 + 1;
            while (left < size) {
                int smallest = left + 1 < size && distanceMap.get(nodes[left + 1]) < distanceMap.get(nodes[left])
                        ? left + 1
                        : left;
                smallest = distanceMap.get(nodes[smallest]) < distanceMap.get(nodes[index]) ? smallest : index;
                if (smallest == index) {
                    break;
                }
                swap(smallest, index);
                index = smallest;
                left = index * 2 + 1;
            }
        }

        private boolean isEntered(Node node) {
            return heapIndexMap.containsKey(node);
        }

        private boolean inHeap(Node node) {
            return isEntered(node) && heapIndexMap.get(node) != -1;
        }

        private void swap(int index1, int index2) {
            heapIndexMap.put(nodes[index1], index2);
            heapIndexMap.put(nodes[index2], index1);
            Node tmp = nodes[index1];
            nodes[index1] = nodes[index2];
            nodes[index2] = tmp;
        }
    }

    // 改进后的dijkstra算法
    // 从head出发,所有head能到达的节点,生成到达每个节点的最小路径记录并返回
    public static Map<Node, Integer> dijkstra2(Node head, int size) {
        NodeHeap nodeHeap = new NodeHeap(size);
        nodeHeap.addOrUpdateOrIgnore(head, 0);
        Map<Node, Integer> result = new HashMap<>();
        while (!nodeHeap.isEmpty()) {
            NodeRecord record = nodeHeap.pop();
            Node cur = record.node;
            int distance = record.distance;
            for (Edge edge : cur.edges) {
                nodeHeap.addOrUpdateOrIgnore(edge.to, edge.weight + distance);
            }
            result.put(cur, distance);
        }
        return result;
    }

}

9. 暴力递归

9.1 汉罗塔问题

    public static void func(int n) {
        if (n > 0) {
            func(n, "from", "to", "other");
        }
    }
    public static void func(int n, String from, String to, String other) {
        if (n == 1) {
            System.out.println("move 1 from " + from + " to " + to);
        } else {
            func(n - 1, from, other, to);
            System.out.println("move " + n + " from " + from + " to " + to);
            func(n - 1, other, to, from);
        }
    }

9.2 逆序一个栈,不能申请额外的数据结构

    //逆序一个栈
    public static void reverse(Stack<Integer> stack) {
        if (stack.isEmpty()) {
            return;
        }
        int i = f(stack);
        reverse(stack);
        stack.push(i);
    }
    //取出栈底的元素
    public static int f(Stack<Integer> stack) {
        int res = stack.pop();
        if (stack.isEmpty()) {
            return res;
        } else {
            int last = f(stack);
            stack.add(res);
            return last;
        }
    }

从左往右的尝试模型

9.3 打印一个字符串的全部子序列

import java.util.*;
public class Main {
    //打印一个字符串的全部子序列
    public static List<String> subs(String s) {
        char[] chars = s.toCharArray();
        String path = "";
        List<String> ans = new ArrayList<>();
        process1(chars, 0, ans, "");
        return ans;
    }
    private static void process1(char[] chars, int idx, List<String> ans, String path) {
        if (idx == chars.length) {
            ans.add(path);
            return;
        }
        String no = path;
        process1(chars, idx + 1, ans, no);
        String yes = path + String.valueOf(chars[idx]);
        process1(chars, idx + 1, ans, yes);
    }

    //打印一个字符串的全部子序列且没有重复字面值
    public static List<String> subsNoRepeat(String s) {
        char[] chars = s.toCharArray();
        String path = "";
        Set<String> set = new HashSet<>();
        process2(chars, 0, set, "");
        List<String> ans = new ArrayList<>();
        for (String temp : set) {
            ans.add(temp);
        }
        return ans;
    }
    private static void process2(char[] chars, int idx, Set<String> set, String path) {
        if (idx == chars.length) {
            set.add(path);
            return;
        }
        String no = path;
        process2(chars, idx + 1, set, no);
        String yes = path + String.valueOf(chars[idx]);
        process2(chars, idx + 1, set, yes);
    }
}

9.4 打印一个字符串的全部排列

import java.util.*;
public class Main {
    //打印一个字符串的全部排列
    public static List<String> permutation(String s) {
        List<String> ans = new ArrayList<>();
        if (s == null || s.length() == 0) {
            return ans;
        }
        char[] chars = s.toCharArray();
        process1(chars, 0, ans);
        return ans;
    }
    private static void process1(char[] chars, int idx, List<String> ans) {
        if (idx == chars.length) {
            ans.add(String.valueOf(chars));
            return;
        }
        for (int i = idx; i < chars.length; i++) {
            swap(chars, idx, i);
            process1(chars, idx + 1, ans);
            swap(chars, idx, i);
        }
    }

    //打印一个字符串的全部排列,且字面值无重复
    public static List<String> permutationNoRepeat(String s) {
        List<String> ans = new ArrayList<>();
        if (s == null || s.length() == 0) {
            return ans;
        }
        char[] chars = s.toCharArray();
        process2(chars, 0, ans);
        return ans;
    }
    private static void process2(char[] chars, int idx, List<String> ans) {
        if (idx == chars.length) {
            ans.add(String.valueOf(chars));
            return;
        }
        boolean[] visited = new boolean[26];
        for (int i = idx; i < chars.length; i++) {
            if (!visited[chars[i] - 'a']) {
                visited[chars[i] - 'a'] = true;
                swap(chars, i, idx);
                process2(chars, idx + 1, ans);
                swap(chars, i, idx);
            }
        }
    }

    private static void swap(char[] chars, int i, int j) {
        char temp = chars[i];
        chars[i] = chars[j];
        chars[j] = temp;
    }
}

9.5 编码转化

public class Main {
    //递归
    public static int number1(String s) {
        if (s == null || s.length() == 0) {
            return 0;
        }
        return process(s.toCharArray(), 0);
    }

    private static int process(char[] chars, int idx) {
        if (idx == chars.length) {
            return 1;
        }
        if (chars[idx] == '0') {
            return 0;
        }
        if (chars[idx] == '1') {
            int res = process(chars, idx + 1);
            if (idx + 1 < chars.length) {
                res += process(chars, idx + 2);
            }
            return res;
        }
        if (chars[idx] == '2') {
            int res = process(chars, idx + 1);
            if (idx + 1 < chars.length && chars[idx + 1] >= '0' && chars[idx] <= '6') {
                res += process(chars, idx + 2);
            }
            return res;
        }
        return process(chars, idx + 1);
    }

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

    public static void main(String[] args) {
        System.out.println(number1("111"));
        System.out.println(number2("111"));
    }
}

9.6 背包问题: 给定两个长度为n的数组weight和values,给定一个载重为正数的袋子bag,最多能装的价值为多少

public class Main {
    //方式一 暴力回溯
    public static int getMaxValue1(int[] w, int[] v, int bag) {
        return process1(w, v, 0, 0, bag);
    }
    private static int process1(int[] w, int[] v, int idx, int alreadyW, int bag) {
        if (alreadyW > bag) {
            return -1;
        }
        if (idx == w.length) {
            return 0;
        }
        int p1 = process1(w, v, idx + 1, alreadyW, bag);
        int p2Next = process1(w, v, idx + 1, alreadyW + w[idx], bag);
        int p2 = -1;
        if (p2Next != -1) {
            p2 = p2Next + v[idx];
        }
        return Math.max(p1, p2);
    }

    //方式二 暴力回溯
    public static int getMaxValue2(int[] w, int[] v, int bag) {
        return process2(w, v, 0, bag);
    }
    private static int process2(int[] w, int[] v, int idx, int rest) {
        if (rest < 0) {
            return -1;
        }
        if (idx == w.length) {
            return 0;
        }
        int p1 = process2(w, v, idx + 1, rest);
        int p2Next = process2(w, v, idx + 1, rest - w[idx]);
        int p2 = -1;
        if (p2Next != -1) {
            p2 = p2Next + v[idx];
        }
        return Math.max(p1, p2);
    }

    //方式三 动态规划(方式二改)
    public static int getMaxValue3(int[] w, int[] v, int bag) {
        int n = w.length;
        int[][] dp = new int[n + 1][bag + 1];
        for (int idx = n - 1; idx >= 0; idx--) {
            for (int res = 1; res <= bag; res++) {
                dp[idx][res] = dp[idx + 1][res];
                if (res >= w[idx]) {
                    dp[idx][res] = Math.max(dp[idx][res], v[idx] + dp[idx + 1][res - w[idx]]);
                }
            }
        }
        return dp[0][bag];
    }

    public static void main(String[] args) {
        int[] weights = {3, 2, 4, 7};
        int[] values = {5, 6, 3, 19};
        int bag = 11;
        System.out.println(getMaxValue1(weights, values, bag));
        System.out.println(getMaxValue2(weights, values, bag));
        System.out.println(getMaxValue3(weights, values, bag));
    }
}

范围上尝试模型

9.7 给定一个数组arr,玩家A和玩家B每次只能拿最左或者最右的纸牌,玩家A和B都绝顶聪明,返回最后获胜者的分数

public class Main {
    public static int win1(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        return Math.max(f(arr, 0, arr.length - 1), s(arr, 0, arr.length - 1));
    }

    public static int f(int[] arr, int i, int j) {
        if (i == j) {
            return arr[i];
        }
        return Math.max(arr[i] + s(arr, i + 1, j), arr[j] + s(arr, i, j - 1));
    }

    public static int s(int[] arr, int i, int j) {
        if (i == j) {
            return 0;
        }
        return Math.min(f(arr, i + 1, j), f(arr, i, j - 1));
    }

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

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

9.8 n皇后求最多数量

public class Solution {
    public static int totalNQueens(int n) {
        if (n == 1) {
            return 1;
        }
        int[] record = new int[n];
        return process(0, n, record);
    }
    //i表示当前在多少行
    //record记录的是多少行多少列记录的数据
    private static int process(int i, int n, int[] record) {
        if (i == n) {
            return 1;
        }
        int res = 0;
        for (int j = 0; j < n; j++) {
            if (isValid(i, j, record)) {
                record[i] = j;
                res += process(i + 1, n, record);
            }
        }
        return res;
    }

    private static boolean isValid(int i, int j, int[] record) {
        for (int k = 0; k < i; k++) {
            if (record[k] == j || Math.abs(i - k) == Math.abs(record[k] - j)) {
                return false;
            }
        }
        return true;
    }


    public static int totalNQueues2(int n) {
        if (n == 1) {
            return 1;
        }
        int limit = n == 32 ? -1 : (1 << n) - 1;
        return process2(limit, 0, 0, 0);
    }
    private static int process2(int limit, int colLimit, int leftLimit, int rightLimit) {
        if (colLimit == limit) {
            return 1;
        }
        int pos = limit & (~(colLimit | leftLimit | rightLimit));
        int rightOne = 0;
        int res = 0;
        while (pos != 0) {
            rightOne = pos & (~pos + 1);
            pos = pos - rightOne;
            res += process2(limit,
                    colLimit | rightOne,
                    (leftLimit | rightOne) << 1,
                    (rightLimit | rightOne) >>> 1);
        }
        return res;
    }
}

9.9 机器人走法

假设排成一行的N个位置,记为1~N,N一定大于等于2

开始时机器人在其中的M位置上(M一定是1~N中的一个)

如果机器人来到1位置,那么下一步只能向右走到2位置

如果机器人来到N位置,那么下一步只能向左来到N-1位置

如果机器人来到中间位置,那么下一步既可以向左走,也可以向右走

规定机器人必须走K步,最终能来到P位置(P也是1~N中的一个)的方法有多少种

给定四个参数N,M,K,P,返回方法数

public class Main {
    //n: 位置为1~n,固定参数
    //cur: 当前在cur位置,可变参数
    //res: 剩余还有多少步没有走
    //p: 最终要到达的位置
    //方式1: 暴力回溯
    public static int ways1(int n, int cur, int res, int p) {
        if (n < 2 || res < 1 || cur > n || p < 1 || p > n) {
            return 0;
        }
        return walk1(n, cur, res, p);
    }
    private static int walk1(int n, int cur, int res, int p) {
        if (res == 0) {
            return cur == p ? 1 : 0;
        }
        if (cur == 1) {
            return walk1(n, cur + 1, res - 1, p);
        }
        if (cur == n) {
            return walk1(n, cur - 1, res - 1, p);
        }
        return walk1(n, cur + 1, res - 1, p) + walk1(n, cur - 1, res - 1, p);
    }


    //方式二: 暴力回溯+缓存 记忆化搜索下的动态规划
    public static int ways2(int n, int cur, int res, int p) {
        if (n < 2 || res < 1 || cur > n || p < 1 || p > n) {
            return 0;
        }
        int[][] dp = new int[res + 1][n + 1];
        for (int i = 0; i <= res; i++) {
            for (int j = 0; j <= n; j++) {
                dp[i][j] = -1;
            }
        }
        return walk2(n, cur, res, p, dp);
    }
    private static int walk2(int n, int cur, int res, int p, int[][] dp) {
        if (dp[res][cur] != -1) {
            return dp[res][cur];
        }
        if (res == 0) {
            dp[res][cur] = cur == p ? 1 : 0;
            return dp[res][cur];
        }
        if (cur == 1) {
            dp[res][cur] = walk2(n, cur + 1, res - 1, p, dp);
            return dp[res][cur];
        }
        if (cur == n) {
            dp[res][cur] = walk2(n, cur - 1, res - 1, p, dp);
            return dp[res][cur];
        }
        dp[res][cur] = walk2(n, cur + 1, res - 1, p, dp) + walk2(n, cur - 1, res - 1, p, dp);
        return dp[res][cur];
    }


    //改动态规划
    public static int ways3(int n, int cur, int res, int p) {
        if (n < 2 || res < 1 || cur > n || p < 1 || p > n) {
            return 0;
        }
        int[][] dp = new int[res + 1][n + 1];
        dp[0][p] = 1;
        for (int i = 1; i <= res; i++) {
            for (int j = 1; j <= n; j++) {
                if (j == 1) {
                    dp[i][j] = dp[i - 1][j + 1];
                } else if (j == n) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    dp[i][j] = dp[i - 1][j + 1] + dp[i - 1][j - 1];
                }
            }
        }
        return dp[res][cur];
    }

    public static void main(String[] args) {
        System.out.println(ways1(7, 4, 9, 5));
        System.out.println(ways2(7, 4, 9, 5));
        System.out.println(ways3(7, 4, 9, 5));
    }
}

9.10 硬币合并问题,一共有多少种方式可以凑齐一个数

public class Main {
    public static int ways1(int[] arr, int target) {
        if (arr == null || arr.length == 0 || target < 0) {
            return 0;
        }
        return process1(arr, 0, target);
    }
    private static int process1(int[] arr, int idx, int res) {
        if (idx == arr.length) {
            return res == 0 ? 1 : 0;
        }
        int ways = 0;
        for (int zhang = 0; zhang * arr[idx] <= res; zhang++) {
            ways += process1(arr, idx + 1, res - zhang * arr[idx]);
        }
        return ways;
    }

    public static int ways2(int[] arr, int target) {
        if (arr == null || arr.length == 0 || target < 0) {
            return 0;
        }
        int[][] dp = new int[arr.length + 1][target + 1];
        for (int i = 0; i < dp.length; i++) {
            for (int j = 0; j < dp[0].length; j++) {
                dp[i][j] = -1;
            }
        }
        return process2(arr, 0, target, dp);
    }
    private static int process2(int[] arr, int idx, int res, int[][] dp) {
        if (dp[idx][res] != -1) {
            return dp[idx][res];
        }
        if (idx == arr.length) {
            dp[idx][res] = res == 0 ? 1 : 0;
            return dp[idx][res];
        }
        int ways = 0;
        for (int zhang = 0; zhang * arr[idx] <= res; zhang++) {
            ways += process2(arr, idx + 1, res - zhang * arr[idx], dp);
        }
        dp[idx][res] = ways;
        return ways;
    }

    public static int waysdp1(int[] arr, int target) {
        if (arr == null || arr.length == 0 || target < 0) {
            return 0;
        }
        int n = arr.length;
        int[][] dp = new int[n + 1][target + 1];
        dp[n][0] = 1;
        for (int idx = n - 1; idx >= 0; idx--) {
            for (int res = 0; res <= target; res++) {
                int ways = 0;
                for (int zhang = 0; zhang * arr[idx] <= res; zhang++) {
                    ways += dp[idx + 1][res - zhang * arr[idx]];
                }
                dp[idx][res] = ways;
            }
        }
        return dp[0][target];
    }

    public static int waysdp2(int[] arr, int target) {
        if (arr == null || arr.length == 0 || target < 0) {
            return 0;
        }
        int n = arr.length;
        int[][] dp = new int[n + 1][target + 1];
        dp[n][0] = 1;
        for (int i = n - 1; i >= 0; i--) {
            for (int res = 0; res <= target; res++) {
                dp[i][res] = dp[i + 1][res];
                if (res - arr[i] >= 0) {
                    dp[i][res] += dp[i][res - arr[i]];
                }
            }
        }
        return dp[0][target];
    }

    public static void main(String[] args) {
        int[] arr = {5, 10, 50, 100};
        int sum = 1000;
        System.out.println(ways1(arr, sum));
        System.out.println(ways2(arr, sum));
        System.out.println(waysdp1(arr, sum));
        System.out.println(waysdp2(arr, sum));
    }
}

9.11 剪贴纸

给定一个字符串str,给定一个字符串类型的数组arr.

arr里的每一个字符串,代表一张贴纸,你可以把打个字符剪开使用,目的是拼出str来

返回至少需要多少张贴纸可以完成这个任务,每一张贴纸可以重复使用

例子: str="babac", arr={"ba","c","abcd"}

至少需要两张贴纸"ba"和"abcd",因为使用这两张贴纸,把每一个字符单独剪开,含有2个a,2个b,1个c.是可以拼出str的.所以返回2.

import java.util.HashMap;
import java.util.Map;

public class Main{
    public static int minStickers1(String[] stickers, String target) {
        int n = stickers.length;
        int[][] arr = new int[n][26];
        Map<String, Integer> map = new HashMap<>();
        for (int i = 0; i < n; i++) {
            char[] chars = stickers[i].toCharArray();
            for (char c : chars) {
                arr[i][c - 'a']++;
            }
        }
        map.put("", 0);
        return process1(arr, map, target);
    }

    private static int process1(int[][] arr, Map<String, Integer> map, String s) {
        if (map.containsKey(s)) {
            return map.get(s);
        }
        int ans = Integer.MAX_VALUE;
        int n = arr.length;
        int[] tmap = new int[26];
        char[] chars = s.toCharArray();
        for (char c : chars) {
            tmap[c - 'a']++;
        }
        for (int i = 0; i < n; i++) {
            if (arr[i][chars[0] - 'a'] == 0) {
                continue;
            }
            StringBuilder builder = new StringBuilder();
            for (int j = 0; j < 26; j++) {
                if (tmap[j] > 0) {
                    for (int k = 0; k < Math.max(0, tmap[j] - arr[i][j]); k++) {
                        builder.append((char) ('a' + j));
                    }
                }
            }
            String s1 = builder.toString();
            int tmp = process1(arr, map, s1);
            if (tmp != -1) {
                ans = Math.min(ans, 1 + tmp);
            }
        }
        map.put(s, ans == Integer.MAX_VALUE ? 0 : ans);
        return map.get(s);
    }

    public static void main(String[] args) {
        String str="babac";
        String[] arr = {"ba", "c", "abcd"};
        System.out.println(minStickers1(arr, str));
    }
}

多样本位置全对应的尝试模型

9.12 两个字符串的最长公共子序列问题

public class Main{
    public static int longestCommonSubsequence(String text1, String text2) {
        char[] str1 = text1.toCharArray();
        char[] str2 = text2.toCharArray();
        int[][] dp = new int[str1.length][str2.length];
        dp[0][0] = str1[0] == str2[0] ? 1 : 0;
        for (int i = 1; i < str1.length; i++) {
            dp[i][0] = Math.max(dp[i - 1][0], str1[i] == str2[0] ? 1 : 0);
        }
        for (int i = 1; i < str2.length; i++) {
            dp[0][i] = Math.max(dp[0][i - 1], str1[0] == str2[i] ? 1 : 0);
        }
        for (int i = 1; i < str1.length; i++) {
            for (int j = 1; j < str2.length; j++) {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                if (str1[i] == str2[j]) {
                    dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - 1] + 1);
                }
            }
        }
        return dp[str1.length - 1][str2.length - 1];
    }

    public static void main(String[] args) {
        String s1 = "wks";
        String s2 = "awkysw";
        System.out.println(longestCommonSubsequence(s1, s2));
    }
}

寻找业务限制的尝试模型

9.13 洗咖啡杯

给定一个数组,代表每个人喝完咖啡准备洗杯子的时间

只有一台咖啡机,一次只能洗一个杯子,时间消耗a,洗完才能洗下一个杯子

每个咖啡杯也可以自己挥发干净,时间消耗b,咖啡杯可以并行挥发

返回让所有咖啡杯变干净的最早完成时间

三个参数: int[] arr,int a,int b

public class Main {
    public static int process1(int[] drinks, int a, int b, int idx, int washLine) {
        if (idx == drinks.length - 1) {
            return Math.min(Math.max(drinks[idx], washLine) + a, drinks[idx] + b);
        }
        int wash = Math.max(drinks[idx], washLine) + a;
        int next1 = process1(drinks, a, b, idx + 1, wash);
        int p1 = Math.max(wash, next1);
        int dir = drinks[idx] + b;
        int next2 = process1(drinks, a, b, idx + 1, washLine);
        int p2 = Math.max(dir, next2);
        return Math.min(p1, p2);
    }

    public static int dp(int[] drinks, int a, int b) {
        if (a >= b) {
            return drinks[drinks.length - 1] + b;
        }
        int n = drinks.length;
        int limit = 0;
        for (int i = 0; i < n; i++) {
            limit = Math.max(limit, drinks[i] + a);
        }
        int[][] dp = new int[n][limit + 1];
        for (int washLine = 0; washLine <= limit; washLine++) {
            dp[n - 1][washLine] = Math.min(Math.max(drinks[n - 1], washLine) + a, drinks[n - 1] + b);
        }
        for (int idx = n - 2; idx >= 0; idx--) {
            for (int washLine = 0; washLine <= limit; washLine++) {
                int wash = Math.max(drinks[idx], washLine) + a;
                int p1 = Integer.MAX_VALUE;
                if (wash <= limit) {
                    p1 = Math.max(wash, dp[idx + 1][wash]);
                }
                int p2 = Math.max(drinks[idx] + b, dp[idx + 1][washLine]);
                dp[idx][washLine] = Math.min(p1, p2);
            }
        }
        return dp[0][0];
    }

    public static void main(String[] args) {
        int[] arr = {1, 1, 5, 5, 7, 10, 12, 12, 12, 12, 12, 12, 15};
        int a = 3;
        int b = 10;
        System.out.println(process1(arr, a, b, 0, 0));
        System.out.println(dp(arr, a, b));
    }
}

猜你喜欢

转载自blog.csdn.net/qq_43204550/article/details/108302273
今日推荐