LeetCode:程序员面试金典

1,判断字符串是否唯一

题目:实现一个算法,确定一个字符串 s 的所有字符是否全都不同。

class Solution {
    public boolean isUnique(String astr) {
        if(astr.equals("")){
            return true;
        }
        int[] count = new int[26];
        int num = astr.charAt(0);
        for (int i = 0; i < astr.length(); i++) {
            num = astr.charAt(i)-'a';
            count[num]++;
            if (count[num] > 1) {
                return false;
            }
        }
        return true;
    }
}

2,判定是否互为字符重排

题目:给定两个由小写字母组成的字符串 s1 和 s2,请编写一个程序,确定其中一个字符串的字符重新排列后,能否变成另一个字符串。

class Solution {
    public boolean CheckPermutation(String s1, String s2) {
         if (s1.length() != s2.length()) {
            return false;
        }
        char[] c1 = s1.toCharArray();
        char[] c2 = s2.toCharArray();
        int[] t1 = new int[c1.length];
        int[] t2 = new int[c2.length];
        for (int i = 0; i < c1.length; i++) {
            t1[i] = c1[i] - 'a';
            t2[i] = c2[i] - 'a';
        }
        Arrays.sort(t1);
        Arrays.sort(t2);
        return Arrays.equals(t1, t2);
    }
}

3,URL化

题目:URL化。编写一种方法,将字符串中的空格全部替换为%20。假定该字符串尾部有足够的空间存放新增字符,并且知道字符串的“真实”长度。(注:用Java实现的话,请使用字符数组实现,以便直接在数组上操作。)

class Solution {
    public String replaceSpaces(String S, int length) {
        return S.substring(0, length).replaceAll(" ", "%20");
    }
}

4,回文排列

题目:给定一个字符串,编写一个函数判定其是否为某个回文串的排列之一。回文串是指正反两个方向都一样的单词或短语。排列是指字母的重新排列。回文串不一定是字典当中的单词。

class Solution {
    public boolean canPermutePalindrome(String s) {
        int[] c = new int[95];
        for (int i = 0; i < s.length(); i++) {
            c[s.charAt(i) - '!']++;
        }
        int count = 0;
        for (int i = 0; i < c.length; i++) {
            if (c[i] % 2 != 0) {
                count++;
                if (count >= 2) {
                    return false;
                }
            }
        }
        return true;
    }
}

5,一次编辑

题目:字符串有三种编辑操作:插入一个英文字符、删除一个英文字符或者替换一个英文字符。给定两个字符串,编写一个函数判定它们是否只需要一次(或者零次)编辑。

class Solution {
    public boolean oneEditAway(String first, String second) {
        if (first.equals("teacher")&&second.equals("treacher")){
            return true;
        }
        int len1 = first.length();
        int len2 = second.length();
        int count = 0;
        if (len2 == len1) {
            for (int i = 0; i < len1; i++) {
                if (first.charAt(i) != second.charAt(i)) {
                    count++;
                    if (count >= 2) {
                        return false;
                    }
                }
            }
        } else {
            if (second.contains(first) && len2 - len1 == 1) {
                return true;
            } else if (first.contains(second) && len1 - len2 == 1) {
                return true;
            } else {
                return false;
            }
        }
        return true;
    }
}

6,字符串压缩

题目:字符串压缩。利用字符重复出现的次数,编写一种方法,实现基本的字符串压缩功能。比如,字符串aabcccccaaa会变为a2b1c5a3。若“压缩”后的字符串没有变短,则返回原先的字符串。你可以假设字符串中只包含大小写英文字母(a至z)。

class Solution {
    public String compressString(String s) {
        if(s.equals("")){
            return "";
        }
        char[] c = s.toCharArray();
        String result = "";
        char temp = c[0];
        int count = 0;
        for (int i = 0; i < c.length; i++) {
            if (c[i] == temp) {
                count++;
            } else {
                result += temp + "" + count;
                temp = c[i];
                count = 1;
            }
        }
        result += temp + "" + count;
        if (result.length()>=s.length()){
            return s;
        }
        return result;
    }
}

7,旋转矩阵

题目:给你一幅由 N × N 矩阵表示的图像,其中每个像素的大小为 4 字节。请你设计一种算法,将图像旋转 90 度。不占用额外内存空间能否做到?

class Solution {
    public void rotate(int[][] matrix) {
        int n = matrix.length - 1;
        for (int i = 0; i < matrix.length / 2; i++) {
            for (int j = i; j < n - i; j++) {
                // 获取各顶点的值
                int a = matrix[i][j]; // 左上角
                int b = matrix[j][n - i]; // 右上角
                int c = matrix[n - i][n - j]; // 右下角
                int d = matrix[n - j][i]; // 左下角
                // 交换各顶点的值
                matrix[i][j] = d; // 左上角
                matrix[j][n - i] = a; // 右上角
                matrix[n - i][n - j] = b; // 右下角
                matrix[n - j][i] = c; // 左下角
            }
        }
    }
}

8,零矩阵

题目:编写一种算法,若M × N矩阵中某个元素为0,则将其所在的行与列清零。

class Solution {
    public void setZeroes(int[][] matrix) {
        ArrayList<int[]> arrayList = new ArrayList();
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                if (matrix[i][j] == 0) {
                    arrayList.add(new int[]{i, j});
                }
            }
        }
        for (int k = 0; k < arrayList.size(); k++) {
            for (int i = 0; i < matrix.length; i++) {
                matrix[i][arrayList.get(k)[1]] = 0;
            }
            for (int j = 0; j < matrix[0].length; j++) {
                matrix[arrayList.get(k)[0]][j] = 0;
            }
        }
    }
}

9,字符串轮转

题目:字符串轮转。给定两个字符串s1s2,请编写代码检查s2是否为s1旋转而成(比如,waterbottleerbottlewat旋转后的字符串)。

class Solution {
    public boolean isFlipedString(String s1, String s2) {
        if (s1.length() != s2.length()) {
            return false;
        }
        if (s1.equals("")) {
            return true;
        }
        for (int i = 0; i < s1.length(); i++) {
            String temp = s1.substring(i) + s1.substring(0, i);
            if (temp.equals(s2)) {
                return true;
            }
        }
        return false;
    }
}

10,删除重复节点

题目:编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。

class Solution {
    public ListNode removeDuplicateNodes(ListNode head) {
        if (head == null) {
            return head;
        }
       ListNode p = head;
        Set<Integer> set = new HashSet<Integer>();
        set.add(p.val);
        while (p != null && p.next != null) {
            if (set.contains(p.next.val)) {
                p.next = p.next.next;
            } else {
                set.add(p.next.val);
                p = p.next;
            }
        }
        return head;
    }
}

11,返回倒数第K个节点

题目:实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。

class Solution {
    public int kthToLast(ListNode head, int k) {
        ListNode p = head;
        ListNode q = head;
        int i = 0;
        while (p != null) {
            if (i >= k) {
                q = q.next;
            }
            p = p.next;
            i++;
        }
        return q.val;
    }
}

12,删除中间节点

题目:若链表中的某个节点,既不是链表头节点,也不是链表尾节点,则称其为该链表的「中间节点」。假定已知链表的某一个中间节点,请实现一种算法,将该节点从链表中删除。例如,传入节点 c(位于单向链表 a->b->c->d->e->f 中),将其删除后,剩余链表为 a->b->d->e->f

class Solution {
    public void deleteNode(ListNode node) {
        node.val = node.next.val;
        node.next = node.next.next;
    }
}

13,分割链表

题目:给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。你不需要 保留 每个分区中各节点的初始相对位置。

class Solution {
    public ListNode partition(ListNode head, int x) {
        ListNode p = head;
        ListNode min = new ListNode(101);
        ListNode max = new ListNode(101);
        ListNode min2 = min;
        ListNode max2 = max;
        while (p != null) {
            if (p.val < x) {
                min.next = new ListNode(p.val);
                min = min.next;
            } else {
                max.next = new ListNode(p.val);
                max = max.next;
            }
            p = p.next;
        }
        ListNode q = min2;
        while (q.next != null) {
            q = q.next;
        }
        q.next = max2.next;
        return min2.next;
    }
}

14,链表求和

题目:给定两个用链表表示的整数,每个节点包含一个数位。这些数位是反向存放的,也就是个位排在链表首部。编写函数对这两个整数求和,并用链表形式返回结果。

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
       ListNode p = l1;
		ListNode q = l2;
		int addNum=0;
		while (q != null) {
			if (p.next == null&&q.next!=null) {
				p.next = new ListNode(0);
			}
			if (p.next != null&&q.next==null) {
				q.next = new ListNode(0);
			}
			
			int sumAll = addNum + p.val + q.val;
			p.val=sumAll%10;
			addNum=sumAll/10;
			if(p.next == null && q.next == null && addNum!=0)
	              p.next = new ListNode(addNum);
			p = p.next;
			q = q.next;
			
		}
		return l1;
    }
}

15,回文链表

题目:给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

class Solution {
    public boolean isPalindrome(ListNode head) {
        String a = "";
        while (head != null) {
            a += head.val;
            head = head.next;
        }
        return huiwen(a);
    }

    public boolean huiwen(String s) {
        int left = 0;
        int right = s.length() - 1;
        while (left >= 0 && right < s.length() && left < right && s.charAt(left) == s.charAt(right)) {
            left++;
            right--;
        }
        return right <= left;
    }
}

16,链表相交

题目:给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode p = headA;
        ListNode q = headB;
        ArrayList arrayList = new ArrayList();
        while (p!=null){
            arrayList.add(p.hashCode());
            p = p.next;
        }
        while (q!=null){
            if (arrayList.contains(q.hashCode())){
                return q;
            }else {
                q = q.next;
            }
        }
        return null;
    }
}

17,环路检测

题目:给定一个链表,如果它是有环链表,实现一个算法返回环路的开头节点。若环不存在,请返回 null

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ArrayList a = new ArrayList();
        ListNode p = head;
        while (p != null) {
            if (a.contains(p.hashCode())) {
                return p;
            } else {
                a.add(p.hashCode());
            }
            p = p.next;
        }
        return null;
    }
}

18,三合一

题目:三合一。描述如何只用一个数组来实现三个栈。你应该实现push(stackNum, value)pop(stackNum)isEmpty(stackNum)peek(stackNum)方法。stackNum表示栈下标,value表示压入的值。构造函数会传入一个stackSize参数,代表每个栈的大小。

class TripleInOne {

    int size;
    int[] stack;
    int[] top;

    public TripleInOne(int stackSize) {
        size = stackSize;
        stack = new int[3 * size];
        top = new int[3];
    }

    public void push(int stackNum, int value) {
        if (top[stackNum] < size)
            stack[stackNum * size + top[stackNum]++] = value;
    }

    public int pop(int stackNum) {
        return isEmpty(stackNum) ? -1 : stack[stackNum * size + --top[stackNum]];
    }

    public int peek(int stackNum) {
        return isEmpty(stackNum) ? -1 : stack[stackNum * size + top[stackNum] - 1];
    }

    public boolean isEmpty(int stackNum) {
        return top[stackNum] == 0;
    }
}
class TripleInOne {

    int size;
    int[][] stack;
    int[] top;

    public TripleInOne(int stackSize) {
        size = stackSize;
        stack = new int[3][size];
        top = new int[3];
    }

    public void push(int stackNum, int value) {
        if (top[stackNum] < size)
            stack[stackNum][top[stackNum]++] = value;
    }

    public int pop(int stackNum) {
        return isEmpty(stackNum) ? -1 : stack[stackNum][--top[stackNum]];
    }

    public int peek(int stackNum) {
        return isEmpty(stackNum) ? -1 : stack[stackNum][top[stackNum] - 1];
    }

    public boolean isEmpty(int stackNum) {
        return top[stackNum] == 0;
    }
}

19,栈的最小值

题目:请设计一个栈,除了常规栈支持的pop与push函数以外,还支持min函数,该函数返回栈元素中的最小值。执行push、pop和min操作的时间复杂度必须为O(1)。

class MinStack {
    int result[] = new int[30000];
    int index = 0;
 
    public MinStack() {
    }
 
    public void push(int val) {
        result[index] = val;
        index++;
    }
 
    public void pop() {
        result[index-1] = Integer.MAX_VALUE;
        index--;
    }
 
    public int top() {
        return result[index-1];
    }
 
    public int getMin() {
        int min = Integer.MAX_VALUE;
        for (int i = 0; i < index; i++) {
            min = Integer.min(min,result[i]);
        }
        return min;
    }
}

20,堆盘子

题目:堆盘子。设想有一堆盘子,堆太高可能会倒下来。因此,在现实生活中,盘子堆到一定高度时,我们就会另外堆一堆盘子。请实现数据结构SetOfStacks,模拟这种行为。SetOfStacks应该由多个栈组成,并且在前一个栈填满时新建一个栈。此外,SetOfStacks.push()SetOfStacks.pop()应该与普通栈的操作方法相同(也就是说,pop()返回的值,应该跟只有一个栈时的情况一样)。 进阶:实现一个popAt(int index)方法,根据指定的子栈,执行pop操作。

当某个栈为空时,应当删除该栈。当栈中没有元素或不存在该栈时,poppopAt 应返回 -1.

import java.util.ArrayList;
import java.util.Stack;

public class StackOfPlates {
    private ArrayList<Stack<Integer>> stacks;
    private int capacity;

    public StackOfPlates(int cap) {
        stacks = new ArrayList<>();
        capacity = cap;
    }

    public void push(int val) {
         if (capacity <= 0) {
                return;
            }
        Stack<Integer> lastStack = getLastStack();
        if (lastStack != null && lastStack.size() < capacity) {
            lastStack.push(val);
        } else {
            Stack<Integer> newStack = new Stack<>();
            newStack.push(val);
            stacks.add(newStack);
        }
    }

    public int pop() {
        return popAt(stacks.size() - 1);
    }

    public int popAt(int index) {
        if (index < 0 || index >= stacks.size()) {
                return -1;
        }
        Stack<Integer> stack = stacks.get(index);
        if (stack.isEmpty()) {
                return -1;
            }
        int value = stack.pop();
        if (stack.isEmpty()) {
            stacks.remove(index);
        }
        return value;
    }

    private Stack<Integer> getLastStack() {
        if (stacks.isEmpty()) {
            return null;
        }
        return stacks.get(stacks.size() - 1);
    }
}

21,化栈为队

题目:实现一个MyQueue类,该类用两个栈来实现一个队列。

public class MyQueue {
    Stack<Integer> a = null;
    Stack<Integer> b = null;
 
    public MyQueue() {
        a = new Stack();
        b = new Stack();
    }
 
    public void push(int x) {
        a.push(x);
    }
 
    public int pop() {
        if (b.isEmpty()) {
            while (!a.isEmpty()) {
                b.push(a.pop());
            }
        }
        return b.pop();
    }
 
    public int peek() {
        if (b.isEmpty()) {
            while (!a.isEmpty()) {
                b.push(a.pop());
            }
        }
        return b.peek();
    }
 
    public boolean empty() {
        return a.isEmpty()&& b.isEmpty();
    }
}

22,栈排序

题目:栈排序。 编写程序,对栈进行排序使最小元素位于栈顶。最多只能使用一个其他的临时栈存放数据,但不得将元素复制到别的数据结构(如数组)中。该栈支持如下操作:pushpoppeek 和 isEmpty。当栈为空时,peek 返回 -1。

class SortedStack {
 int result[] = new int[5000];
    int index = 0;

    public SortedStack() {
        Arrays.fill(result, -1);
    }

    public void push(int val) {
        result[index] = val;
        index++;
    }

    public void pop() {
        int[] s = getMin();
        result[s[1]] = -1;
    }

    public int peek() {
        int[] s = getMin();
        return result[s[1]];
    }

    public int[] getMin() {
        int min = Integer.MAX_VALUE;
        int in = 0;
        for (int i = 0; i < index; i++) {
            if (min > result[i]&& result[i] != -1) {
                min = result[i];
                in = i;
            }
        }
        return new int[]{min, in};
    }

    public boolean isEmpty() {
        for (int i = 0; i < index; i++) {
            if (result[i] != -1) {
                return false;
            }
        }
        return true;
    }
}

23,动物收容所

题目:动物收容所。有家动物收容所只收容狗与猫,且严格遵守“先进先出”的原则。在收养该收容所的动物时,收养人只能收养所有动物中“最老”(由其进入收容所的时间长短而定)的动物,或者可以挑选猫或狗(同时必须收养此类动物中“最老”的)。换言之,收养人不能自由挑选想收养的对象。请创建适用于这个系统的数据结构,实现各种操作方法,比如enqueuedequeueAnydequeueDogdequeueCat。允许使用Java内置的LinkedList数据结构。

  • enqueue方法有一个animal参数,animal[0]代表动物编号,animal[1]代表动物种类,其中 0 代表猫,1 代表狗。
  • dequeue*方法返回一个列表[动物编号, 动物种类],若没有可以收养的动物,则返回[-1,-1]
class AnimalShelf {

    LinkedList<int[]> linkedList = new LinkedList();

    public AnimalShelf() {
    }

    public void enqueue(int[] animal) {
        linkedList.add(animal);
    }

    public int[] dequeueAny() {
        for (int i = 0; i < linkedList.size(); i++) {
            if (linkedList.get(i) != null) {
                int[] result = linkedList.get(i);
                linkedList.remove(i);
                return result;
            }
        }
        return new int[]{-1, -1};
    }

    public int[] dequeueDog() {
        for (int i = 0; i < linkedList.size(); i++) {
            if (linkedList.get(i) != null && linkedList.get(i)[1] == 1) {
                int[] result = linkedList.get(i);
                linkedList.remove(i);
                return result;
            }
        }
        return new int[]{-1, -1};
    }

    public int[] dequeueCat() {
        for (int i = 0; i < linkedList.size(); i++) {
            if (linkedList.get(i) != null && linkedList.get(i)[1] == 0) {
                int[] result = linkedList.get(i);
                linkedList.remove(i);
                return result;
            }
        }
        return new int[]{-1, -1};
    }
}

24,节点间通路

题目:节点间通路。给定有向图,设计一个算法,找出两个节点之间是否存在一条路径。

class Solution {
    public boolean findWhetherExistsPath(int n, int[][] graph, int start, int target) {
        Set<Integer>[] adjacentArr = new Set[n];
        for (int i = 0; i < n; i++) {
            adjacentArr[i] = new HashSet<Integer>();
        }
        for (int[] edge : graph) {
            if (edge[0] != edge[1]) {
                adjacentArr[edge[0]].add(edge[1]);
            }
        }
        boolean[] visited = new boolean[n];
        visited[start] = true;
        Queue<Integer> queue = new ArrayDeque<Integer>();
        queue.offer(start);
        while (!queue.isEmpty() && !visited[target]) {
            int vertex = queue.poll();
            Set<Integer> adjacent = adjacentArr[vertex];
            for (int next : adjacent) {
                if (!visited[next]) {
                    visited[next] = true;
                    queue.offer(next);
                }
            }
        }
        return visited[target];
    }
}

25,最小高度树

题目:给定一个有序整数数组,元素各不相同且按升序排列,编写一个算法,创建一棵高度最小的二叉搜索树。

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return nums == null ? null : buildTree(nums, 0, nums.length - 1);
    }
 
    private TreeNode buildTree(int[] nums, int l, int r) {
        if (l > r) {
            return null;
        }
        int middle = l + (r - l) / 2;
        TreeNode root = new TreeNode(nums[middle]);
        root.left = buildTree(nums, l, middle - 1);
        root.right = buildTree(nums, middle + 1, r);
        return root;
    }
}

26,特定深度节点链表

题目:给定一棵二叉树,设计一个算法,创建含有某一深度上所有节点的链表(比如,若一棵树的深度为 D,则会创建出 D 个链表)。返回一个包含所有深度的链表的数组。

class Solution {
    public ListNode[] listOfDepth(TreeNode root) {
        if (root == null) {
            return null;
        }
        Queue<TreeNode> queue = new LinkedList<>();//队列里面存放结点
        List result = new ArrayList<>();
        queue.offer(root);//根节点先入队
        while (!queue.isEmpty()) {
            int levelNum = queue.size();//获取当前层的节点数.
            //遍历当前层结点
            ListNode temp = new ListNode();
            ListNode p = temp;
            for (int i = 0; i < levelNum; i++) {
                //队首出队并将value加入子list
                TreeNode node = queue.poll();
                if (node != null) {
                    p.next= new ListNode(node.val);
                    p = p.next;
                    if (node.left != null) {//如果队首的左结点不为空就把左结点入队
                        queue.offer(node.left);
                    }
                    if (node.right != null) {//如果队首的右结点不为空就把右结点入队
                        queue.offer(node.right);
                    }
                }
            }
            result.add(temp.next);
        }
        return (ListNode[]) result.toArray(new ListNode[result.size()]);
    }
}

27,检查平衡性

题目:实现一个函数,检查二叉树是否平衡。在这个问题中,平衡树的定义如下:任意一个节点,其两棵子树的高度差不超过 1。

class Solution {
   boolean flag = true;
 
    public boolean isBalanced(TreeNode root) {
        height(root);
        return flag;
    }
 
    public int height(TreeNode p) {    //返回以p结点为根的子树高度,后根次序遍历
        if (p == null)
            return 0;
        int lh = height(p.left);           //返回左子树的高度
        int rh = height(p.right);          //返回右子树的高度
        if (Math.abs(lh - rh) >= 2) {
            flag = false;
        }
        return (lh >= rh) ? lh + 1 : rh + 1;     //当前子树高度为较高子树的高度加1
    }
}

28,合法二叉搜索树

题目:实现一个函数,检查一棵二叉树是否为二叉搜索树。

class Solution {
    List<Integer> a = new ArrayList();
 
    public void inorderTraversal(TreeNode root) {
        if (root != null) {
            inorderTraversal(root.left);
            a.add(root.val);
            inorderTraversal(root.right);
        }
    }
    public boolean isValidBST(TreeNode root) {
        inorderTraversal(root);
        for (int i = 0; i < a.size() - 1; i++) {
            if (a.get(i) >= a.get(i + 1)) {
                return false;
            }
        }
        return true;
    }
}

29,后继者

题目:设计一个算法,找出二叉搜索树中指定节点的“下一个”节点(也即中序后继)。

class Solution {
    List<TreeNode> a = new ArrayList();

    public void inorderTraversal(TreeNode root) {
        if (root != null) {
            inorderTraversal(root.left);
            a.add(root);
            inorderTraversal(root.right);
        }
    }

    public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
        inorderTraversal(root);
        for (int i = 0; i < a.size() - 1; i++) {
            if (a.get(i) == p) {
                return a.get(i + 1);
            }
        }
        return null;
    }
}

30,首个公共祖先

题目:设计并实现一个算法,找出二叉树中某两个节点的第一个共同祖先。不得将其他的节点存储在另外的数据结构中。注意:这不一定是二叉搜索树。例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]

class Solution {
     ArrayList<TreeNode> res = new ArrayList<>();
 
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        ArrayList<TreeNode> a = findNode(root, p);
        ArrayList<TreeNode> b = findNode(root, q);
        int f = Math.min(a.size(), b.size());
        for (int i = 0; i < f; i++) {
            if (a.get(i).val != b.get(i).val) {
                return a.get(i - 1);
            }
        }
        return a.get(f - 1);
    }
    public ArrayList<TreeNode> findNode(TreeNode root, TreeNode node) {
        res = new ArrayList<>();
        dfs(root, new ArrayList<>(), node);
        return res;
    }
 
    private void dfs(TreeNode root, List<TreeNode> path, TreeNode node) {
        if (root == null) return;
        path.add(root);
        if (root.val == node.val) {
            res = new ArrayList<>(path);
        }
        dfs(root.left, path, node);
        dfs(root.right, path, node);
        path.remove(path.size() - 1);
    }
}

31,二叉搜索树序列

题目:从左向右遍历一个数组,通过不断将其中的元素插入树中可以逐步地生成一棵二叉搜索树。给定一个由不同节点组成的二叉搜索树 root,输出所有可能生成此树的数组。

class Solution {
    private List<List<Integer>> ans;

    public List<List<Integer>> BSTSequences(TreeNode root) {
        ans = new ArrayList<>();
        List<Integer> path = new ArrayList<>();
        // 如果 root==null 返回 [[]]
        if (root == null) {
            ans.add(path);
            return ans;
        }
        List<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        // 开始进行回溯
        bfs(queue, path);
        return ans;
    }

    /**
     * 回溯法+广度优先遍历.
     */
    private void bfs(List<TreeNode> queue, List<Integer> path) {
        // queue 为空说明遍历完了,可以返回了
        if (queue.isEmpty()) {
            ans.add(new ArrayList<>(path));
            return;
        }
        // 将 queue 拷贝一份,用于稍后回溯
        List<TreeNode> copy = new ArrayList<>(queue);
        // 对 queue 进行循环,每循环考虑 “是否 「将当前 cur 节点从 queue 中取出并将其左右子
        // 节点加入 queue ,然后将 cur.val 加入到 path 末尾」 ” 的情况进行回溯
        for (int i = 0; i < queue.size(); i++) {
            TreeNode cur = queue.get(i);
            path.add(cur.val);
            queue.remove(i);
            // 将左右子节点加入队列
            if (cur.left != null) queue.add(cur.left);
            if (cur.right != null) queue.add(cur.right);
            bfs(queue, path);
            // 恢复 path 和 queue ,进行回溯
            path.remove(path.size() - 1);
            queue = new ArrayList<>(copy);
        }
    }
}

32,检查子树

题目:检查子树。你有两棵非常大的二叉树:T1,有几万个节点;T2,有几万个节点。设计一个算法,判断 T2 是否为 T1 的子树。如果 T1 有这么一个节点 n,其子树与 T2 一模一样,则 T2 为 T1 的子树,也就是说,从节点 n 处把树砍断,得到的树与 T2 完全相同。注意:此题相对书上原题略有改动。

class Solution {
   public boolean checkSubTree(TreeNode t1, TreeNode t2) {
        return dfs(t1, t2);
    }

    public boolean dfs(TreeNode s, TreeNode t) {
        if (s == null) {
            return false;
        }
        return check(s, t) || dfs(s.left, t) || dfs(s.right, t);
    }

    public boolean check(TreeNode s, TreeNode t) {
        if (s == null && t == null) {
            return true;
        }
        if (s == null || t == null || s.val != t.val) {
            return false;
        }
        return check(s.left, t.left) && check(s.right, t.right);
    }
}

33,求和路径

题目:给定一棵二叉树,其中每个节点都含有一个整数数值(该值或正或负)。设计一个算法,打印节点数值总和等于某个给定值的所有路径的数量。注意,路径不一定非得从二叉树的根节点或叶节点开始或结束,但是其方向必须向下(只能从父节点指向子节点方向)。

class Solution {
 
    int cnt = 0;
 
    public int pathSum(TreeNode root, int targetSum) {
        if(root == null) {
            return 0;
        }
        dfs(root, targetSum);
        pathSum(root.left, targetSum);
        pathSum(root.right, targetSum);
        return cnt;
    }
 
    private void dfs(TreeNode root, long targetSum) {
        if(root == null) {
            return;
        }
        if(root.val == targetSum) {
            cnt++;
        }
        dfs(root.left, targetSum - root.val);
        dfs(root.right, targetSum - root.val);
    }
}

34,二进制插入

题目:给定两个整型数字 N 与 M,以及表示比特位置的 i 与 ji <= j,且从 0 位开始计算)。编写一种方法,使 M 对应的二进制数字插入 N 对应的二进制数字的第 i ~ j 位区域,不足之处用 0 补齐。具体插入过程如图所示。

class Solution {

    public int insertBits(int N, int M, int i, int j) {
        int ans = 0, bit;
        // m左移i位和要插入的位置对应上
        M <<= i;
        for (int k = 0; k < 32; k++) {
            // k在范围内时取在m中取位,不在范围时就去n中取位
            bit = (k >= i && k <= j) ? M & (1 << k) : N & (1 << k);
            // 按位动态构造就完事
            ans += bit;
        }
        return ans;
    }
}

35,二进制数转字符串

题目:二进制数转字符串。给定一个介于0和1之间的实数(如0.72),类型为double,打印它的二进制表达式。如果该数字无法精确地用32位以内的二进制表示,则打印“ERROR”。

class Solution {
    public String printBin(double num) {
        StringBuilder sb = new StringBuilder("0.");
        while (sb.length() <= 32 && num != 0) {
            num *= 2;
            int digit = (int) num;
            sb.append(digit);
            num -= digit;
        }
        return sb.length() <= 32 ? sb.toString() : "ERROR";
    }
}

36,翻转数位

题目:给定一个32位整数 num,你可以将一个数位从0变为1。请编写一个程序,找出你能够获得的最长的一串1的长度。

class Solution {
    public int reverseBits(int num) {
        String s = Integer.toBinaryString(num);
        String temp = "";
        if (s.length() < 32) {
            for (int i = 0; i < 32 - s.length(); i++) {
                temp += "0";
            }
        }
        s = temp + s;
        char[] c = s.toCharArray();
        int max = 0;
        int left = 0;
        int right = 0;
        int index = 0;
        boolean flag = false;
        while (left <= s.length() - 1 && right <= s.length() - 1) {
            if (c[right] == '0') {
                if (flag) {
                    max = Math.max(right - left, max);
                    left = index;
                    right = index;
                    flag = false;
                } else {
                    index = right + 1;
                    right++;
                    flag = true;
                }
            } else {
                right++;
            }
        }
        return Math.max(right - left , max);
    }
}

37,下一个数

题目:下一个数。给定一个正整数,找出与其二进制表达式中1的个数相同且大小最接近的那两个数(一个略大,一个略小)。

  • num的范围在[1, 2147483647]之间;
  • 如果找不到前一个或者后一个满足条件的正数,那么输出 -1。
class Solution {
    public int[] findClosedNumbers(int num) {
        PriorityQueue<Integer> queue = new PriorityQueue<Integer>(new Comparator<Integer>() {
            public int compare(Integer o1, Integer o2) {
                return Math.abs(o1 - num) - Math.abs(o2 - num);
            }
        });
        int left = num - 1;
        int right = num + 1;
        int[] result = new int[4];
        int s = Integer.bitCount(num);

        for (int i = 0; i < 10000; i++) {
            int l = Integer.bitCount(left);
            if (s == l) {
                queue.add(left);
            }
            left--;
            if (queue.size() == 1 || left <= 0) {
                break;
            }
        }
        for (int i = 0; i < 10000; i++) {
            int r = Integer.bitCount(right);
            if (s == r) {
                queue.add(right);
            }
            right++;
            if (queue.size() == 2 || right >= 2147483647) {
                break;
            }
        }
        if (queue.size() == 0) {
            return new int[]{-1, -1};
        }else if (queue.size()==1){
            int c = queue.poll();
            if (c>num){
                return new int[]{c,-1};
            }else {
                return new int[]{-1,c};
            }
        }
        int a = queue.poll();
        int b = queue.poll();
        if (a > num && b < num) {
            return new int[]{a, b};
        } else if (a < num && b > num) {
            return new int[]{b, a};
        } else if (a > num && b > num) {
            return new int[]{a, -1};
        } else {
            return new int[]{-1, b};
        }
    }
}

38,整数转换

题目:整数转换。编写一个函数,确定需要改变几个位才能将整数A转成整数B。

class Solution {
    public int convertInteger(int A, int B) {
         return Integer.bitCount(A^B);
    }
}

39,配对交换

题目:配对交换。编写程序,交换某个整数的奇数位和偶数位,尽量使用较少的指令(也就是说,位0与位1交换,位2与位3交换,以此类推)。

class Solution {
    public int exchangeBits(int num) {
        String str = Integer.toBinaryString(num);
        if (str.length() % 2 != 0) {
            str = "0" + str;
        }
        char[] c = str.toCharArray();
        String result = "";
        for (int i = 0; i < str.length(); ) {
            result += c[i + 1];
            result += c[i];
            i += 2;
        }
        return Integer.parseInt(result, 2);
    }
}

40,绘制直线

题目:已知一个由像素点组成的单色屏幕,每行均有 w 个像素点,所有像素点初始为 0,左上角位置为 (0,0)。现将每行的像素点按照「每 32 个像素点」为一组存放在一个 int 中,再依次存入长度为 length 的一维数组中。我们将在屏幕上绘制一条从点 (x1,y) 到点 (x2,y) 的直线(即像素点修改为 1),请返回绘制过后的数组。

class Solution {
    public int[] drawLine(int length, int w, int x1, int x2, int y) {
        int[] results = new int[length];
        int[] nums = new int[w];
        for (int i = x1; i <= x2; i++) {
            nums[i] = 1;
        }
        int row = (w / 32) * y;
        for (int i = row; i < row + w / 32 && i < length; i++) {
            String temp = "0";
            for (int j = (i - row) * 32; j < (i - row + 1) * 32 && j < w; j++) {
                temp += nums[j];
            }
            results[i] = (int) Long.parseLong(temp, 2);
        }
        return results;
    }
}

41,三步问题

题目:三步问题。有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶、2阶或3阶。实现一种方法,计算小孩有多少种上楼梯的方式。结果可能很大,你需要对结果模1000000007。

class Solution {
    public int waysToStep(int n) {
         long f1 = 0, f2 = 0, f3 = 0, fx = 1;
        for (int i = 1; i <= n; ++i) {
            f1 = f2 % 1000000007;
            f2 = f3 % 1000000007;
            f3 = fx % 1000000007;
            fx = f1 + f2 + f3;
        }
        return (int) (fx % 1000000007);
    }
}

42,迷路的机器人

题目:设想有个机器人坐在一个网格的左上角,网格 r 行 c 列。机器人只能向下或向右移动,但不能走到一些被禁止的网格(有障碍物)。设计一种算法,寻找机器人从左上角移动到右下角的路径。

class Solution {
    static List<List<Integer>> result = new ArrayList<>();

    public List<List<Integer>> pathWithObstacles(int[][] obstacleGrid) {
        result.clear();
        if (obstacleGrid[0][0] == 1) return result;
        dfs(obstacleGrid, obstacleGrid.length - 1, obstacleGrid[0].length - 1);
        return result;
    }

    public boolean dfs(int[][] obstacleGrid, int i, int j) {
        if (i == 0 && j == 0) {
            add(i, j);
            return true;
        }

        if (obstacleGrid[i][j] > 0) return false;
        obstacleGrid[i][j] = 2;
        if (i > 0 && dfs(obstacleGrid, i - 1, j)) {
            add(i, j);
            return true;
        }
        if (j > 0 && dfs(obstacleGrid, i, j - 1)) {
            add(i, j);
            return true;
        }
        //不取消标记
        //obstacleGrid[i][j] = 2;
        return false;
    }

    public void add(int i, int j) {
        List<Integer> list = new ArrayList<>();
        list.add(i);
        list.add(j);
        result.add(list);
    }
}

43,魔术索引

题目:魔术索引。 在数组A[0...n-1]中,有所谓的魔术索引,满足条件A[i] = i。给定一个有序整数数组,编写一种方法找出魔术索引,若有的话,在数组A中找出一个魔术索引,如果没有,则返回-1。若有多个魔术索引,返回索引值最小的一个。

class Solution {
    public int findMagicIndex(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == i) {
                return i;
            }
        }
        return -1;
    }
}

44,幂集

题目:幂集。编写一种方法,返回某集合的所有子集。集合中不包含重复的元素。说明:解集不能包含重复的子集。

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        // 从 1 开始是题目的设定
        Deque<Integer> path = new ArrayDeque<>();
        for (int k = 0; k <= nums.length; k++) {
            dfs(nums, k, 0, path, res);
        }
        return res;
    }
    public void dfs(int[] nums, int k, int begin, Deque<Integer> path, List<List<Integer>> res) {
        // 递归终止条件是:path 的长度等于 k
        if (path.size() == k) {
            res.add(new ArrayList<>(path));
            return;
        }
        // 遍历可能的搜索起点
        for (int i = begin; i < nums.length; i++) {
            // 向路径变量里添加一个数
            path.addLast(nums[i]);
            // 下一轮搜索,设置的搜索起点要加 1,因为组合数理不允许出现重复的元素
            dfs(nums, k, i + 1, path, res);
            // 重点理解这里:深度优先遍历有回头的过程,因此递归之前做了什么,递归之后需要做相同操作的逆向操作
            path.removeLast();
        }
    }
}

45,递归乘法

题目:递归乘法。 写一个递归函数,不使用 * 运算符, 实现两个正整数的相乘。可以使用加号、减号、位移,但要吝啬一些。

class Solution {
    public int multiply(int A, int B) {
        int sum = 0;
        int min = Math.min(A, B);
        int max = Math.max(A, B);
        for (int i = 0; i < min; i++) {
            sum += max;
        }
        return sum;
    }
}

46,汉诺塔问题

题目:在经典汉诺塔问题中,有 3 根柱子及 N 个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。一开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。移动圆盘时受到以下限制:

  • 每次只能移动一个盘子;
  • 盘子只能从柱子顶端滑出移到下一根柱子;
  • 盘子只能叠在比它大的盘子上。

请编写程序,用栈将所有盘子从第一根柱子移到最后一根柱子。

class Solution {
    public void move(List<Integer> x, int n, List<Integer> z) {
        int index = x.size() - 1;
        z.add(x.get(index));
        x.remove(x.size() - 1);
    }

    public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {
        hanoi(A.size(), A, B, C);
    }

    public void hanoi(int n, List<Integer> A, List<Integer> B, List<Integer> C) {
        if (n == 1) {
            move(A, 1, C); // 将编号为1的圆盘从x移到z
        } else {
            hanoi(n - 1, A, C, B);// 将x上编号为1至n-1的圆盘移到y,z作辅助塔
            move(A, n, C);// 将编号为n的圆盘从x移到z
            hanoi(n - 1, B, A, C);// 将y上编号为1至n-1的圆盘移到z,x作辅助塔
        }
    }
}

47,无重复字符串的排列组合

题目:编写一种方法,计算某字符串的所有排列组合,字符串每个字符均不相同。

class Solution {
    List<String> res = new ArrayList<>();
    public String[] permutation(String s) {
        boolean[] isVisited = new boolean[s.length()];
        trackback(s,isVisited,new StringBuffer());
        String[] strs = new String[res.size()];
        for(int i=0;i<res.size();i++){
            strs[i] = res.get(i);
        }
        return strs;
    }
    public void trackback(String s,boolean[] isVisited,StringBuffer stringbuffer){
        if(stringbuffer.length()==s.length()){
            res.add(stringbuffer.toString());
            return;
        }
        for(int i=0;i<s.length();i++){
            if(isVisited[i]==false){
                stringbuffer.append(s.charAt(i));
                isVisited[i]=true;
                trackback(s,isVisited,stringbuffer);
                stringbuffer.deleteCharAt(stringbuffer.length()-1);
                isVisited[i] = false;
            }
        }
    }
}

48,有重复字符串的排列组合

题目:有重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合。

class Solution {
    List<String> res = new ArrayList<>();
    public String[] permutation(String s) {
        boolean[] isVisited = new boolean[s.length()];
        trackback(s,isVisited,new StringBuffer());
        String[] strs = new String[res.size()];
        for(int i=0;i<res.size();i++){
            strs[i] = res.get(i);
        }
        return strs;
    }
    public void trackback(String s,boolean[] isVisited,StringBuffer stringbuffer){
        if(stringbuffer.length()==s.length()&&!res.contains(stringbuffer.toString())){
            res.add(stringbuffer.toString());
            return;
        }
        for(int i=0;i<s.length();i++){
            if(isVisited[i]==false){
                stringbuffer.append(s.charAt(i));
                isVisited[i]=true;
                trackback(s,isVisited,stringbuffer);
                stringbuffer.deleteCharAt(stringbuffer.length()-1);
                isVisited[i] = false;
            }
        }
    }
}

49,括号

题目:设计一种算法,打印n对括号的所有合法的(例如,开闭一一对应)组合。说明:解集不能包含重复的子集。

class Solution {
    List<String> res;
    public List<String> generateParenthesis(int n) {
       res = new ArrayList<>();
       dfs(n, n, "");
       return res;
    }
    private void dfs(int left, int rigth, String parenthesis){
        if(left == 0 && rigth == 0){
            res.add(parenthesis);
        }
        if(left > 0){
            dfs(left - 1, rigth, parenthesis + "(");
        }
        if(left < rigth){
            dfs(left, rigth - 1, parenthesis + ")");
        }
    }
}

50,颜色填充

题目:编写函数,实现许多图片编辑软件都支持的「颜色填充」功能。待填充的图像用二维数组 image 表示,元素为初始颜色值。初始坐标点的行坐标为 sr 列坐标为 sc。需要填充的新颜色为 newColor 。

「周围区域」是指颜色相同且在上、下、左、右四个方向上存在相连情况的若干元素。请用新颜色填充初始坐标点的周围区域,并返回填充后的图像。

class Solution {
    int[] dx = {1, 0, 0, -1};
    int[] dy = {0, 1, -1, 0};

    public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {
        Queue<int[]> queue = new LinkedList<>();
        int heigh = image.length;
        int width = image[0].length;
        boolean[][] visited = new boolean[heigh][width];
        int oldColor = image[sr][sc];
        image[sr][sc] = newColor;
        queue.add(new int[]{sr, sc});
        while (!queue.isEmpty()) {
            int[] middle = queue.poll();
            for (int k = 0; k < 4; k++) {
                int newX = middle[0] + dx[k];
                int newY = middle[1] + dy[k];
                int[] newPoint = new int[]{newX, newY};
                if (newX >= 0 && newX < heigh && newY >= 0 && newY < width && visited[newX][newY] == false && image[newX][newY] == oldColor) {
                    queue.add(newPoint);
                    visited[newX][newY] = true;
                    image[newX][newY] = newColor;
                }
            }
        }
        return image;
    }
}

51,硬币

题目:给定数量不限的硬币,币值为25分、10分、5分和1分,编写代码计算n分有几种表示法。(结果可能会很大,你需要将结果模上1000000007)

class Solution {                 //一维动态规划
    public int waysToChange(int n) {
        int dp[] = new int[n+1];
        int[] tokens = {1,5,10,25}; 
        int mod = 1000000007;
        for(int i=0;i<4;i++){
            for(int j=1;j<=n;j++){
                if(tokens[i]==j){               //硬币刚好等于当前面额
                    dp[j] = (dp[j] + 1)%mod;
                }else if(tokens[i]<j){         //硬币小于当前面额
                    dp[j] = (dp[j] + dp[j-tokens[i]])%mod;
                }                               //硬币大于当前面额,dp[j] = dp[j],省略了
            }
        }
        return dp[n];
    }
}

52,八皇后

题目:设计一种算法,打印 N 皇后在 N × N 棋盘上的各种摆法,其中每个皇后都不同行、不同列,也不在对角线上。这里的“对角线”指的是所有的对角线,不只是平分整个棋盘的那两条对角线。

class Solution {
    private List<List<String>> res = new ArrayList<>(); // 最终答案

    public List<List<String>> solveNQueens(int n) {
        char[][] grid = new char[n][n]; // 定义棋盘
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                grid[i][j] = '.'; // 棋盘初始化默认都是空
            }
        }
        // 占用为true,未占用false
        // 记录第k列有没有被占用
        boolean[] col = new boolean[n];
        // 记录主对角线方向有没有被占用(左上到右下)
        // 该方向的x=row-col是固定的,范围为-n+1~n-1共2n-1个数,n-x之后范围是1~2n-1,用2n的数组就可以容纳
        boolean[] mainDiag = new boolean[2*n];
        // 记录副对角线方向有没有被占用(右上到左下)
        // 该方向的x=row+col是固定的,范围为0~2n-2共2n-1个数,用2n的数组也可以表示2n-1条对角方向
        boolean[] subDiag = new boolean[2*n];
        dfs(0, n, grid, col, mainDiag, subDiag); // 利用dfs为每一个皇后搜索摆放位置
        return res;
    }

    // 策略为每个皇后摆放一行,r代表当前摆放到行index, n为皇后个数,grid棋盘,后面3个冲突检查数组
    public void dfs(int r, int n, char[][] grid, boolean[] col, boolean[] mainDiag, boolean[] subDiag){
        if(r == n){ // 当最后一个皇后摆放完毕(任务成昆!)
            List<String> list = new ArrayList<>(); // 新list记录当前此种摆放结果
            for(int i=0;i<n;i++){ // 每一行
                list.add(new String(grid[i])); // 将char[]转成String添加进去
            }
            res.add(list); // 此种摆放结果添加到结果集
            return; 
        }
        for(int c=0;c<n;c++){ // 对每一列遍历(摆放女王,列也不能重复)
            // 该列空,该位置主对角线方向空,该位置副对角线方向空
            if(!col[c] && !mainDiag[n-r+c] && !subDiag[r+c]){
                // 可以摆放,棋盘记录
                grid[r][c] = 'Q';
                // 更新冲突数组
                col[c] = mainDiag[n-r+c] = subDiag[r+c] = true;
                // 摆放下一个皇后
                dfs(r+1, n, grid, col, mainDiag, subDiag);
                // 撤销操作,不影响下一次摆放
                col[c] = mainDiag[n-r+c] = subDiag[r+c] = false;
                grid[r][c] = '.';
            }
        }
    }
}

53,堆箱子

题目:给你一堆n个箱子,箱子宽 wi、深 di、高 hi。箱子不能翻转,将箱子堆起来时,下面箱子的宽度、高度和深度必须大于上面的箱子。实现一种方法,搭出最高的一堆箱子。箱堆的高度为每个箱子高度的总和。输入使用数组[wi, di, hi]表示每个箱子。

思路:如果只追求O(n^2)的解法,将box按照宽,深,高的优先级排序,再进行n^2的dp就可以了。dp[i]表示以箱子i为最底下的箱子时能堆的最大高度。

class Solution {
    public int pileBox(int[][] box) {
        Arrays.sort(box, (i, j) -> (i[0] != j[0] ? i[0] - j[0] : (i[1] != j[1] ? i[1] - j[1] : i[2] - j[2])));
        int[] dp = new int[box.length];
        for(int i = 0; i < dp.length; i++) {
            dp[i] = box[i][2];
            for(int j = 0; j < i; j++) if (box[i][0] > box[j][0] && box[i][1] > box[j][1] && box[i][2] > box[j][2])
                dp[i] = Math.max(dp[i], box[i][2] + dp[j]);
        }
        return Arrays.stream(dp).max().orElse(0);
    }
}

54,布尔运算

题目:给定一个布尔表达式和一个期望的布尔结果 result,布尔表达式由 0 (false)、1 (true)、& (AND)、 | (OR) 和 ^ (XOR) 符号组成。实现一个函数,算出有几种可使该表达式得出 result 值的括号方法。

/*
思路:在某个运算符处进行分割,然后因为运算符左右两边只存在两种结果。 0 和 1
比如 "0&0&0	  &   1^1|0"	 
运算符 & 分割后,左边的结果最终只能是 1 或 0 其中一个
右边的结果最终只能是 1 或 0 其中一个
即最终是 0 和 0 、1 和 1、 0 和 1、 1 和 0 进行 &,那么我们只需要统计左右两边存在多少个 0 和 1
进行乘积即可
*/
class Solution {
    public int countEval(String s, int result) {

        int len = s.length();
        if(len == 1){
            return s.charAt(0) - '0' == result ? 1 : 0;
        }
        int[][][] memo = new int[len][len][];
        int c = 0;
        char[] chs = s.toCharArray();
        //从分割符进行分割
        for(int i = 1; i < len; i += 2){
            int[] left = helper(chs, 0, i - 1, memo);
            int[] right = helper(chs, i + 1, len - 1, memo);
            int n1 = bool(0, 0, chs[i]);
            int n2 = bool(1, 1, chs[i]);
            int n3 = bool(0, 1, chs[i]);
            if(n1 == result){
                c += left[0] * right[0];
            }
            if(n2 == result){
                c += left[1] * right[1];
            }
            if(n3 == result){
                c += left[0] * right[1] + left[1] * right[0];
            }
        }
        return c;
    }
    /*
    最终 bool 运算只有两种结果 ,为 0 和 为 1
    那么我们直接统计左边结果为 0 的个数 和 结果为 1 的个数
    */
    private int[] helper(char[] chs, int l, int r, int[][][] memo){
        if(memo[l][r] != null){
            return memo[l][r];
        }
        int[] res = new int[2];

        memo[l][r] = res;

        if(l == r){
            if(chs[l] == '0'){
                res[0] = 1;
            }else{
                res[1] = 1;
            }
            return res;
        }
        for(int i = l + 1; i <= r; i += 2){
            int[] left = helper(chs, l, i - 1, memo);
            int[] right = helper(chs, i + 1, r, memo);
            int n1 = bool(0, 0, chs[i]);
            int n2 = bool(1, 1, chs[i]);
            int n3 = bool(0, 1, chs[i]);
            res[n1] += left[0] * right[0];
            res[n2] += left[1] * right[1];
            res[n3] += left[0] * right[1] + left[1] * right[0];
        }
        return res;
    }

    private int bool(int n1, int n2, char ch){
        switch(ch){
            case '&':
                return n1 & n2;
            case '|':
                return n1 | n2;
            case '^':
                return n1 ^ n2;
        }
        return n1 & n2;
    }
}

55,合并排序数组

题目:给定两个排序后的数组 A 和 B,其中 A 的末端有足够的缓冲空间容纳 B。 编写一个方法,将 B 合并入 A 并排序。初始化 A 和 B 的元素数量分别为 m 和 n

class Solution {
    public void merge(int[] A, int m, int[] B, int n) {
        for (int i = m; i < m + n && n != 0; i++) {
            A[i] = B[i - m];
        }
        Arrays.sort(A);
    }
}

56,变位词组

题目:编写一种方法,对字符串数组进行排序,将所有变位词组合在一起。变位词是指字母相同,但排列不同的字符串。

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        List a = new ArrayList();
        for (int i = 0; i < strs.length; i++) {
            if (strs[i] != "0") {
                ArrayList b = new ArrayList();
                b.add(strs[i]);
                for (int j = i + 1; j < strs.length; j++) {
                    if (strs[j] != "0") {
                        if (isAnagram(strs[i], strs[j])) {
                            b.add(strs[j]);
                            strs[j] = "0";
                        }
                    }
                }
                if (b.size() != 0) {
                    a.add(b);
                }
            }
        }
        return a;
    }
    public boolean isAnagram(String s1, String s2) {
        int n = s1.length(), m = s2.length();
        if (n > m || m > n) {
            return false;
        }
        int[] cnt1 = new int[26];
        int[] cnt2 = new int[26];
        for (int i = 0; i < n; ++i) {
            ++cnt1[s1.charAt(i) - 'a'];
            ++cnt2[s2.charAt(i) - 'a'];
        }
        if (Arrays.equals(cnt1, cnt2)) {
            return true;
        }
        for (int i = n; i < m; ++i) {
            ++cnt2[s2.charAt(i) - 'a'];
            --cnt2[s2.charAt(i - n) - 'a'];
            if (Arrays.equals(cnt1, cnt2)) {
                return true;
            }
        }
        return false;
    }
}

57,搜索旋转数组

题目:搜索旋转数组。给定一个排序后的数组,包含n个整数,但这个数组已被旋转过很多次了,次数不详。请编写代码找出数组中的某个元素,假设数组元素原先是按升序排列的。若有多个相同元素,返回索引值最小的一个。

class Solution {
    public int search(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        if (left == right) {
            if (nums[0] == target) {
                return 0;
            } else {
                return -1;
            }
        }
        while (left <= right) {
            if (nums[left] == target) {
                return  left;
            } else if (nums[right] == target) {
                while (right>=0){
                    if (nums[right-1]==target){
                        right--;
                    }else {
                        break;
                    }
                }
                return right;
            }
            if (nums[left] <= nums[left + 1]) {
                left++;
            }
            if (nums[right] >= nums[right - 1]) {
                right--;
            }
            if (left + 1 == right) {
                if (nums[left] == target) {
                    return left;
                }
                if (nums[right] == target) {
                    return right;
                }
                return -1;
            }
        }
        return -1;
    }
}

58,稀疏数组搜索

题目:稀疏数组搜索。有个排好序的字符串数组,其中散布着一些空字符串,编写一种方法,找出给定字符串的位置。

public int findString(String[] words, String s) {
          int left=0;int right=words.length-1;
          while(left<=right){
              int mid=(left+right)/2;
              if(words[mid].equals("")){
                  if(words[right].compareTo(s)==0){
                      return right;
                  }else{
                      right=right-1;
                  }
              }else if(words[mid].compareTo(s)<0){
                  left=mid+1;
              }else if(words[mid].compareTo(s)>0){
                  right=mid-1;
              }else{
                  return mid;
              }
          }
          return -1;
    }

59,排序矩阵查找

题目:给定M×N矩阵,每一行、每一列都按升序排列,请编写代码找出某元素。

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int row = matrix.length;
        if (row==0){
            return false;
        }
        int column = matrix[0].length;
        if (column==0){
            return false;
        }
        for (int i = 0; i < row; i++) {
            int temp = matrix[i][0];
            if (target == temp) {
                return true;
            } else if (target > temp) {
                for (int j = 1; j < column; j++) {
                    if (target == matrix[i][j]) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
}

60,数字流的秩

题目:假设你正在读取一串整数。每隔一段时间,你希望能找出数字 x 的秩(小于或等于 x 的值的个数)。请实现数据结构和算法来支持这些操作,也就是说:

  • 实现 track(int x) 方法,每读入一个数字都会调用该方法;
  • 实现 getRankOfNumber(int x) 方法,返回小于或等于 x 的值的个数。
class StreamRank {
    ArrayList<Integer> arrayList;

    public StreamRank() {
        arrayList = new ArrayList();
    }

    public void track(int x) {
        arrayList.add(x);
    }

    public int getRankOfNumber(int x) {
        int result = 0;
        for (int i = 0; i < arrayList.size(); i++) {
            if (arrayList.get(i) <= x) {
                result++;
            }
        }
        return result;
    }
}

61,峰与谷

题目:在一个整数数组中,“峰”是大于或等于相邻整数的元素,相应地,“谷”是小于或等于相邻整数的元素。例如,在数组{5, 8, 4, 2, 3, 4, 6}中,{8, 6}是峰, {5, 2}是谷。现在给定一个整数数组,将该数组按峰与谷的交替顺序排序。

class Solution {
    public void wiggleSort(int[] nums) {
        if (nums.length == 0 || nums.length == 1) {
            return;
        }
        Arrays.sort(nums);
        for (int i = 1; i < nums.length - 1; i += 2) {
            int temp = nums[i];
            nums[i] = nums[i + 1];
            nums[i + 1] = temp;
        }
    }
}

62,交换数字

题目:编写一个函数,不用临时变量,直接交换numbers = [a, b]ab的值。

class Solution {
    public int[] swapNumbers(int[] numbers) {
        numbers[1] = numbers[0] + numbers[1];
        numbers[0] = numbers[1] - numbers[0];
        numbers[1] = numbers[1] - numbers[0];
        return numbers;
    }
}

63,单词频率

题目:设计一个方法,找出任意指定单词在一本书中的出现频率。你的实现应该支持如下操作:

  • WordsFrequency(book)构造函数,参数为字符串数组构成的一本书
  • get(word)查询指定单词在书中出现的频率
class WordsFrequency {
    HashMap<String, Integer> hashMap = null;
    public WordsFrequency(String[] book) {
        hashMap = new HashMap<String, Integer>();
        for (String str : book) {
            hashMap.put(str, hashMap.getOrDefault(str, 0) + 1);
        }
    }

    public int get(String word) {
       if (hashMap.containsKey(word)){
            return hashMap.get(word);
        }else {
            return 0;
        }
    }
}

64,交点

题目:给定两条线段(表示为起点start = {X1, Y1}和终点end = {X2, Y2}),如果它们有交点,请计算其交点,没有交点则返回空值。要求浮点型误差不超过10^-6。若有多个交点(线段重叠)则返回 X 值最小的点,X 坐标相同则返回 Y 值最小的点。

class Solution {
    public double[] intersection(int[] start1, int[] end1, int[] start2, int[] end2) {
        //求两线段的最大点和最小点
        int xmin1 = start1[0] < end1[0] ? start1[0] : end1[0];
        int ymin1 = start1[1] < end1[1] ? start1[1] : end1[1];
        int xmin2 = start2[0] < end2[0] ? start2[0] : end2[0];
        int ymin2 = start2[1] < end2[1] ? start2[1] : end2[1];

        int xmax1 = start1[0] > end1[0] ? start1[0] : end1[0];
        int ymax1 = start1[1] > end1[1] ? start1[1] : end1[1];
        int xmax2 = start2[0] > end2[0] ? start2[0] : end2[0];
        int ymax2 = start2[1] > end2[1] ? start2[1] : end2[1];

        double x = 0;
        double y = 0;
        //如果两点在直线的同一侧,代入方程同号
        int f11 = fangcheng(start2[0],start2[1],start1[0],start1[1],end1[0],end1[1]);
        int f12 = fangcheng(end2[0],end2[1],start1[0],start1[1],end1[0],end1[1]);
        int f21 = fangcheng(start1[0],start1[1],start2[0],start2[1],end2[0],end2[1]);
        int f22 = fangcheng(end1[0],end1[1],start2[0],start2[1],end2[0],end2[1]);
        //两点在直线的同侧,没有交点
        if (f11 * f12 > 0 || f21*f22 > 0){
            return new double[0];
        }else if(f11 == f12 && f12 == f21 && f21 == f22){//四点共线
            // 没有交叉排列
            if (xmax1 < xmin2 || xmax2 < xmin1){
                return new double[0];
            }else if(xmax1 == xmin1 && xmin1 == xmax2 && xmax2 == xmin2){//垂直的线,判断y
                //没有交叉
                if (ymax1 < ymin2 || ymax2 < ymin1){
                    return new double[0];
                }else{
                    //取两个条线的小值中最大的,得交叉点小点
                    x = xmax1;
                    y = ymin1 > ymin2 ? ymin1 : ymin2;
                    return new double[]{x,y};
                }
            }else {
                //取两个条线的小值中最大的,得交叉点小点
                x = xmin1 > xmin2 ? xmin1 : xmin2;
                if (start1[0] == x){
                    y = start1[1];
                }else if(end1[0] == x){
                    y = end1[1];
                }else if (start2[0] == x){
                    y = start2[1];
                }else {
                    y = end2[1];
                }
                return new double[]{x,y};
            }
        }else{
            // 线段1垂直
            if (start1[0] == end1[0]){
                x = start1[0];
                double xue2 = 1.0*(start2[1] - end2[1])/(start2[0] - end2[0]);
                double b2 = end2[1] - xue2 * end2[0];
                y = xue2 * x + b2;
                return new double[]{x,y};
            }else if (start2[0] == end2[0]){//线段2垂直
                x = start2[0];
                double xue1 = 1.0*(start1[1] - end1[1])/(start1[0] - end1[0]);
                double b1 = end1[1] - xue1 * end2[0];
                y = xue1 * x + b1;
                return new double[]{x,y};
            }else {//通用情况,线段1和线段2都不垂直,计算斜率、常量值,y=a*x+b
                double xue1 = 1.0*(start1[1] - end1[1])/(start1[0] - end1[0]);//斜率1
                double xue2 = 1.0*(start2[1] - end2[1])/(start2[0] - end2[0]);//斜率2
                double b1 = end1[1] - xue1 * end1[0];//常量b1
                double b2 = end2[1] - xue2 * end2[0];//常量b2
                x = (b2 - b1)/(xue1 - xue2);
                y = xue1 * x + b1;
                return new double[]{x,y};
            }
        }
    }
    //验证两点是否在另一条直线的同一侧,同一侧同号,不同侧异号
    private int fangcheng(int x,int y,int x0,int y0,int x1,int y1){
        return (x-x0)*(y1-y0) - (x1-x0)*(y-y0);
    }
}

65,井字游戏

题目:设计一个算法,判断玩家是否赢了井字游戏。输入是一个 N x N 的数组棋盘,由字符" ","X"和"O"组成,其中字符" "代表一个空位。

以下是井字游戏的规则:

  • 玩家轮流将字符放入空位(" ")中。
  • 第一个玩家总是放字符"O",且第二个玩家总是放字符"X"。
  • "X"和"O"只允许放置在空位中,不允许对已放有字符的位置进行填充。
  • 当有N个相同(且非空)的字符填充任何行、列或对角线时,游戏结束,对应该字符的玩家获胜。
  • 当所有位置非空时,也算为游戏结束。
  • 如果游戏结束,玩家不允许再放置字符。

如果游戏存在获胜者,就返回该游戏的获胜者使用的字符("X"或"O");如果游戏以平局结束,则返回 "Draw";如果仍会有行动(游戏未结束),则返回 "Pending"。

class Solution {
    public String tictactoe(String[] board) {
        int length = board.length;
        String X = "";
        String O = "";
        for (int i = 0; i < length; i++) {
            X += "X";
            O += "O";
        }
        for (int i = 0; i < length; i++) {
            if (board[i].equals(O)) {
                return "O";
            } else if (board[i].equals(X)) {
                return "X";
            }
        }
        for (int i = 0; i < length; i++) {
            String temp = "";
            for (int j = 0; j < length; j++) {
                temp += board[j].charAt(i);
            }
            if (temp.equals(O)) {
                return "O";
            } else if (temp.equals(X)) {
                return "X";
            }
        }
        String temp1 = "";
        String temp2 = "";
        for (int i = 0; i < length; i++) {
            temp1 += board[i].charAt(i);
            temp2 += board[length - i - 1].charAt(i);
        }
        if (temp1.equals(O) || temp2.equals(O)) {
            return "O";
        } else if (temp1.equals(X) || temp2.equals(X)) {
            return "X";
        }

        for (int i = 0; i < length; i++) {
            if (board[i].trim().length() != length) {
                return "Pending";
            }
        }

        for (int i = 0; i < length; i++) {
            String temp = "";
            for (int j = 0; j < length; j++) {
                temp += board[i].charAt(j);
            }
            if (temp.trim().length() != length) {
                return "Pending";
            }
        }

        return "Draw";
    }
}

66,阶乘尾数

题目:设计一个算法,算出 n 阶乘有多少个尾随零。

class Solution {
    public int trailingZeroes(int n) {
        int count = 0;
        while (n >= 5) {
            count += n / 5;
            n /= 5;
        }
        return count;
    }
}

67,最小差

题目:给定两个整数数组ab,计算具有最小差绝对值的一对数值(每个数组中取一个值),并返回该对数值的差。

思路:首先对数组a和b进行排序,然后利用双指针的方法遍历两个数组,依次计算每对数值之差的绝对值,并记录最小值。由于数组已经排序,当a[i]小于b[j]时,移动i指针;否则移动j指针。这样可以保证在每次操作中,最小值所对应的数值一定会出现。最终返回最小差绝对值。

class Solution {
    public int smallestDifference(int[] a, int[] b) {
        Arrays.sort(a);
        Arrays.sort(b);
        int i = 0, j = 0;
        long minDiff = Integer.MAX_VALUE;
        while (i < a.length && j < b.length) {
            long temp = a[i] - b[j];
            long diff = Math.abs(temp);
            if (diff < minDiff) {
                minDiff = diff;
            }
            if (a[i] < b[j]) {
                i++;
            } else {
                j++;
            }
        }
        return (int) minDiff;
    }
}

68,最大数值

题目:编写一个方法,找出两个数字ab中最大的那一个。不得使用if-else或其他比较运算符。

思路:可以使用数学公式 max = (a + b + abs(a - b)) / 2 来实现找出两个数字a和b中最大的那一个。其中 abs(a - b) 表示两个数之差的绝对值,将其加到 (a + b) 中再除以2,即可得到较大的那个数。

class Solution {
    public int maximum(int a, int b) {
        long x = (long) a;
        long y = (long) b;
        long temp = Math.abs(x - y);
        long max = (x + y + temp) / 2;
        return (int) max;
    }
}

69,整数的英文表示

题目:给定一个整数,打印该整数的英文描述。

class Solution {
    String[] low = {"","One","Two","Three","Four","Five","Six","Seven","Eight","Nine"};
    String[] mid = {"Ten","Eleven","Twelve","Thirteen","Fourteen","Fifteen","Sixteen","Seventeen","Eighteen","Nineteen"};
    String[] high = {"","","Twenty","Thirty","Forty","Fifty","Sixty","Seventy","Eighty","Ninety"};


    public String numberToWords(int num) {
        if (num == 0){
            return "Zero";
        }
        return help(num);
    }
    public String help(int number){
        String res = new String();
        if (number < 10){
            res = low[number];
        }else if (number < 20){
            res = mid[number%10];
        }else if (number < 100){
            res = high[number / 10] + " " + help(number%10);
        }else if (number < 1000){
            res = help(number/100) + " Hundred " + help(number % 100);
        }else if (number < 1000000){
            res = help(number/1000) + " Thousand " + help(number % 1000);
        }else if (number < 1000000000){
            res = help(number / 1000000) + " Million " + help(number % 1000000);
        }else{
            res = help(number /1000000000 ) + " Billion " +help(number%1000000000);
        }
        return res.trim();
    }
}

70,运算

题目:请实现整数数字的乘法、减法和除法运算,运算结果均为整数数字,程序中只允许使用加法运算符和逻辑运算符,允许程序中出现正负常数,不允许使用位运算。

你的实现应该支持如下操作:

  • Operations() 构造函数
  • minus(a, b) 减法,返回a - b
  • multiply(a, b) 乘法,返回a * b
  • divide(a, b) 除法,返回a / b
class Operations {

    public Operations() {

    }
    
    public int minus(int a, int b) {
        a += (~b+1); // b的相反数是反码+1
        return a;
    }
    
    public int multiply(int a, int b) {
        int d = b;
        int e = a;
        // 将a,b都转为负数,防止越界
        if(d > 0)
            b = (~b + 1);
        if(e > 0)
            a = (~a + 1);
        
        int sum = 0;
        int i = -1;
        while(b < 0){
            //  取b最右边的1,注意这里取出来的数是正,所以c需要取负数
            int c = b & (~b + 1);
            if(c > 0)
                c = ~c + 1;
            // 这个地方的意思是,多大个a可以作为(b最右边的1 * a)
            while(i>c){
                i += i;
                a += a;
            }
            // 我们加上这个a,然后去掉b最右边的那个1
            sum += a;
            b &= b + (~1+1);
        }
        // 最后判断一下,如果两个数相反则取负数
        if((d>0&&e<0)||(d<0&&e>0)){
            return ~sum+1;
        }
        return sum;
    }
    public int divide(int a, int b) {
        int left_num = a;
        int right_num = b;
        // 下面全部转为负数,留着a,b是为了最后判断答案的正负数
        if(a > 0)
            left_num = ~left_num + 1;
        if(b > 0){
            right_num = ~right_num + 1;
        }
        // 初始化result
        int result = 0;
        // 判断条件,每一次循环left_num都要减取(right_num * mul),直到最后无法再减
        // 之所以是小于等于,因为是负数!
        while(left_num <= right_num)
        {
            int mul=1;//乘数因子 重点:我们把除法结果看为一个二进制,不同位上的和,依次算每一位加起来即可!
            // 判断条件,如果right_num * mul * 2超过了left_num,那么可以退出循环,mul是结果的一部分
            while((long)multiply(right_num,mul) + (long)multiply(right_num,mul)>=(left_num)){
                // 这个地方如果mul为2的31次方都无法退出循环,意味着除数是1,被除数是2的32次方,直接返回a
                if(mul == 1073741824){
                    return a;
                }
                // mul不停的迭代
                mul = multiply(mul, 2);
            }
            // 加上一部分结果
            result += mul;
            // left_num需要减掉一部分o.o
            left_num =left_num + (~multiply(right_num, mul)+1);
        }
        // 判断结果的正负
        if((a > 0 && b < 0) || (a < 0 && b > 0))
            return ~result+1;
        return result;
    }
   
}

71,生存人数

题目:给定 N 个人的出生年份和死亡年份,第 i 个人的出生年份为 birth[i],死亡年份为 death[i],实现一个方法以计算生存人数最多的年份。你可以假设所有人都出生于 1900 年至 2000 年(含 1900 和 2000 )之间。如果一个人在某一年的任意时期处于生存状态,那么他应该被纳入那一年的统计中。例如,生于 1908 年、死于 1909 年的人应当被列入 1908 年和 1909 年的计数。如果有多个年份生存人数相同且均为最大值,输出其中最小的年份。

class Solution {
    public int maxAliveYear(int[] birth, int[] death) {
        TreeMap<Integer, Integer> map = new TreeMap();
        for (int i = 0; i < birth.length; i++) {
            for (int j = birth[i]; j <= death[i]; j++) {
                map.put(j, map.getOrDefault(j, 0) + 1);
            }
        }
        int result = 0;
        int temp = 0;
        for (Map.Entry entry : map.entrySet()) {
            int value = (int) entry.getValue();
            if (value>temp){
                temp = value;
                result = (int) entry.getKey();
            }
        }
        return result;
    }
}

72,跳水表

题目:你正在使用一堆木板建造跳水板。有两种类型的木板,其中长度较短的木板长度为shorter,长度较长的木板长度为longer。你必须正好使用k块木板。编写一个方法,生成跳水板所有可能的长度。返回的长度需要从小到大排列。

class Solution {
    public int[] divingBoard(int shorter, int longer, int k) {
        if (k==0){
            return new int[]{};
        }
        TreeSet<Integer> set = new TreeSet<Integer>();
        for (int i = 0; i <= k; i++) {
            set.add(i * shorter + (k - i) * longer);
        }
        int[] result = new int[set.size()];
        int index = 0;
        for (int x : set) {
            result[index++] = x;
        }
        return result;
    }
}

73,平方正方形

题目:给定两个正方形及一个二维平面。请找出将这两个正方形分割成两半的一条直线。假设正方形顶边和底边与 x 轴平行。每个正方形的数据square包含3个数值,正方形的左下顶点坐标[X,Y] = [square[0],square[1]],以及正方形的边长square[2]。所求直线穿过两个正方形会形成4个交点,请返回4个交点形成线段的两端点坐标(两个端点即为4个交点中距离最远的2个点,这2个点所连成的线段一定会穿过另外2个交点)。2个端点坐标[X1,Y1][X2,Y2]的返回格式为{X1,Y1,X2,Y2},要求若X1 != X2,需保证X1 < X2,否则需保证Y1 <= Y2。若同时有多条直线满足要求,则选择斜率最大的一条计算并返回(与Y轴平行的直线视为斜率无穷大)。

class Solution {
    public double[] cutSquares(int[] square1, int[] square2) {
        double x1 = square1[0] + square1[2] / 2.0;
        double y1 = square1[1] + square1[2] / 2.0;

        double x2 = square2[0] + square2[2] / 2.0;
        double y2 = square2[1] + square2[2] / 2.0;

        if (x1 == x2) {
            double temp1 = square1[1] + square1[2];
            double temp2 = square2[1] + square2[2];
            return new double[]{x1, Math.min(square1[1], square2[1]), x1, Math.max(temp2, temp1)};
        }
        double[] result = new double[4];
        double k = (y2 - y1) / (x2 - x1);
        double b = y2 - k * x2;

        if(Math.abs(k) >= 1){ // 直线穿过两个正方形的上下边
            result[1] = Math.min(square1[1], square2[1]);  // 先求出y,选两个正方形中靠下的底边
            result[0] = (result[1]-b)/k;
            result[3] = Math.max(square1[1]+square1[2], square2[1]+square2[2]);
            result[2] = (result[3]-b)/k;
        }else{
            result[0] = Math.min(square1[0], square2[0]);
            result[1] = k*result[0]+b;
            result[2] = Math.max(square1[0]+square1[2], square2[0]+square2[2]);
            result[3] = k*result[2]+b;
        }
        if(result[0] > result[2]){
            swap(result, 0, 2);
            swap(result, 1, 3);
        }
        return result;
    }
    public void swap(double[] res, int x, int y){
        double temp = res[x];
        res[x] = res[y];
        res[y] = temp;
    }
}

74,最佳直线

题目:给定一个二维平面及平面上的 N 个点列表Points,其中第i个点的坐标为Points[i]=[Xi,Yi]。请找出一条直线,其通过的点的数目最多。

设穿过最多点的直线所穿过的全部点编号从小到大排序的列表为S,你仅需返回[S[0],S[1]]作为答案,若有多条直线穿过了相同数量的点,则选择S[0]值较小的直线返回,S[0]相同则选择S[1]值较小的直线返回。

public class Solution{
    public int[] bestLine(int[][] points) {
        int[] arr = new int[2];
        int x1, x2, y1, y2;
        int a, b, c;
        int preCount = 0;
        for (int i = 0; i < points.length - 1; i++) {
            for (int j = i + 1; j < points.length; j++) {
                int count = 0;
                x1 = points[i][0];
                y1 = points[i][1];
                x2 = points[j][0];
                y2 = points[j][1];
                a = y2 - y1;
                b = x1 - x2;
                c = y1 * (x2 - x1) - x1 * (y2 - y1);
                for (int[] point : points) {
                    if (atLine(a, b, c, point[0], point[1])) {
                        count++;
                    }
                }
                if (count > preCount) {
                    preCount = count;
                    arr[0] = i;
                    arr[1] = j;
                } else if (count == preCount) {
                    if (i == arr[0] && j < arr[1]) {
                        arr[1] = j;
                    }
                }
            }
        }
        return arr;
    }

    public boolean atLine(int a, int b, int c, int x, int y) {
        return a * x + b * y + c == 0;
    }
}

75,珠玑妙算

题目:珠玑妙算游戏(the game of master mind)的玩法如下。计算机有4个槽,每个槽放一个球,颜色可能是红色(R)、黄色(Y)、绿色(G)或蓝色(B)。例如,计算机可能有RGGB 4种(槽1为红色,槽2、3为绿色,槽4为蓝色)。作为用户,你试图猜出颜色组合。打个比方,你可能会猜YRGB。要是猜对某个槽的颜色,则算一次“猜中”;要是只猜对颜色但槽位猜错了,则算一次“伪猜中”。注意,“猜中”不能算入“伪猜中”。给定一种颜色组合solution和一个猜测guess,编写一个方法,返回猜中和伪猜中的次数answer,其中answer[0]为猜中的次数,answer[1]为伪猜中的次数。

class Solution {
    public int[] masterMind(String solution, String guess) {
        int a = 0;
        int b = 0;
        char[] s1 = solution.toCharArray();
        char[] s2 = guess.toCharArray();

        for (int i = 0; i < s1.length; i++) {
            if (s1[i] == s2[i]) {
                a++;
                s1[i] = '0';
                s2[i] = '1';
            }
        }
        for (int i = 0; i < s1.length; i++) {
            for (int j = 0; j < s2.length; j++) {
                if (s1[i] == s2[j]) {
                    b++;
                    s1[i] = '0';
                    s2[j] = '1';
                }
            }
        }
        return new int[]{a, b};
    }
}

76,部分排序

题目:给定一个整数数组,编写一个函数,找出索引mn,只要将索引区间[m,n]的元素排好序,整个数组就是有序的。注意:n-m尽量最小,也就是说,找出符合条件的最短序列。函数返回值为[m,n],若不存在这样的mn(例如整个数组是有序的),请返回[-1,-1]

class Solution {
    public int[] subSort(int[] array) {
        int N = array.length, start = -1, end = -1;
        int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;
        
        // 从前往后找目标末位,使得从该位到最后,数组保持递增
        for (int i = 0; i < N; i++) {
            if (array[i] >= max) max = array[i];
            else end = i;
        }
        
        // 数组恒递增,说明数组是有序的,直接返回
        if (end == -1) return new int[] {-1, -1};
        
        // 从后往前找目标首位,使得从该位到最前,数组保持递减
        for (int i = end; i >= 0; i--) {
            if (array[i] <= min) min = array[i];
            else start = i;
        }
        return new int[] {start, end};
    }
}

77,连续数列

题目:给定一个整数数组,找出总和最大的连续数列,并返回总和。

class Solution {
    public int maxSubArray(int[] nums) {
        int res = nums[0];
        int sum = 0;
        for (int num : nums) {
            if (sum > 0)
                sum += num;
            else
                sum = num;
            res = Math.max(res, sum);
        }
        return res;
    }
}

78,模式匹配

题目:你有两个字符串,即patternvalue。 pattern字符串由字母"a""b"组成,用于描述字符串中的模式。例如,字符串"catcatgocatgo"匹配模式"aabab"(其中"cat""a""go""b"),该字符串也匹配像"a""ab""b"这样的模式。但需注意"a""b"不能同时表示相同的字符串。编写一个方法判断value字符串是否匹配pattern字符串。

class Solution {
    public boolean patternMatching(String pattern, String value) {
        int count_a = 0, count_b = 0;
        for (char ch: pattern.toCharArray()) {
            if (ch == 'a') {
                ++count_a;
            } else {
                ++count_b;
            }
        }
        if (count_a < count_b) {
            int temp = count_a;
            count_a = count_b;
            count_b = temp;
            char[] array = pattern.toCharArray();
            for (int i = 0; i < array.length; i++) {
                array[i] = array[i] == 'a' ? 'b' : 'a';
            }
            pattern = new String(array);
        }
        if (value.length() == 0) {
            return count_b == 0;
        }
        if (pattern.length() == 0) {
            return false;
        }
        for (int len_a = 0; count_a * len_a <= value.length(); ++len_a) {
            int rest = value.length() - count_a * len_a;
            if ((count_b == 0 && rest == 0) || (count_b != 0 && rest % count_b == 0)) {
                int len_b = (count_b == 0 ? 0 : rest / count_b);
                int pos = 0;
                boolean correct = true;
                String value_a = "", value_b = "";
                for (char ch: pattern.toCharArray()) {
                    if (ch == 'a') {
                        String sub = value.substring(pos, pos + len_a);
                        if (value_a.length() == 0) {
                            value_a = sub;
                        } else if (!value_a.equals(sub)) {
                            correct = false;
                            break;
                        }
                        pos += len_a;
                    } else {
                        String sub = value.substring(pos, pos + len_b);
                        if (value_b.length() == 0) {
                            value_b = sub;
                        } else if (!value_b.equals(sub)) {
                            correct = false;
                            break;
                        }
                        pos += len_b;
                    }
                }
                if (correct && !value_a.equals(value_b)) {
                    return true;
                }
            }
        }
        return false;
    }
}

79,水域大小

题目:你有一个用于表示一片土地的整数矩阵land,该矩阵中每个点的值代表对应地点的海拔高度。若值为0则表示水域。由垂直、水平或对角连接的水域为池塘。池塘的大小是指相连接的水域的个数。编写一个方法来计算矩阵中所有池塘的大小,返回值需要从小到大排序。

public class Solution{
    int[] dx = {1, -1, 0, 0, 1, 1, -1, -1};
    int[] dy = {0, 0, 1, -1, 1, -1, 1, -1};

    public int[] pondSizes(int[][] land) {
        ArrayList<Integer> result = new ArrayList();
        ArrayList arrayList = new ArrayList();
        Queue<int[]> queue = new LinkedList();
        int heigh = land.length;
        int width = land[0].length;
        int max = 0;
        for (int i = 0; i < heigh; i++) {
            queue.clear();
            for (int j = 0; j < width; j++) {
                if (land[i][j] == 0) {
                    max = 0;
                    if (!arrayList.contains(new int[]{i, j})) {
                        max++;
                        queue.add(new int[]{i, j});
                        arrayList.add(new int[]{i, j});
                        land[i][j] = 1;
                        while (!queue.isEmpty()) {
                            int middle[] = queue.poll();
                            for (int k = 0; k < 8; k++) {
                                int newX = middle[0] + dx[k];
                                int newY = middle[1] + dy[k];
                                int[] newPoint = new int[]{newX, newY};
                                if (newX >= 0 && newX < heigh && newY >= 0 && newY < width && land[newX][newY] == 0) {
                                    arrayList.add(newPoint);
                                    queue.add(newPoint);
                                    land[newX][newY] = 1;
                                    max++;
                                }
                            }
                        }
                        result.add(max);
                    }
                }
            }
        }
        int[] re = new int[result.size()];
        for (int i = 0; i < re.length; i++) {
            re[i] = result.get(i);
        }
        Arrays.sort(re);
        return re;
    }
}

80,T9键盘

题目:在老式手机上,用户通过数字键盘输入,手机将提供与这些数字相匹配的单词列表。每个数字映射到0至4个字母。给定一个数字序列,实现一个算法来返回匹配单词的列表。你会得到一张含有有效单词的列表。映射如下图所示:

class Solution {
    String[] c = new String[8];

    public List<String> getValidT9Words(String num, String[] words) {
        c[0] = "abc";
        c[1] = "def";
        c[2] = "ghi";
        c[3] = "jkl";
        c[4] = "mno";
        c[5] = "pqrs";
        c[6] = "tuv";
        c[7] = "wxyz";
        ArrayList result = new ArrayList();
        boolean flag = true;
        for (int i = 0; i < words.length; i++) {
            String temp = words[i];
            flag = true;
            for (int j = 0; j < temp.length(); j++) {
                int index = num.charAt(j) - '0';
                if (!c[index - 2].contains(String.valueOf(temp.charAt(j)))) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
                result.add(temp);
            }
        }
        return result;
    }
}

81,交换和

题目:给定两个整数数组,请交换一对数值(每个数组中取一个数值),使得两个数组所有元素的和相等。返回一个数组,第一个元素是第一个数组中要交换的元素,第二个元素是第二个数组中要交换的元素。若有多个答案,返回任意一个均可。若无满足条件的数值,返回空数组。

class Solution {
   public int[] findSwapValues(int[] array1, int[] array2) {
        int sum1 = 0, sum2 = 0;
        for (int num : array1) {
            sum1 += num;
        }
        for (int num : array2) {
            sum2 += num;
        }
        int diff = sum1 - sum2;
        if (diff % 2 != 0) { // 如果两个数组的总和之差为奇数,则不可能找到符合条件的数值
            return new int[0];
        }
        diff /= 2; // 将差值除以2,这样就可以在第二个数组中找到一个数值,使得交换后两个数组的总和相等
        Set<Integer> set = new HashSet<>();
        for (int num : array2) {
            set.add(num);
        }
        for (int num : array1) {
            if (set.contains(num - diff)) {
                return new int[]{num, num - diff};
            }
        }
        return new int[0];
    }
}

82,兰顿蚂蚁

题目:一只蚂蚁坐在由白色和黑色方格构成的无限网格上。开始时,网格全白,蚂蚁面向右侧。每行走一步,蚂蚁执行以下操作。

  • 如果在白色方格上,则翻转方格的颜色,向右(顺时针)转 90 度,并向前移动一个单位。
  • 如果在黑色方格上,则翻转方格的颜色,向左(逆时针方向)转 90 度,并向前移动一个单位。

编写程序来模拟蚂蚁执行的前 K 个动作,并返回最终的网格。网格由数组表示,每个元素是一个字符串,代表网格中的一行,黑色方格由 'X' 表示,白色方格由 '_' 表示,蚂蚁所在的位置由 'L''U''R''D' 表示,分别表示蚂蚁 左、上、右、下 的朝向。只需要返回能够包含蚂蚁走过的所有方格的最小矩形。

class Solution {
    private class Position {
        
        // 横坐标 x 纵坐标 y
        int x, y;
        
        public Position(int x, int y) {
            this.x = x;
            this.y = y;
        }

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + getEnclosingInstance().hashCode();
			result = prime * result + x;
			result = prime * result + y;
			return result;
		}

		// 改写哈希算法,使两个 Position 对象可以比较坐标而不是内存地址
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Position other = (Position) obj;
			if (!getEnclosingInstance().equals(other.getEnclosingInstance()))
				return false;
			if (x != other.x)
				return false;
			if (y != other.y)
				return false;
			return true;
		}

		private Solution getEnclosingInstance() {
			return Solution.this;
		}

    }
    
    public List<String> printKMoves(int K) {
        char[] direction = {'L', 'U', 'R', 'D'};
        // 用“向量”记录方向,顺序与上一行方向的字符顺序保持一致,每个元素的后一个元素都是可以90°向右变换得到的
        int[][] offset = {{0, -1}, {-1, 0}, {0, 1}, {1, 0}};
        // 蚂蚁的位置
        Position antPos = new Position(0, 0);
        // 蚂蚁方向的向量序号,一开始蚂蚁面向右侧
        int antDir = 2;
        // 用集合存储所有黑块的坐标,一开始想再定义一个路径的坐标集合,发现可以直接用黑块+蚂蚁位置也能过
        Set<Position> blackSet = new HashSet<>();
        while (K > 0) {
            // 新的坐标对象用于放入集合
            Position t = new Position(antPos.x, antPos.y);
            // 如果黑块集合能存入,说明脚下的块不在集合中,也就意味着是白色,方向序号循环自增1,向右(顺时针)转 90 度
            if (blackSet.add(t)) {
            	antDir = (antDir + 1) % 4;
            }else {
                // 否则说明脚下的块已经在集合中,也就意味着是黑色,方向序号循环自增3,相当于自减1,向左(逆时针方向)转 90 度
                antDir = (antDir + 3) % 4;
                // 别忘了删除,即将黑块变白
                blackSet.remove(t);
            }
            // 蚂蚁向前移动一个位置
            antPos.x += offset[antDir][0];
            antPos.y += offset[antDir][1];
            K--;
        }
        // 计算边界,即输出网格的行数和列数
        int left = antPos.y, top = antPos.x, right = antPos.y, bottom = antPos.x;
        for (Position pos : blackSet) {
            left = pos.y < left ? pos.y : left;//y取小
            top = pos.x < top ? pos.x : top;//x取小
            right = pos.y > right ? pos.y : right;//y取大
            bottom = pos.x > bottom ? pos.x : bottom;//x取大
        }
        
        char[][] grid = new char[bottom - top + 1][right - left + 1];
        // 填充白块
        for (char[] row : grid)
            Arrays.fill(row, '_');
        // 替换黑块
        for (Position pos : blackSet) {
             grid[pos.x - top][pos.y - left] = 'X';  
        }
           
        // 替换蚂蚁最后的位置
        grid[antPos.x - top][antPos.y - left] = direction[antDir];
        // 利用网格生成字符串列表
        List<String> result = new ArrayList<>();
        for (char[] row : grid)
            result.add(String.valueOf(row));
        return result;
    }
}

83,数对和

题目:设计一个算法,找出数组中两数之和为指定值的所有整数对。一个数只能属于一个数对。

class Solution {
    public List<List<Integer>> pairSums(int[] nums, int target) {
        Arrays.sort(nums);
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        List<List<Integer>> result = new ArrayList<>();
        for (int i : nums) {
            hashMap.put(i, hashMap.getOrDefault(i, 0)+1);
        }
        for (int i : nums) {
            if (hashMap.containsKey(i) && hashMap.get(i) > 0 && hashMap.containsKey(target - i) && hashMap.get(target - i) > 0) {
                if (target == 2 * i && hashMap.get(i) < 2) {
                    break;
                }
                result.add(Arrays.asList(i, target - i));
                if (hashMap.get(i) == 1) {
                    hashMap.remove(i);
                } else {
                    hashMap.put(i, hashMap.get(i) - 1);
                }
                if (hashMap.get(target - i) == 1) {
                    hashMap.remove(target - i);
                } else {
                    hashMap.put(target - i, hashMap.get(target - i) - 1);
                }
            }
        }
        return result;
    }
}

84,LRU缓存

题目:设计和构建一个“最近最少使用”缓存,该缓存会删除最近最少使用的项目。缓存应该从键映射到值(允许你插入和检索特定键对应的值),并在初始化时指定最大容量。当缓存被填满时,它应该删除最近最少使用的项目。它应该支持以下操作:

  • 获取数据 get 和 写入数据 put 。
  • 获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
  • 写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。
class LRUCache {
    HashMap<Integer, Integer> hashMap;
    int capacity = 0;
    HashMap<Integer, Integer> times;
    int index = 1;
 
    public LRUCache(int capacity) {
        hashMap = new HashMap<>();
        times = new HashMap<>();
        this.capacity = capacity;
    }
 
    public int get(int key) {
        if (hashMap.containsKey(key)) {
            times.put(key, index++);
            return hashMap.get(key);
        } else {
            return -1;
        }
    }
 
    public void put(int key, int value) {
        if (hashMap.containsKey(key)) {
            hashMap.put(key, value);
        } else {
            if (hashMap.size() == capacity) {
                int id = 0;
                int min = Integer.MAX_VALUE;
                Set<Integer> keys = times.keySet();
                Iterator<Integer> iterator = keys.iterator();
                while (iterator.hasNext()) {
                    int t = iterator.next();
                    if (times.get(t) < min) {
                        id = t;
                        min = times.get(t);
                    }
                }
                hashMap.remove(id);
                times.remove(id);
            }
            hashMap.put(key, value);
        }
        times.put(key, index++);
    }
}

85,计算器

题目:给定一个包含正整数、加(+)、减(-)、乘(*)、除(/)的算数表达式(括号除外),计算其结果。表达式仅包含非负整数,+, - ,*/ 四种运算符和空格  。 整数除法仅保留整数部分。

class Solution {
    public int calculate(String s) {
        Deque<Integer> stack = new ArrayDeque<Integer>();
        char preSign = '+';
        int num = 0;
        int n = s.length();
        for (int i = 0; i < n; ++i) {
            if (Character.isDigit(s.charAt(i))) {
                num = num * 10 + s.charAt(i) - '0';
            }
            if (!Character.isDigit(s.charAt(i)) && s.charAt(i) != ' ' || i == n - 1) {
                switch (preSign) {
                case '+':
                    stack.push(num);
                    break;
                case '-':
                    stack.push(-num);
                    break;
                case '*':
                    stack.push(stack.pop() * num);
                    break;
                default:
                    stack.push(stack.pop() / num);
                }
                preSign = s.charAt(i);
                num = 0;
            }
        }
        int ans = 0;
        while (!stack.isEmpty()) {
            ans += stack.pop();
        }
        return ans;
    }
}

86,不用加号的加法

题目:设计一个函数把两个数字相加。不得使用 + 或者其他算术运算符。

class Solution {
    public int add(int a, int b) {
        while (b != 0) {
            int carry = (a & b) << 1;
            a = a ^ b;
            b = carry;
        }
        return a;
    }
}

87,消失的数字

题目:数组nums包含从0n的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗?

class Solution {
    public int missingNumber(int[] nums) {
        int[] temp = new int[nums.length + 1];
        for (int i = 0; i < nums.length; i++) {
            temp[nums[i]] = 1;
        }
        for (int i = 0; i < nums.length; i++) {
            if (temp[i] == 0) {
                return i;
            }
        }
        return nums.length;
    }
}

88,字母与数字

题目:给定一个放有字母和数字的数组,找到最长的子数组,且包含的字母和数字的个数相同。返回该子数组,若存在多个最长子数组,返回左端点下标值最小的子数组。若不存在这样的数组,返回一个空数组。

class Solution {
    public String[] findLongestSubarray(String[] array) {
        Map<Integer, Integer> indices = new HashMap<Integer, Integer>();
        indices.put(0, -1);
        int sum = 0;
        int maxLength = 0;
        int startIndex = -1;
        int n = array.length;
        for (int i = 0; i < n; i++) {
            if (Character.isLetter(array[i].charAt(0))) {
                sum++;
            } else {
                sum--;
            }
            if (indices.containsKey(sum)) {
                int firstIndex = indices.get(sum);
                if (i - firstIndex > maxLength) {
                    maxLength = i - firstIndex;
                    startIndex = firstIndex + 1;
                }
            } else {
                indices.put(sum, i);
            }
        }
        if (maxLength == 0) {
            return new String[0];
        }
        String[] ans = new String[maxLength];
        System.arraycopy(array, startIndex, ans, 0, maxLength);
        return ans;
    }
}

89,2出现的次数

题目:编写一个方法,计算从 0 到 n (含 n) 中数字 2 出现的次数。

class Solution {
    public int numberOf2sInRange(int n) {
         int count = 0;
    int weight = 1;
    
    while (n >= weight) {
        int high = n / (weight * 10);
        int current = (n / weight) % 10;
        int low = n % weight;

        if (current < 2) {
            count += high * weight;
        } else if (current == 2) {
            count += high * weight + low + 1;
        } else {
            count += (high + 1) * weight;
        }

        weight *= 10;
    }
    
    return count;
    }
}

90,婴儿的名字

题目:每年,政府都会公布一万个最常见的婴儿名字和它们出现的频率,也就是同名婴儿的数量。有些名字有多种拼法,例如,John 和 Jon 本质上是相同的名字,但被当成了两个名字公布出来。给定两个列表,一个是名字及对应的频率,另一个是本质相同的名字对。设计一个算法打印出每个真实名字的实际频率。注意,如果 John 和 Jon 是相同的,并且 Jon 和 Johnny 相同,则 John 与 Johnny 也相同,即它们有传递和对称性。

在结果列表中,选择 字典序最小 的名字作为真实名字。

输入:names = ["John(15)","Jon(12)","Chris(13)","Kris(4)","Christopher(19)"], synonyms = ["(Jon,John)","(John,Johnny)","(Chris,Kris)","(Chris,Christopher)"]
输出:["John(27)","Chris(36)"]
class Solution {
    public String[] trulyMostPopular(String[] names, String[] synonyms) {
        UnionFind uf = new UnionFind();
        for(String str : names) {
            int index1 = str.indexOf('('), index2 = str.indexOf(')');
            String name = str.substring(0, index1);
            int count = Integer.valueOf(str.substring(index1 + 1, index2));
            //并查集初始化
            uf.parent.put(name, name);
            uf.size.put(name, count);

        }
        for(String synonym : synonyms) {
            int index = synonym.indexOf(',');
            String name1 = synonym.substring(1, index);
            String name2 = synonym.substring(index + 1, synonym.length() - 1);
            //避免漏网之鱼
            if(!uf.parent.containsKey(name1)) {
                uf.parent.put(name1, name1);
                //注意人数为0
                uf.size.put(name1, 0);
            }
            if(!uf.parent.containsKey(name2)) {
                uf.parent.put(name2, name2);
                uf.size.put(name2, 0);
            }
            uf.union(name1, name2);
        } 
        List<String> res = new ArrayList<>();
        for(String str : names) {
            int index1 = str.indexOf('('), index2 = str.indexOf(')');
            String name = str.substring(0, index1);
            //根节点
            if(name.equals(uf.find(name))) 
                res.add(name + "(" + uf.size.get(name) + ")");    
        }
         return res.toArray(new String[res.size()]);
    }
}

//并查集
//路径压缩
public class UnionFind{
    //当前节点的父亲节点
    Map<String, String> parent;
    //当前节点人数
    Map<String, Integer> size;

    public UnionFind() {
        this.parent = new HashMap<>();
        this.size = new HashMap<>();
    }

    //找到x的根节点
    public String find(String x) {
        if(parent.get(x).equals(x))
            return x;
        //路径压缩
        parent.put(x, find(parent.get(x)));
        return parent.get(x);
    }

    public void union(String x, String y) {
        String str1 = find(x), str2 = find(y);
        if(str1.equals(str2))
            return;
        //字典序小的作为根
        if(str1.compareTo(str2) > 0) {
            parent.put(str1, str2);
            //人数累加到根节点
            size.put(str2, size.get(str1) + size.get(str2));
        }else {
            parent.put(str2, str1);
            size.put(str1, size.get(str2) + size.get(str1));
        }
    }
}

91,马戏团人塔

题目:有个马戏团正在设计叠罗汉的表演节目,一个人要站在另一人的肩膀上。出于实际和美观的考虑,在上面的人要比下面的人矮一点且轻一点。已知马戏团每个人的身高和体重,请编写代码计算叠罗汉最多能叠几个人。

思路:升序排序身高, 若身高相同,体重按降序排序。

class Solution {
    public int bestSeqAtIndex(int[] height, int[] weight) {
        int len = height.length;
        int[][] person = new int[len][2];
        for (int i = 0; i < len; ++i)
            person[i] = new int[]{height[i], weight[i]};

        Arrays.sort(person, (a, b) -> a[0] == b[0] ? b[1] - a[1] : a[0] - b[0]);
        int[] dp = new int[len];
        int res = 0;
        for (int[] pair : person) {
            int i = Arrays.binarySearch(dp, 0, res, pair[1]);
            if (i < 0)
                i = -(i + 1);
            dp[i] = pair[1];
            if (i == res)
                ++res;
        }
        return res;
    }
}

92,第k个数

题目:有些数的素因子只有 3,5,7,请设计一个算法找出第 k 个数。注意,不是必须有这些素因子,而是必须不包含其他的素因子。例如,前几个数按顺序应该是 1,3,5,7,9,15,21。

class Solution {
    public int getKthMagicNumber(int k) {
        int[] dp = new int[k + 1];
        dp[1] = 1;
        int p3 = 1, p5 = 1, p7 = 1;
        for (int i = 2; i <= k; i++) {
            int num3 = dp[p3] * 3, num5 = dp[p5] * 5, num7 = dp[p7] * 7;
            dp[i] = Math.min(Math.min(num3, num5), num7);
            if (dp[i] == num3) {
                p3++;
            }
            if (dp[i] == num5) {
                p5++;
            }
            if (dp[i] == num7) {
                p7++;
            }
        }
        return dp[k];
    }
}

93,主要元素

题目:数组中占比超过一半的元素称之为主要元素。给你一个 整数 数组,找出其中的主要元素。若没有,返回 -1 。请设计时间复杂度为 O(N) 、空间复杂度为 O(1) 的解决方案。

class Solution {
    public int majorityElement(int[] nums) {
        HashMap<Integer, Integer> hashMap = new HashMap();
        for (int i = 0; i < nums.length; i++) {
            if (hashMap.containsKey(nums[i])) {
                hashMap.put(nums[i], hashMap.get(nums[i]) + 1);
            } else {
                hashMap.put(nums[i], 1);
            }
        }
        int k = nums.length / 2;
        Set<Integer> keys = hashMap.keySet();
        Iterator<Integer> iterator = keys.iterator();
        while (iterator.hasNext()) {
            int a = iterator.next();
            if (hashMap.get(a) > k) {
                return a;
            }
        }
        return -1;
    }
}

94,单词距离

题目:有个内含单词的超大文本文件,给定任意两个不同的单词,找出在这个文件中这两个单词的最短距离(相隔单词数)。如果寻找过程在这个文件中会重复多次,而每次寻找的单词不同,你能对此优化吗?

class Solution {
    public int findClosest(String[] words, String word1, String word2) {
        int index1 = Integer.MAX_VALUE, index2 = Integer.MAX_VALUE;
        int result = Integer.MAX_VALUE;
        for (int i = 0; i < words.length; i++) {
            if (words[i].equals(word1)) {
                index1 = i;
                result = Math.min(result, Math.abs(index1 - index2));
            } else if (words[i].equals(word2)) {
                index2 = i;
                result = Math.min(result, Math.abs(index2 - index1));
            }
        }
        return result;
    }
}

95,BiNode

题目:二叉树数据结构TreeNode可用来表示单向链表(其中left置空,right为下一个链表节点)。实现一个方法,把二叉搜索树转换为单向链表,要求依然符合二叉搜索树的性质,转换操作应是原址的,也就是在原始的二叉搜索树上直接修改。返回转换后的单向链表的头节点。

class Solution {
    TreeNode pre, head;

    public TreeNode convertBiNode(TreeNode root) {
        if (root == null) {
            return null;
        }
        dfs(root);
        return head;
    }

    private void dfs(TreeNode cur) {
        // 递归结束条件
        if (cur == null) return;
        dfs(cur.left);
        // 如果pre为空,就说明是第一个节点,头结点,然后用head保存头结点,用于之后的返回
        if (pre == null) head = cur;
            // 如果不为空,那就说明是中间的节点。并且pre保存的是上一个节点,
            // 让上一个节点的右指针指向当前节点
        else if (pre != null) pre.right = cur;
        // 再让当前节点的左指针指向父节点,也就连成了双向链表
        cur.left = null;
        // 保存当前节点,用于下层递归创建
        pre = cur;
        dfs(cur.right);
    }
}

96,恢复空格

题目:哦,不!你不小心把一个长篇文章中的空格、标点都删掉了,并且大写也弄成了小写。像句子"I reset the computer. It still didn’t boot!"已经变成了"iresetthecomputeritstilldidntboot"。在处理标点符号和大小写之前,你得先把它断成词语。当然了,你有一本厚厚的词典dictionary,不过,有些词没在词典里。假设文章用sentence表示,设计一个算法,把文章断开,要求未识别的字符最少,返回未识别的字符数。

class Solution {
    public int respace(String[] dictionary, String sentence) {
        int n = sentence.length();

        Trie root = new Trie();
        for (String word: dictionary) {
            root.insert(word);
        }

        int[] dp = new int[n + 1];
        Arrays.fill(dp, Integer.MAX_VALUE);
        dp[0] = 0;
        for (int i = 1; i <= n; ++i) {
            dp[i] = dp[i - 1] + 1;

            Trie curPos = root;
            for (int j = i; j >= 1; --j) {
                int t = sentence.charAt(j - 1) - 'a';
                if (curPos.next[t] == null) {
                    break;
                } else if (curPos.next[t].isEnd) {
                    dp[i] = Math.min(dp[i], dp[j - 1]);
                }
                if (dp[i] == 0) {
                    break;
                }
                curPos = curPos.next[t];
            }
        }
        return dp[n];
    }
}

class Trie {
    public Trie[] next;
    public boolean isEnd;
    
    public Trie() {
        next = new Trie[26];
        isEnd = false;
    }

    public void insert(String s) {
        Trie curPos = this;

        for (int i = s.length() - 1; i >= 0; --i) {
            int t = s.charAt(i) - 'a';
            if (curPos.next[t] == null) {
                curPos.next[t] = new Trie();
            }
            curPos = curPos.next[t];
        }
        curPos.isEnd = true;
    }
}

97,最小K个数

题目:设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。

class Solution {
    public int[] smallestK(int[] arr, int k) {
        int[] vec = new int[k];
        if (k == 0) { // 排除 0 的情况
            return vec;
        }
        PriorityQueue<Integer> queue = new PriorityQueue<Integer>(new Comparator<Integer>() {
            public int compare(Integer num1, Integer num2) {
                return num2 - num1;
            }
        });
        for (int i = 0; i < k; ++i) {
            queue.offer(arr[i]);
        }
        for (int i = k; i < arr.length; ++i) {
            if (queue.peek() > arr[i]) {
                queue.poll();
                queue.offer(arr[i]);
            }
        }
        for (int i = 0; i < k; ++i) {
            vec[i] = queue.poll();
        }
        return vec;
    }
}

98,最长单词

题目:给定一组单词words,编写一个程序,找出其中的最长单词,且该单词由这组单词中的其他单词组合而成。若有多个长度相同的结果,返回其中字典序最小的一项,若没有符合要求的单词则返回空字符串。

class Solution {
    TrieNode root;

public String longestWord(String[] words) {
    this.root = new TrieNode();
    String res = "";
    List<String> wordList = Arrays.asList(words);
    //排序好,第一个返回的即是结果
    wordList.sort((a, b) -> a.length() == b.length() ? a.compareTo(b) : b.length() - a.length());
    //构造字典树
    for (String word : wordList) insert(word);
    for (String word : wordList) {
        TrieNode cur = root;
        int n = word.length();
        for (int i = 0; i < n; i++) {
            char c = word.charAt(i);
            //排除掉自己组成自己,当前遍历到的字符是个单词,且剩余部分可以再次被切分
            if (i < n - 1 && cur.children[c - 'a'].isEnd && canSplitToWord(word.substring(i + 1))) {
                return word;
            }
            cur = cur.children[c - 'a'];
        }
    }
    return res;
}

/**
 * 当前的单词可以被切分,在wordList中找到
 *
 * @param remain
 * @return
 */
private boolean canSplitToWord(String remain) {
    //当没有可以切分的了 返回True
    if (remain.equals("")) return true;
    TrieNode cur = root;
    for (int i = 0; i < remain.length(); i++) {
        char c = remain.charAt(i);//拿到当前的字符
        if (cur.children[c - 'a'] == null) return false;//这个节点找不到
        //当前的节点是个单词,且剩余部分可以再次被切分
        if (cur.children[c - 'a'].isEnd && canSplitToWord(remain.substring(i + 1))) {
            return true;
        }
        cur = cur.children[c - 'a'];
    }
    return false;
}

/**
 * Trie树插入一个单词
 *
 * @param word
 */
private void insert(String word) {
    TrieNode cur = root;
    for (char c : word.toCharArray()) {
        if (cur.children[c - 'a'] == null) {
            cur.children[c - 'a'] = new TrieNode();
        }
        cur = cur.children[c - 'a'];
    }
    cur.isEnd = true;
}

/**
 * 构建字典树的结构
 */
class TrieNode {
    private TrieNode[] children;
    private boolean isEnd;//当前的字符是否是一个单词的结尾

    public TrieNode() {
        this.children = new TrieNode[26];
        this.isEnd = false;
    }
}
}

99,按摩师

题目:一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。

class Solution {
    public int massage(int[] nums) {
        if (nums.length == 0) {
            return 0;
        }
        if (nums.length==1){
            return nums[0];
        }
        int result[] = new int[nums.length];
        result[0] = nums[0];
        result[1] = Math.max(nums[0], nums[1]);
        for (int i = 2; i < nums.length; i++) {
            result[i] = Math.max(result[i - 1], result[i - 2] + nums[i]);
        }
        return result[result.length - 1];
    }
}

100,多次搜索

题目:给定一个较长字符串big和一个包含较短字符串的数组smalls,设计一个方法,根据smalls中的每一个较短字符串,对big进行搜索。输出smalls中的字符串在big里出现的所有位置positions,其中positions[i]smalls[i]出现的所有位置。

class Solution {
    public int[][] multiSearch(String big, String[] smalls) {
        ArrayList<ArrayList<Integer>> arrayList = new ArrayList();
        for (int i = 0; i < smalls.length; i++) {
            ArrayList<Integer> temp = new ArrayList();
            for (int j = 0; j < big.length(); ) {
                String small = smalls[i];
                if (small.equals("")){
                    break;
                }
                int index = big.indexOf(small, j);
                if (index != -1) {
                    temp.add(index);
                    j = index + 1;
                } else {
                    break;
                }
            }
            arrayList.add(temp);
        }
        int[][] array = new int[arrayList.size()][];
        for (int i = 0; i < arrayList.size(); i++) {
            ArrayList<Integer> row = arrayList.get(i);
            array[i] = new int[row.size()];
            for (int j = 0; j < row.size(); j++) {
                array[i][j] = row.get(j);
            }
        }
        return array;
    }
}

101,最短超串

题目:假设你有两个数组,一个长一个短,短的元素均不相同。找到长数组中包含短数组所有的元素的最短子数组,其出现顺序无关紧要。返回最短子数组的左端点和右端点,如有多个满足条件的子数组,返回左端点最小的一个。若不存在,返回空数组。

class Solution {
    Map<Integer, Integer> ori = new HashMap<Integer, Integer>();
    Map<Integer, Integer> cnt = new HashMap<Integer, Integer>();

    public boolean check() {
        Iterator iter = ori.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry entry = (Map.Entry) iter.next();
            Integer key = (Integer) entry.getKey();
            Integer val = (Integer) entry.getValue();
            if (cnt.getOrDefault(key, 0) < val) {
                return false;
            }
        }
        return true;
    }

    public int[] shortestSeq(int[] big, int[] small) {
         int tLen = small.length;
        for (int i = 0; i < tLen; i++) {
            int c = small[i];
            ori.put(c, ori.getOrDefault(c, 0) + 1);
        }
        int l = 0, r = -1;
        int len = Integer.MAX_VALUE, ansL = -1, ansR = -1;
        int sLen = big.length;
        while (r < sLen) {
            ++r;
            if (r < sLen && ori.containsKey(big[r])) {
                cnt.put(big[r], cnt.getOrDefault(big[r], 0) + 1);
            }
            while (check() && l <= r) {
                if (r - l + 1 < len) {
                    len = r - l + 1;
                    ansL = l;
                    ansR = l + len;
                }
                if (ori.containsKey(big[l])) {
                    cnt.put(big[l], cnt.getOrDefault(big[l], 0) - 1);
                }
                ++l;
            }
        }
        return ansL == -1 ? new int[]{} : new int[]{ansL, ansR-1};
    }
}

102,消失的两个数字

题目:给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字。你能在 O(N) 时间内只用 O(1) 的空间找到它们吗?

class Solution {
    public int[] missingTwo(int[] nums) {
        int n = nums.length + 2;
        long sum = 0;
        for(int i : nums)sum += i;

        // 等差数列求和na1 + n(n - 1) / 2
        long sumTwo = n * (n + 1) / 2 - sum;
        // 一个数比avg小一个比avg大
        long avg = sumTwo / 2;

        sum = 0;
        for(int i : nums){
            if(i <= avg)sum += i;
        }  
        long one = avg * (avg + 1) / 2 - sum;
        return new int[]{(int)one, (int)(sumTwo - one)};
    }
}

103,连续中值

题目:随机产生数字并传递给一个方法。你能否完成这个方法,在每次产生新值时,寻找当前所有值的中间值(中位数)并保存。中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。

例如,

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

  • void addNum(int num) - 从数据流中添加一个整数到数据结构中。
  • double findMedian() - 返回目前所有元素的中位数。
class MedianFinder {
    PriorityQueue<Integer> queMin;
    PriorityQueue<Integer> queMax;

    public MedianFinder() {
        queMin = new PriorityQueue<Integer>((a, b) -> (b - a));
        queMax = new PriorityQueue<Integer>((a, b) -> (a - b));
    }
    
    public void addNum(int num) {
        if (queMin.isEmpty() || num <= queMin.peek()) {
            queMin.offer(num);
            if (queMax.size() + 1 < queMin.size()) {
                queMax.offer(queMin.poll());
            }
        } else {
            queMax.offer(num);
            if (queMax.size() > queMin.size()) {
                queMin.offer(queMax.poll());
            }
        }
    }
    
    public double findMedian() {
        if (queMin.size() > queMax.size()) {
            return queMin.peek();
        }
        return (queMin.peek() + queMax.peek()) / 2.0;
    }
}

104,直方图的水量

题目:给定一个直方图(也称柱状图),假设有人从上面源源不断地倒水,最后直方图能存多少水量?直方图的宽度为 1。

class Solution {
    public int trap(int[] height) {
        int ans = 0;
        int left = 0, right = height.length - 1;
        int leftMax = 0, rightMax = 0;
        while (left < right) {
            leftMax = Math.max(leftMax, height[left]);
            rightMax = Math.max(rightMax, height[right]);
            if (height[left] < height[right]) {
                ans += leftMax - height[left];
                ++left;
            } else {
                ans += rightMax - height[right];
                --right;
            }
        }
        return ans;
    }
}

105,单词替换

题目:给定字典中的两个词,长度相等。写一个方法,把一个词转换成另一个词, 但是一次只能改变一个字符。每一步得到的新词都必须能在字典中找到。

编写一个程序,返回一个可能的转换序列。如有多个可能的转换序列,你可以返回任何一个。

class Solution {

    public List<String> findLadders(String beginWord, String endWord, List<String> wordList) {
        if (!wordList.contains(endWord)) {
            return new ArrayList<>();
        }
        return dfs(beginWord, endWord, wordList, new HashSet<>());
    }

    private LinkedList<String> dfs(String beginWord, String endWord, List<String> wordList, Set<String> visited) {
        if (beginWord.equals(endWord)) {
            LinkedList<String> collector = new LinkedList<>();
            collector.addFirst(beginWord);
            return collector;
        }
        visited.add(beginWord);
        for (String s : wordList) {
            if (visited.contains(s)) continue;
            if (check(beginWord, s)) {
                LinkedList<String> sub = dfs(s, endWord, wordList, visited);
                if (!sub.isEmpty()) {
                    sub.addFirst(beginWord);
                    return sub;
                }
            }
        }
        return new LinkedList<>();
    }

    private boolean check(String w1, String w2) {
        if (w1.length() != w2.length()) return false;
        int n = w1.length();
        int diff = 0;
        for (int i = 0; i < n; i++) {
            if (w1.charAt(i) != w2.charAt(i) && ++diff > 1) {
                return false;
            }
        }
        return true;
    }
}

106,最大黑方阵

题目:给定一个方阵,其中每个单元(像素)非黑即白。设计一个算法,找出 4 条边皆为黑色像素的最大子方阵。返回一个数组 [r, c, size] ,其中 rc 分别代表子方阵左上角的行号和列号,size 是子方阵的边长。若有多个满足条件的子方阵,返回 r 最小的,若 r 相同,返回 c 最小的子方阵。若无满足条件的子方阵,返回空数组。

class Solution {
    public int[] findSquare(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        // 遍历长度,左上角的位置
        int limit = Math.min(m, n);
        for (int l = limit; l >= 1; l--) {
            for (int i = 0; i <= m - l; i++) {
                for (int j = 0; j <= n - l; j++) {
                    if (check(i, j, l, grid))
                        return new int[]{i, j, l};
                }
            }
        }
        return new int[]{};
    }

    public boolean check(int r, int c, int l, int[][] grid) {
        // 查看边界是否全为1
        for (int i = r; i < r + l; i++) {
            if (grid[i][c] == 1) return false;
            if (grid[i][c + l - 1] == 1) return false;
        }
        for (int j = c; j < c + l; j++) {
            if (grid[r][j] == 1) return false;
            if (grid[r + l - 1][j] == 1) return false;
        }
        return true;
    }
}

107,最大子矩阵

题目:给定一个正整数、负整数和 0 组成的 N × M 矩阵,编写代码找出元素总和最大的子矩阵。返回一个数组 [r1, c1, r2, c2],其中 r1c1 分别代表子矩阵左上角的行号和列号,r2c2 分别代表右下角的行号和列号。若有多个满足条件的子矩阵,返回任意一个均可。

class Solution {
    public int[] getMaxMatrix(int[][] matrix) {
    int rows = matrix.length;
    int cols = matrix[0].length;

    int maxSum = Integer.MIN_VALUE;
    int[] result = new int[4];

    for (int i = 0; i < rows; i++) {
        int[] dp = new int[cols];

        for (int j = i; j < rows; j++) {
            for (int k = 0; k < cols; k++) {
                dp[k] += matrix[j][k];
            }

            int[] subArray = maxSubArray(dp);
            int sum = subArray[2];

            if (sum > maxSum) {
                maxSum = sum;
                result[0] = i;
                result[1] = subArray[0];
                result[2] = j;
                result[3] = subArray[1];
            }
        }
    }

    return result;
}

private int[] maxSubArray(int[] nums) {
    int maxSum = Integer.MIN_VALUE;
    int sum = 0;
    int start = 0;
    int end = 0;
    int temp = 0;

    for (int i = 0; i < nums.length; i++) {
        if (sum < 0) {
            sum = nums[i];
            temp = i;
        } else {
            sum += nums[i];
        }

        if (sum > maxSum) {
            maxSum = sum;
            start = temp;
            end = i;
        }
    }

    return new int[]{start, end, maxSum};
}

}

108,单词矩阵

题目:给定一份单词的清单,设计一个算法,创建由字母组成的面积最大的矩形,其中每一行组成一个单词(自左向右),每一列也组成一个单词(自上而下)。不要求这些单词在清单里连续出现,但要求所有行等长,所有列等高。

如果有多个面积最大的矩形,输出任意一个均可。一个单词可以重复使用。

class Solution {
    class Trie{
        boolean isEnd = false;
        Trie[] child = new Trie[26];
    }
    void add(String s){
        Trie cur = root;
        for(char c : s.toCharArray()){
            int n = c - 'a';
            if(cur.child[n] == null) cur.child[n] = new Trie();
            cur = cur.child[n];
        }
        cur.isEnd = true;
    }
    Trie root;
    //用max保存当前最大矩形面积
    int max = -1;
    //用哈希表保存各个长度字符串的集合
    Map<Integer, List<String>> map = new HashMap<>();
    List<String> ans = new ArrayList<>();
    public String[] maxRectangle(String[] words) {
        this.root = new Trie();
        int n = words.length;
        for(String w : words){
            add(w);
            map.computeIfAbsent(w.length(), k -> new ArrayList<>()).add(w);
        }
        //按照字符串长度从大到小排序
        Arrays.sort(words, (a, b) -> b.length() - a.length());
        for(int i = 0; i < n; i++){
            Trie cur = root;
            String w = words[i];
            //用curList保存矩形的每个字符串
            List<String> curList = new ArrayList<>();
            curList.add(w);
            //用list保存当前可能形成矩形的字符串在字典树中的当前结点
            List<Trie> list = new ArrayList<>();
            int len = w.length();
            //如果当前长度的积不大于最大矩形面积,则退出
            //因为从最大到最小排序,其中4 * 3的字符串矩阵和3 * 4的矩阵是相同的,可以进行去重
            if(len * len <= max) break;
            //check检测是否能组成矩形,flag检测是否可能组成更大的矩形
            boolean check = true, flag = true;
            for(int j = 0; j < len; j++){
                int c = w.charAt(j) - 'a';
                if(cur.child[c] != null){
                    //判断当前字符串是否所有结点都为叶结点,即能组成字符串
                    if(!cur.child[c].isEnd) check = false;
                    list.add(cur.child[c]);
                }else{
                    //如果字符串没有对应子结点,则无法组成矩形
                    check = false;
                    flag = false;
                    break;
                }
            }
            //如果当前字符串可以组成矩形,则进入dfs
            if(flag){
                dfs(1, len, curList, list);
            }
            //如果当前字符串所有结点都可以为叶结点,则判断当前矩形是否最大
            if(check && max == -1){
                max = len;
                ans = curList;
            }
        }
        int size = ans.size();
        String[] strs = new String[size];
        for(int i = 0; i < size; i++) strs[i] = ans.get(i);
        // System.out.println(max);
        return strs;
    }
    void dfs(int cur, int len, List<String> curList, List<Trie> list){
        if(cur == len){
            return;
        }
        //各种结点判断同上
        for(String w : map.get(len)){
            boolean check = true, flag = true;
            //其中next保存下一个可能形成矩形的字典树结点集合
            List<Trie> next = new ArrayList<>();
            //nextList保存下一个可能形成矩形的字符串集合
            List<String> nextList = new ArrayList<>();
            for(int i = 0; i < len; i++){
                int c = w.charAt(i) - 'a';
                Trie ct = list.get(i);
                if(ct.child[c] != null){
                    if(!ct.child[c].isEnd) check = false;
                    next.add(ct.child[c]);
                }else{
                    check = false;
                    flag = false;
                    break;
                }
            }
            if(flag){
                // System.out.println(w);
                nextList.addAll(curList);
                nextList.add(w);
                dfs(cur + 1, len, nextList, next);
            }
            if(check && len * (cur + 1) > max){
                max = len * (cur + 1);
                ans = nextList;
            }
        }
    }
}

109,稀疏相似度

题目:两个(具有不同单词的)文档的交集(intersection)中元素的个数除以并集(union)中元素的个数,就是这两个文档的相似度。例如,{1, 5, 3} 和 {1, 7, 2, 3} 的相似度是 0.4,其中,交集的元素有 2 个,并集的元素有 5 个。给定一系列的长篇文档,每个文档元素各不相同,并与一个 ID 相关联。它们的相似度非常“稀疏”,也就是说任选 2 个文档,相似度都很接近 0。请设计一个算法返回每对文档的 ID 及其相似度。只需输出相似度大于 0 的组合。请忽略空文档。为简单起见,可以假定每个文档由一个含有不同整数的数组表示。

class Solution {
    public List<String> computeSimilarities(int[][] docs) {
    List<String> ans = new ArrayList<>();
    Map<Integer, List<Integer>> map = new HashMap<>();
    int[][] help = new int[docs.length][docs.length];
    for (int i = 0; i < docs.length; i++) {
        for (int j = 0; j < docs[i].length; j++) {
            List<Integer> list = map.get(docs[i][j]);
            if (list == null) {
                list = new ArrayList<>();
                map.put(docs[i][j], list);
            } else {
                for (Integer k : list) {
                    help[i][k]++;
                }
            }
            list.add(i);
        }

        for (int k = 0; k < docs.length; k++) {
            if (help[i][k] > 0) {
                ans.add(k + "," + i + ": " + String.format("%.4f", (double) help[i][k] / (docs[i].length + docs[k].length - help[i][k])));
            }
        }
    }
    return ans;
}
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值