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,字符串轮转
题目:字符串轮转。给定两个字符串
s1
和s2
,请编写代码检查s2
是否为s1
旋转而成(比如,waterbottle
是erbottlewat
旋转后的字符串)。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操作。当某个栈为空时,应当删除该栈。当栈中没有元素或不存在该栈时,
pop
,popAt
应返回 -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,栈排序
题目:栈排序。 编写程序,对栈进行排序使最小元素位于栈顶。最多只能使用一个其他的临时栈存放数据,但不得将元素复制到别的数据结构(如数组)中。该栈支持如下操作:
push
、pop
、peek
和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,动物收容所
题目:动物收容所。有家动物收容所只收容狗与猫,且严格遵守“先进先出”的原则。在收养该收容所的动物时,收养人只能收养所有动物中“最老”(由其进入收容所的时间长短而定)的动物,或者可以挑选猫或狗(同时必须收养此类动物中“最老”的)。换言之,收养人不能自由挑选想收养的对象。请创建适用于这个系统的数据结构,实现各种操作方法,比如
enqueue
、dequeueAny
、dequeueDog
和dequeueCat
。允许使用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
与j
(i <= 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]
中a
与b
的值。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,最小差
题目:给定两个整数数组
a
和b
,计算具有最小差绝对值的一对数值(每个数组中取一个值),并返回该对数值的差。思路:首先对数组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,最大数值
题目:编写一个方法,找出两个数字
a
和b
中最大的那一个。不得使用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,部分排序
题目:给定一个整数数组,编写一个函数,找出索引
m
和n
,只要将索引区间[m,n]
的元素排好序,整个数组就是有序的。注意:n-m
尽量最小,也就是说,找出符合条件的最短序列。函数返回值为[m,n]
,若不存在这样的m
和n
(例如整个数组是有序的),请返回[-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,模式匹配
题目:你有两个字符串,即
pattern
和value
。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
包含从0
到n
的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在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]
,其中r
,c
分别代表子方阵左上角的行号和列号,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]
,其中r1
,c1
分别代表子矩阵左上角的行号和列号,r2
,c2
分别代表右下角的行号和列号。若有多个满足条件的子矩阵,返回任意一个均可。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; } }