正文 最近发现自己基础算法方面还有些欠缺。虽然之前也看过数据结构、算法导论、计算机网络等等一些基础书籍,也都理解基础算法和数据结构,但因为没有每道都手写过,所以当真正去实现时还需要去查阅参考资料。由此,下定决心弥补这些不足。基础算法在工作中对业务理解、代码编写并没有非常显著的提升,但是个人觉得这些影响是潜在的,它会潜在的影响你的思考和解决方案。总之,基础夯实终归是没错的。
目前较为推荐的有牛客的剑指 offer、LeetCode 的 Top 100 Liked Questions。个人更推荐 LeetCode,但是牛客有手机 App,如果想在上下班路上刷一刷,可以选择牛客。我也是因此选择先刷牛客,下面是相关的题目和个人通过编译的答案,不定期更新,如答案中有错误之处烦请指出。
牛客 剑指 offer 链表 JZ6 从尾到头打印链表 时间限制: 1 秒 空间限制: 64M 知识点: 链表
描述
输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。
如输入 {1,2,3}
的链表,返回一个数组为 [3,2,1]
0 <= 链表长度 <= 10000
示例 1 输入: {1,2,3}
返回值: [3,2,1]
示例 2 输入: {67,0,24,58} 返回值: [58,24,0,67]
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import java.util.ArrayList;import java.util.Stack;public class Solution { public ArrayList<Integer> printListFromTailToHead (ListNode listNode) { ArrayList<Integer> result = new ArrayList <>(); if (listNode == null ) { return result; } Stack<ListNode> stack = new Stack (); while (listNode != null ) { stack.push(listNode); listNode = listNode.next; } while (!stack.empty()) { result.add(stack.pop().val); } return result; } }
JZ24 反转链表 时间限制: 1 秒 空间限制: 64M 知识点: 链表
描述
给定一个单链表的头结点 pHead,长度为 n,反转该链表后,返回新链表的表头。
数据范围: n≤1000 要求: 空间复杂度 O(1),时间复杂度 O(n)。
如当输入链表 {1,2,3}
时, 经反转后,原链表变为 {3,2,1}
,所以对应的输出为 {3,2,1}
。
示例 1 输入: {1,2,3}
返回值: {3,2,1}
示例 2 输入: {}
返回值: {}
说明: 空链表则输出空
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class Solution { public ListNode ReverseList (ListNode head) { if (head == null || head.next == null ) { return head; } ListNode pre = null ; ListNode next = null ; while (head != null ) { next = head.next; head.next = pre; pre = head; head = next; } return pre; } }
JZ25 合并两个排序的链表 时间限制: 1 秒 空间限制: 64M 知识点: 链表
描述
输入两个递增的链表,单个链表的长度为 n,合并这两个链表并使新链表中的节点仍然是递增排序的。 数据范围: 0≤n≤1000,−1000≤ 节点值 ≤1000 要求: 空间复杂度 O(1),时间复杂度 O(n)
如输入 {1,3,5},{2,4,6}
时,合并后的链表为 {1,2,3,4,5,6}
,所以对应的输出为 {1,2,3,4,5,6}
输入 {-1,2,4},{1,3,4}
时,合并后的链表为 {-1,1,2,3,4,4}
,所以对应的输出为 {-1,1,2,3,4,4}
示例 1 输入: {1,3,5},{2,4,6}
返回值: {1,2,3,4,5,6}
示例 2 输入: {},{}
返回值: {}
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Solution { public ListNode Merge (ListNode list1,ListNode list2) { ListNode h = new ListNode (-1 ); ListNode cur = h; while (list1 != null && list2 != null ){ if (list1.val <= list2.val){ cur.next = list1; list1 = list1.next; }else { cur.next = list2; list2 = list2.next; } cur = cur.next; } if (list1 != null ) cur.next = list1; if (list2 != null ) cur.next = list2; return h.next; } }
JZ52 两个链表的第一个公共结点 时间限制: 1 秒 空间限制: 64M 知识点: 链表
描述
输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
数据范围:n≤1000 要求:空间复杂度 O(1),时间复杂度 O(n)
输入描述: 输入分为是 3 段,第一段是第一个链表的非公共部分,第二段是第二个链表的非公共部分,第三段是第一个链表和第二个链表的公共部分。 后台会将这 3 个参数组装为两个链表,并将这两个链表对应的头节点传入到函数 FindFirstCommonNode 里面,用户得到的输入只有 pHead1 和 pHead2。
示例 1 输入: {1,2,3},{4,5},{6,7}
返回值: {6,7}
说明: 第一个参数 {1,2,3}
代表是第一个链表非公共部分,第二个参数 {4,5}
代表是第二个链表非公共部分,最后的 {6,7}
表示的是 2 个链表的公共部分。这 3 个参数最后在后台会组装成为 2 个两个无环的单链表,且是有公共节点的。示例 2 输入: {1},{2,3},{}
返回值: {}
说明: 2 个链表没有公共节点 ,返回 null,后台打印 {}
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import java.util.*;public class Solution { public ListNode FindFirstCommonNode (ListNode pHead1, ListNode pHead2) { ListNode l1 = pHead1; ListNode l2 = pHead2; while (l1 != l2) { l1 = (l1 == null ) ? pHead2 : l1.next; l2 = (l2 == null ) ? pHead1 : l2.next; } return l1; } }
JZ23 链表中环的入口结点 时间限制: 1 秒 空间限制: 64M 知识点: 链表 哈希 双指针
描述
给一个长度为 n 链表,若其中包含环,请找出该链表的环的入口结点,否则,返回 null。
数据范围: n≤10000,1<=结点值<=10000 要求:空间复杂度 O(1),时间复杂度 O(n)
输入描述: 输入分为 2 段,第一段是入环前的链表部分,第二段是链表环的部分,后台会根据第二段是否为空将这两段组装成一个无环或者有环单链表
返回值描述: 返回链表的环的入口结点即可,我们后台程序会打印这个结点对应的结点值;若没有,则返回对应编程语言的空结点即可。
示例 1 输入: {1,2},{3,4,5}
返回值: “null” 说明: 没有环,返回对应编程语言的空结点,后台程序会打印 “null”示例 2 输入: {},{2}
返回值: 2 说明: 环的部分只有一个结点,所以返回该环形链表入口结点,后台程序打印该结点对应的结点值,即 2
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 import java.util.*;public class Solution { public ListNode EntryNodeOfLoop (ListNode pHead) { Set<ListNode> set = new HashSet <>(); while (pHead != null ) { if (set.contains(pHead)) { return pHead; } set.add(pHead); pHead = pHead.next; } return null ; } } public class Solution { public ListNode EntryNodeOfLoop (ListNode pHead) { ListNode slow = pHead; ListNode fast = pHead; while (fast != null && fast.next != null ) { fast = fast.next.next; slow = slow.next; if (fast == slow) { break ; } } if (fast == null || fast.next == null ) { return null ; } fast = pHead; while (fast != slow) { fast = fast.next; slow = slow.next; } return fast; } }
JZ22 链表中倒数最后 k 个结点 时间限制: 1 秒 空间限制: 256M 知识点: 链表
描述
输入一个长度为 n 的链表,设链表中的元素的值为 ai ,返回该链表中倒数第 k 个节点。 如果该链表长度小于 k,请返回一个长度为 0 的链表。
数据范围:0≤n≤105 ,0≤ai ≤109 ,0≤k≤109
要求:空间复杂度 O(n),时间复杂度 O(n) 进阶:空间复杂度 O(1),时间复杂度 O(n)
示例 1 输入: {1,2,3,4,5},2
返回值: {4,5}
说明:返回倒数第 2 个节点 4,系统会打印后面所有的节点来比较。示例 2 输入: {2},8
返回值: {}
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 import java.util.*;public class Solution { public ListNode FindKthToTail (ListNode pHead, int k) { if (pHead == null || k <= 0 ) { return null ; } ListNode cursor1 = pHead; ListNode cursor2 = pHead; for (int i = 0 ; i < k; i ++) { if (cursor1 == null ) { return null ; } cursor1 = cursor1.next; } while (cursor1 != null ) { cursor1 = cursor1.next; cursor2 = cursor2.next; } return cursor2; } } public class Solution { public ListNode FindKthToTail (ListNode pHead, int k) { if (pHead == null ) { return null ; } int i = 1 ; ListNode cursor = pHead; while ((cursor = cursor.next) != null ) { i ++; } if (i < k) { return null ; } cursor = pHead; for (int j = 0 ; j < i - k; j ++) { cursor = cursor.next; } return cursor; } } public class Solution { public ListNode FindKthToTail (ListNode pHead, int k) { if (pHead == null || k == 0 ){ return null ; } Stack<ListNode> stack = new Stack <>(); while (pHead != null ) { stack.push(pHead); pHead = pHead.next; } if (stack.size() < k){ return null ; } ListNode firstNode = stack.pop(); while (--k > 0 ) { ListNode temp = stack.pop(); temp.next = firstNode; firstNode = temp; } return firstNode; } }
JZ35 复杂链表的复制 时间限制: 1 秒 空间限制: 64M 知识点: 链表
描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针 random 指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)。 下图是一个含有 5 个结点的复杂链表。图中实线箭头表示 next 指针,虚线箭头表示 random 指针。为简单起见,指向 null 的指针没有画出。
示例: 输入: {1,2,3,4,5,3,5,#,2,#}
输出: {1,2,3,4,5,3,5,#,2,#}
解析:我们将链表分为两段,前半部分 {1,2,3,4,5}
为 ListNode,后半部分 {3,5,#,2,#}
是随机指针域表示。 以上示例前半部分可以表示链表为的 ListNode:1->2->3->4->5 后半部分,3,5,#,2,#
分别的表示为 1 的位置指向 3,2 的位置指向 5,3 的位置指向 null,4 的位置指向 2,5 的位置指向 null 如下图:
示例 1 输入: {1,2,3,4,5,3,5,#,2,#}
返回值: {1,2,3,4,5,3,5,#,2,#}
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 import java.util.*;public class Solution { public RandomListNode Clone (RandomListNode pHead) { if (pHead == null ) { return null ; } RandomListNode cur = pHead; Map<RandomListNode, RandomListNode> map = new HashMap <>(); while (cur != null ) { map.put(cur, new RandomListNode (cur.label)); cur = cur.next; } cur = pHead; while (cur != null ) { map.get(cur).next = map.get(cur.next); map.get(cur).random = map.get(cur.random); cur = cur.next; } return map.get(pHead); } } public class Solution { public RandomListNode Clone (RandomListNode pHead) { if (pHead == null ) { return null ; } RandomListNode cur = pHead; while (cur != null ) { RandomListNode clone = new RandomListNode (cur.label); clone.next = cur.next; cur.next = clone; cur = clone.next; } cur = pHead; while (cur != null ) { cur.next.random = cur.random != null ? cur.random.next : null ; cur = cur.next.next; } RandomListNode cloneHead = pHead.next; RandomListNode old = pHead; RandomListNode clone = cloneHead; while (old != null ) { old.next = clone.next; if (old.next != null ) { clone.next = old.next.next; } old = old.next; clone = clone.next; } return cloneHead; } }
JZ76 删除链表中重复的结点 时间限制: 1 秒 空间限制: 64M 知识点: 链表
描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表 1->2->3->3->4->4->5
处理后为 1->2->5
数据范围:链表长度满足 0≤n≤1000 ,链表中的值满足 1≤val≤1000
进阶:空间复杂度 O(n),时间复杂度 O(n)
例如输入 {1,2,3,3,4,4,5}
时,对应的输出为 {1,2,5}
示例 1 输入: {1,2,3,3,4,4,5}
返回值: {1,2,5}
示例 2 输入: {1,1,1,8}
返回值: {8}
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import java.util.*;public class Solution { public ListNode deleteDuplication (ListNode pHead) { if (pHead == null ) { return null ; } ListNode res = new ListNode (0 ); res.next = pHead; ListNode cur = res; while (cur.next != null && cur.next.next != null ){ if (cur.next.val == cur.next.next.val){ int temp = cur.next.val; while (cur.next != null && cur.next.val == temp) cur.next = cur.next.next; } else cur = cur.next; } return res.next; } }
JZ18 删除链表的节点 时间限制: 1 秒 空间限制: 64M 知识点: 链表
描述
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。返回删除后的链表的头节点。
1.此题对比原题有改动 2.题目保证链表中节点的值互不相同 3.该题只会输出返回的链表和结果做对比,所以若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点
数据范围: 0<=链表节点值<=10000 0<=链表长度<=10000
示例 1 输入: {2,5,1,9},5
返回值: {2,1,9}
说明: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 2 -> 1 -> 9示例 2 输入: {2,5,1,9},1
返回值: {2,5,9}
说明: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 2 -> 5 -> 9
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import java.util.*;public class Solution { public ListNode deleteNode (ListNode head, int val) { ListNode res = new ListNode (-1 ); res.next = head; ListNode cur = res; while (cur != null ) { if (cur.next != null && cur.next.val == val) { cur.next = cur.next.next; break ; } cur = cur.next; } return res.next; } }
树 JZ55 二叉树的深度 时间限制: 1 秒 空间限制: 64M 知识点: 树
描述
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度,根节点的深度视为 1 。
数据范围:节点的数量满足 0≤n≤100 ,节点上的值满足 0≤val≤100 进阶:空间复杂度 O(1),时间复杂度 O(n)
示例 1 输入: {1,2,3,4,5,#,6,#,#,7}
返回值: 4示例 2 输入: {}
返回值: 0
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import java.util.*;public class Solution { public int TreeDepth (TreeNode root) { if (root == null ) { return 0 ; } return 1 + Math.max(TreeDepth(root.left), TreeDepth(root.right)); } }
JZ77 按之字形顺序打印二叉树 时间限制: 1 秒 空间限制: 64M 知识点: 栈 树 队列
描述
给定一个二叉树,返回该二叉树的之字形层序遍历,(第一层从左向右,下一层从右向左,一直这样交替)
数据范围:0≤n≤1500,树上每个节点的 val 满足 ∣val∣<=1500 要求:空间复杂度:O(n),时间复杂度:O(n) 例如: 给定的二叉树是{1,2,3,#,#,4,5}
该二叉树之字形层序遍历的结果是
示例 1 输入: {1,2,3,#,#,4,5}
返回值: [[1],[3,2],[4,5]]
示例 2 输入: {8,6,10,5,7,9,11}
返回值: [[8],[10,6],[5,7,9,11]]
示例 3 输入: {1,2,3,4,5}
返回值: [[1],[3,2],[4,5]]
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 import java.util.*;import java.util.ArrayList;public class Solution { public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) { ArrayList<ArrayList<Integer> > result = new ArrayList (); if (pRoot == null ) { return result; } ArrayList<TreeNode> layer = new ArrayList <>(); layer.add(pRoot); result.add(new ArrayList <Integer>() {{add(pRoot.val);}}); while (layer.size() > 0 ) { ArrayList<TreeNode> nextLayer = new ArrayList <>(); ArrayList<Integer> nextLayerValue = new ArrayList <>(); if (result.size() % 2 == 1 ) { for (int i = layer.size() - 1 ; i >= 0 ; i --) { TreeNode treeNode = layer.get(i); addTreeNode(treeNode.right, nextLayer, nextLayerValue); addTreeNode(treeNode.left, nextLayer, nextLayerValue); } } else { for (int i = layer.size() - 1 ; i >= 0 ; i --) { TreeNode treeNode = layer.get(i); addTreeNode(treeNode.left, nextLayer, nextLayerValue); addTreeNode(treeNode.right, nextLayer, nextLayerValue); } } layer = nextLayer; if (nextLayerValue.size() > 0 ) { result.add(nextLayerValue); } } return result; } private void addTreeNode (TreeNode treeNode, ArrayList<TreeNode> nextLayer, ArrayList<Integer> nextLayerValue) { if (treeNode != null ) { nextLayer.add(treeNode); nextLayerValue.add(treeNode.val); } } }
JZ54 二叉搜索树的第 k 个节点 时间限制: 1 秒 空间限制: 64M 知识点: 树 dfs 递归
描述
给定一棵结点数为 n 二叉搜索树,请找出其中的第 k 小的 TreeNode 结点值。 1.返回第 k 小的节点值即可 2.不能查找的情况,如二叉树为空,则返回-1,或者 k 大于 n 等等,也返回-1 3.保证 n 个节点的值不一样
数据范围:0≤n≤1000,0≤k≤1000,树上每个结点的值满足 0≤val≤1000 进阶:空间复杂度 O(n),时间复杂度 O(n)
如输入{5,3,7,2,4,6,8},3
时。该二叉树所有节点按结点值升序排列后可得[2,3,4,5,6,7,8]
,所以第 3 个结点的结点值为 4,故返回对应结点值为 4 的结点即可。
示例 1 输入: {5,3,7,2,4,6,8},3
返回值: 4示例 2 输入: {},1
返回值: -1 说明: 当树是空
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 import java.util.*;public class Solution { public int KthNode (TreeNode proot, int k) { if (proot == null || k < 1 ) { return -1 ; } List<Integer> list = new LinkedList <>(); addNode(list, proot); if (k > list.size()) { return -1 ; } return list.get(k - 1 ); } private void addNode (List<Integer> list, TreeNode curNode) { if (curNode == null ) { return ; } addNode(list, curNode.left); list.add(curNode.val); addNode(list, curNode.right); } } public class Solution { public int KthNode (TreeNode proot, int k) { TreeNode[] res = new TreeNode [] {null }; int [] count = new int [] {0 }; midOrder(res, count, proot, k); if (res[0 ] != null ) { return res[0 ].val; } return -1 ; } private void midOrder (TreeNode[] res, int [] count, TreeNode curNode, int k) { if (curNode == null || count[0 ] > k) { return ; } midOrder(res, count, curNode.left, k); count[0 ] ++; if (count[0 ] == k) { res[0 ] = curNode; } midOrder(res, count, curNode.right, k); } }
JZ7 重建二叉树 时间限制: 1 秒 空间限制: 64M 知识点: 树 dfs 数组
描述
给定节点数为 n 二叉树的前序遍历和中序遍历结果,请重建出该二叉树并返回它的头结点。 例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出如下图所示。
提示: 1.vin.length == pre.length 2.pre 和 vin 均无重复元素 3.vin 出现的元素均出现在 pre 里 4.只需要返回根结点,系统会自动输出整颗树做答案对比
数据范围:n≤2000,节点的值 −10000≤val≤10000 要求:空间复杂度 O(n),时间复杂度 O(n)
示例 1 输入: [1,2,4,7,3,5,6,8],[4,7,2,1,5,3,8,6]
返回值: {1,2,3,4,#,5,6,#,7,#,#,8}
说明:返回根节点,系统会输出整颗二叉树对比结果,重建结果如题面图示示例 2 输入: [1],[1]
返回值: {1}
示例 3 输入: [1,2,3,4,5,6,7],[3,2,4,1,6,5,7]
返回值: {1,2,5,3,4,6,7}
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 import java.util.Arrays;public class Solution { public TreeNode reConstructBinaryTree (int [] pre,int [] in) { if (pre.length == 0 || in.length == 0 || pre.length != in.length){ return null ; } TreeNode node = new TreeNode (pre[0 ]); for (int i = 0 ; i < in.length; i++) { if (pre[0 ] == in[i]) { node.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1 , i+1 ), Arrays.copyOfRange(in, 0 , i)); node.right = reConstructBinaryTree(Arrays.copyOfRange(pre, i+1 , pre.length), Arrays.copyOfRange(in, i+1 , in.length)); } } return node; } } public class Solution { public TreeNode reConstructBinaryTree (int [] pre,int [] in) { return reConstructBinaryTree(pre, in, 0 , 0 , pre.length); } public TreeNode reConstructBinaryTree (int [] pre, int [] in, int preBeginIndex, int inBeginIndex, int length) { if (preBeginIndex < 0 || preBeginIndex + length > pre.length || inBeginIndex < 0 || inBeginIndex + length > pre.length || length <= 0 ) { return null ; } TreeNode root = new TreeNode (pre[preBeginIndex]); int rootIndex = preBeginIndex; int leftLength = 0 ; for (int i = inBeginIndex; i < inBeginIndex + length; i ++) { if (pre[preBeginIndex] == in[i]) { rootIndex = i; leftLength = i - inBeginIndex; break ; } } int rightLength = length - leftLength - 1 ; root.left = reConstructBinaryTree(pre, in, preBeginIndex + 1 , inBeginIndex, leftLength); root.right = reConstructBinaryTree(pre, in, preBeginIndex + leftLength + 1 , rootIndex + 1 , rightLength); return root; } }
JZ26 树的子结构 时间限制: 1 秒 空间限制: 64M 知识点: 二叉树 树
描述
输入两棵二叉树 A,B,判断 B 是不是 A 的子结构。(我们约定空树不是任意一个树的子结构) 假如给定 A 为{8,8,7,9,2,#,#,#,#,4,7},B 为{8,9,2},2 个树的结构如下,可以看出 B 是 A 的子结构
数据范围: 0 <= A 的节点个数 <= 10000 0 <= B 的节点个数 <= 10000
示例 1 输入: {8,8,7,9,2,#,#,#,#,4,7},{8,9,2}
返回值: true示例 2 输入: {1,2,3,4,5},{2,4}
返回值: true示例 3 输入: {1,2,3},{3,1}
返回值: false
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 import java.util.*;public class Solution { public boolean HasSubtree (TreeNode root1,TreeNode root2) { if (root1 == null || root2 == null ) { return false ; } return traverse(root1, root2); } public boolean traverse (TreeNode root1,TreeNode root2) { if (root1 == null ) { return false ; } if (compare(root1, root2)) { return true ; } else { return traverse(root1.left, root2) || traverse(root1.right, root2); } } private boolean compare (TreeNode root1, TreeNode root2) { if (root1 != null ) { if (root2 == null ) { return true ; } else if (root1.val != root2.val) { return false ; } else { return compare(root1.left, root2.left) && compare(root1.right, root2.right); } } else { if (root2 == null ) { return true ; } else { return false ; } } } }
JZ27 二叉树的镜像 时间限制: 1 秒 空间限制: 64M 知识点:
描述
操作给定的二叉树,将其变换为源二叉树的镜像。 数据范围:二叉树的节点数 0≤n≤1000 , 二叉树每个节点的值 0≤val≤1000 要求: 空间复杂度 O(n)。本题也有原地操作,即空间复杂度 O(1) 的解法,时间复杂度 O(n)
比如: 源二叉树 镜像二叉树
示例 1 输入: {8,6,10,5,7,9,11}
返回值: {8,10,6,11,9,7,5}
说明: 如题面所示示例 2 输入: {}
返回值: {}
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 import java.util.*;public class Solution { public TreeNode Mirror (TreeNode pRoot) { if (pRoot == null ) { return null ; } TreeNode tmp = pRoot.left; pRoot.left = pRoot.right; pRoot.right = tmp; Mirror(pRoot.left); Mirror(pRoot.right); return pRoot; } } public class Solution { public TreeNode Mirror (TreeNode pRoot) { if (pRoot == null ) return null ; Stack<TreeNode> stack = new Stack <>(); stack.add(pRoot); while (!stack.isEmpty()) { TreeNode node = stack.pop(); if (node.left != null ) stack.add(node.left); if (node.right != null ) stack.add(node.right); TreeNode tmp = node.left; node.left = node.right; node.right = tmp; } return pRoot; } }
解法 2
JZ32 从上往下打印二叉树 时间限制: 1 秒 空间限制: 64M 知识点: 队列 树
描述
不分行从上往下打印出二叉树的每个节点,同层节点从左至右打印。例如输入{8,6,10,#,#,2,1},如以下图中的示例二叉树,则依次打印 8,6,10,2,1(空节点不打印,跳过),请你将打印的结果存放到一个数组里面,返回。
数据范围: 0<=节点总数<=1000 -1000<=节点值<=1000
示例 1 输入: {8,6,10,#,#,2,1}
返回值: [8,6,10,2,1]示例 2 输入: {5,4,#,3,#,2,#,1}
返回值: [5,4,3,2,1]
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import java.util.*;import java.util.ArrayList;public class Solution { public ArrayList<Integer> PrintFromTopToBottom (TreeNode root) { ArrayList<Integer> result = new ArrayList <>(); List<TreeNode> layer = new LinkedList <>(); layer.add(root); while (layer.size() > 0 ) { List<TreeNode> tmp = layer; layer = new LinkedList <>(); for (TreeNode node : tmp) { if (node != null ) { result.add(node.val); layer.add(node.left); layer.add(node.right); } } } return result; } }
JZ33 二叉搜索树的后序遍历序列 时间限制: 1 秒 空间限制: 64M 知识点: 栈 树
描述
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回 true ,否则返回 false 。假设输入的数组的任意两个数字都互不相同。
数据范围: 节点数量 0≤n≤1000 ,节点上的值满足 1≤val≤105 ,保证节点上的值各不相同 要求:空间复杂度 O(n) ,时间时间复杂度 O(n^2)
提示: 1.二叉搜索树是指父亲节点大于左子树中的全部节点,但是小于右子树中的全部节点的树。 2.该题我们约定空树不是二叉搜索树 3.后序遍历是指按照 “左子树-右子树-根节点” 的顺序遍历 4.参考下面的二叉搜索树,示例 1
示例 1 输入: [1,3,2]
返回值: true 说明: 是上图的后序遍历 ,返回 true示例 2 输入: [3,1,2]
返回值: false 说明: 不属于上图的后序遍历,从另外的二叉搜索树也不能后序遍历出该序列 ,因为最后的 2 一定是根节点,前面一定是孩子节点,可能是左孩子,右孩子,根节点,也可能是全左孩子,根节点,也可能是全右孩子,根节点,但是 [3,1,2]
的组合都不能满足这些情况,故返回 false示例 3 输入: [5,7,6,9,11,10,8]
返回值: true
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import java.util.*;public class Solution { public boolean VerifySquenceOfBST (int [] sequence) { int length = sequence.length; if (sequence.length <= 0 ) { return false ; } return verify(sequence, 0 , length - 1 ); } public boolean verify (int [] sequence, int l, int r) { if (l >= r) { return true ; } int root = sequence[r]; int j = r - 1 ; while (j >= 0 && sequence[j] > root) { j --; } for (int i = l; i <= j; i ++) { if (sequence[i] > root) { return false ; } } return verify(sequence, l, j) && verify(sequence, j + 1 , r - 1 ); } }
JZ82 二叉树中和为某一值的路径(一) 时间限制: 1 秒 空间限制: 64M 知识点: 树 dfs
描述
给定一个二叉树 root 和一个值 sum ,判断是否有从根节点到叶子节点的节点值之和等于 sum 的路径。 1.该题路径定义为从树的根结点开始往下一直到叶子结点所经过的结点 2.叶子节点是指没有子节点的节点 3.路径只能从父节点到子节点,不能从子节点到父节点 4.总节点数目为 n
例如: 给出如下的二叉树,sum=22,
返回 true,因为存在一条路径 5→4→11→2 的节点值之和为 22
数据范围: 1.树上的节点数满足 0≤n≤10000 2.每 个节点的值都满足 ∣val∣≤1000 要求:空间复杂度 O(n),时间复杂度 O(n) 进阶:空间复杂度 O(树的高度),时间复杂度 O(n)
示例 1 输入: {5,4,8,1,11,#,9,#,#,2,7},22
返回值: true示例 2 输入: {1,2},0
返回值: false示例 3 输入: {1,2},3
返回值: true示例 4 输入: {},0
返回值: false
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 import java.util.*;public class Solution { public boolean hasPathSum (TreeNode root, int sum) { if (root == null ) { return false ; } return hasPathSum(root, sum, root.val); } public boolean hasPathSum (TreeNode root, int sum, int res) { if (root.left == null && root.right == null ) { if (res == sum) { return true ; } else { return false ; } } else { boolean left = false ; boolean right = false ; if (root.left != null ) { left = hasPathSum(root.left, sum, res + root.left.val); } if (root.right != null ) { right = hasPathSum(root.right, sum, res + root.right.val); } return left || right; } } } public class Solution { public boolean hasPathSum (TreeNode root, int sum) { if (root == null ) return false ; if (root.left == null && root.right == null && sum - root.val == 0 ) return true ; return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val); } }
JZ34 二叉树中和为某一值的路径(二) 时间限制: 1 秒 空间限制: 64M 知识点: 树
描述
输入一颗二叉树的根节点 root 和一个整数 expectNumber,找出二叉树中结点值的和为 expectNumber 的所有路径。 1.该题路径定义为从树的根结点开始往下一直到叶子结点所经过的结点 2.叶子节点是指没有子节点的节点 3.路径只能从父节点到子节点,不能从子节点到父节点 4.总节点数目为 n
如二叉树 root 为 {10,5,12,4,7}
, expectNumber 为 22 则合法路径有 [[10,5,7],[10,12]]
数据范围: 树中节点总数在范围 [0, 5000] 内 -1000 <= 节点值 <= 1000 -1000 <= expectNumber <= 1000
示例 1 输入: {10,5,12,4,7},22
返回值: [[10,5,7],[10,12]]
说明: 返回 [[10,12],[10,5,7]]
也是对的示例 2 输入: {10,5,12,4,7},15
返回值: []
示例 3 输入: {2,3},0
返回值: []
示例 4 输入: {1,3,4},7
返回值: []
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 import java.util.*;import java.util.ArrayList;public class Solution { public ArrayList<ArrayList<Integer>> FindPath (TreeNode root,int expectNumber) { ArrayList<ArrayList<Integer>> result = new ArrayList <>(); if (root == null ) { return result; } ArrayList<Integer> path = new ArrayList <>(); path.add(root.val); findPath(root, expectNumber, root.val, path, result); return result; } private void findPath (TreeNode root, int expectNumber, int sum, ArrayList<Integer> path, ArrayList<ArrayList<Integer>> result) { if (root.left == null && root.right == null && sum == expectNumber) { result.add(new ArrayList <>(path)); return ; } if (root.left != null ) { path.add(root.left.val); findPath(root.left, expectNumber, sum + root.left.val, path, result); path.remove(path.size() - 1 ); } if (root.right != null ) { path.add(root.right.val); findPath(root.right, expectNumber, sum + root.right.val, path, result); path.remove(path.size() - 1 ); } } } import java.util.*;public class Solution { private ArrayList<ArrayList<Integer>> ret = new ArrayList <>(); private LinkedList<Integer> path = new LinkedList <>(); public ArrayList<ArrayList<Integer>> FindPath (TreeNode root,int expectNumber) { dfs(root, expectNumber); return ret; } private void dfs (TreeNode root, int number) { if (root == null ) return ; path.add(root.val); number -= root.val; if (root.left == null && root.right == null && number == 0 ) { ret.add(new ArrayList <>(path)); } dfs(root.left, number); dfs(root.right, number); path.removeLast(); } }
JZ36 二叉搜索树与双向链表 时间限制: 1 秒 空间限制: 64M 知识点: 分治
描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。如下图所示
数据范围:输入二叉树的节点数 0≤n≤1000,二叉树中每个节点的值 0≤val≤1000 要求:空间复杂度 O(1)(即在原树上操作),时间复杂度 O(n)
注意: 1.要求不能创建任何新的结点,只能调整树中结点指针的指向。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继 2.返回链表中的第一个节点的指针 3.函数返回的 TreeNode,有左右指针,其实可以看成一个双向链表的数据结构 4.你不用输出双向链表,程序会根据你的返回值自动打印输出
输入描述: 二叉树的根节点
返回值描述: 双向链表的其中一个头节点。
示例 1 输入: {10,6,14,4,8,12,16}
返回值: From left to right are:4,6,8,10,12,14,16;From right to left are:16,14,12,10,8,6,4; 说明: 输入题面图中二叉树,输出的时候将双向链表的头节点返回即可。示例 2 输入: {5,4,#,3,#,2,#,1}
返回值: From left to right are:1,2,3,4,5;From right to left are:5,4,3,2,1; 说明:
1 2 3 4 5 6 7 8 9 5 / 4 / 3 / 2 / 1
树的形状如上图
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 import java.util.*;public class Solution { public TreeNode head = null ; public TreeNode pre = null ; public TreeNode Convert (TreeNode pRootOfTree) { if (pRootOfTree == null ) { return null ; } Convert(pRootOfTree.left); if (pre == null ) { head = pRootOfTree; pre = pRootOfTree; } else { pre.right = pRootOfTree; pRootOfTree.left = pre; pre = pRootOfTree; } Convert(pRootOfTree.right); return head; } } public class Solution { public TreeNode Convert (TreeNode pRootOfTree) { if (pRootOfTree == null ) return null ; Stack<TreeNode> s = new Stack <TreeNode>(); TreeNode head = null ; TreeNode pre = null ; boolean isFirst = true ; while (pRootOfTree != null || !s.isEmpty()) { while (pRootOfTree != null ) { s.push(pRootOfTree); pRootOfTree = pRootOfTree.left; } pRootOfTree = s.pop(); if (isFirst) { head = pRootOfTree; pre = head; isFirst = false ; } else { pre.right = pRootOfTree; pRootOfTree.left = pre; pre = pRootOfTree; } pRootOfTree = pRootOfTree.right; } return head; } }
JZ79 判断是不是平衡二叉树 时间限制: 1 秒 空间限制: 64M 知识点: 树 dfs
描述
输入一棵节点数为 n 二叉树,判断该二叉树是否是平衡二叉树。 在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树 平衡二叉树(Balanced Binary Tree),具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过 1,并且左右两个子树都是一棵平衡二叉树。 样例解释:
样例二叉树如图,为一颗平衡二叉树 注:我们约定空树是平衡二叉树。
数据范围:n≤100,树上节点的 val 值满足 0≤n≤1000 要求:空间复杂度 O(1),时间复杂度 O(n)
输入描述: 输入一棵二叉树的根节点
返回值描述: 输出一个布尔类型的值
示例 1 输入: {1,2,3,4,5,6,7}
返回值: true示例 2 输入: {}
返回值: true
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class Solution { public boolean IsBalanced_Solution (TreeNode root) { if (root == null ) { return true ; } return Math.abs(treeDepth(root.left) - treeDepth(root.right)) <= 1 && IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right); } public int treeDepth (TreeNode root) { if (root == null ) return 0 ; int l = treeDepth(root.left); int r = treeDepth(root.right); return Math.max(l, r) + 1 ; } }
JZ8 二叉树的下一个结点 时间限制: 1 秒 空间限制: 64M 知识点: 树
描述
给定一个二叉树其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的 next 指针。
示例: 输入: {8,6,10,5,7,9,11},8
返回: 9 解析: 这个组装传入的子树根节点,其实就是整颗树,中序遍历 {5,6,7,8,9,10,11}
,根节点 8 的下一个节点就是 9,应该返回 {9,10,11}
,后台只打印子树的下一个节点,所以只会打印 9,如下图,其实都有指向左右孩子的指针,还有指向父节点的指针,下图没有画出来
数据范围:节点数满足 1≤n≤50 ,节点上的值满足 1≤val≤100
要求:空间复杂度 O(1),时间复杂度 O(n)
示例 1 输入: {8,6,10,5,7,9,11},8
返回值: 9示例 2 输入: {8,6,10,5,7,9,11},6
返回值: 7示例 3 输入: {1,2,#,#,3,#,4},4
返回值: 1示例 4 输入: {5},5
返回值: “null” 说明: 不存在,后台打印”null”
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import java.util.*;public class Solution { ArrayList<TreeLinkNode> nodes = new ArrayList <>(); public TreeLinkNode GetNext (TreeLinkNode pNode) { TreeLinkNode root = pNode; while (root.next != null ) root = root.next; InOrder(root); int n = nodes.size(); for (int i = 0 ; i < n - 1 ; i++) { TreeLinkNode cur = nodes.get(i); if (pNode == cur) { return nodes.get(i+1 ); } } return null ; } void InOrder (TreeLinkNode root) { if (root != null ) { InOrder(root.left); nodes.add(root); InOrder(root.right); } } }
JZ28 对称的二叉树 时间限制: 1 秒 空间限制: 64M 知识点: 树
描述
给定一棵二叉树,判断其是否是自身的镜像(即:是否对称) 例如: 下面这棵二叉树是对称的 下面这棵二叉树不对称
数据范围:节点数满足 0≤n≤1000,节点上的值满足 ∣val∣≤1000 要求:空间复杂度 O(n),时间复杂度 O(n) 备注: 你可以用递归和迭代两种方法解决这个问题
示例 1 输入: {1,2,2,3,4,4,3}
返回值: true示例 2 输入: {8,6,9,5,7,7,5}
返回值: false
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import java.util.*;public class Solution { boolean isSymmetrical (TreeNode pRoot) { if (pRoot == null ) { return true ; } return recursion(pRoot.left, pRoot.right); } boolean recursion (TreeNode root1, TreeNode root2) { if (root1 == null && root2 == null ) { return true ; } if (root1 == null || root2 == null || root1.val != root2.val) { return false ; } return recursion(root1.left, root2.right) && recursion(root1.right, root2.left); } }
JZ78 把二叉树打印成多行 时间限制: 1 秒 空间限制: 64M 知识点: 树 广度优先搜索(BFS)
描述
给定一个节点数为 n 二叉树,要求从上到下按层打印二叉树的 val 值,同一层结点从左至右输出,每一层输出一行,将输出的结果存放到一个二维数组中返回。 例如: 给定的二叉树是 {1,2,3,#,#,4,5}
该二叉树多行打印层序遍历的结果是
数据范围:二叉树的节点数 0≤n≤1000,0≤val≤1000 要求:空间复杂度 O(n),时间复杂度 O(n) 输入描述: 给定一个二叉树的根节点
示例 1 输入: {1,2,3,#,#,4,5}
返回值: [[1],[2,3],[4,5]]
示例 2 输入: {8,6,10,5,7,9,11}
返回值: [[8],[6,10],[5,7,9,11]]
示例 3 输入: {1,2,3,4,5}
返回值: [[1],[2,3],[4,5]]
示例 4 输入: {}
返回值: []
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import java.util.*;import java.util.ArrayList;public class Solution { ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) { ArrayList<ArrayList<Integer> > res = new ArrayList <>(); if (pRoot == null ) { return res; } ArrayList<TreeNode> layer = new ArrayList <>(); layer.add(pRoot); while (layer.size() > 0 ) { ArrayList<Integer> values = new ArrayList <>(); ArrayList<TreeNode> tmp = new ArrayList <>(); for (TreeNode node : layer) { values.add(node.val); if (node.left != null ) { tmp.add(node.left); } if (node.right != null ) { tmp.add(node.right); } } res.add(values); layer = tmp; } return res; } }
JZ37 序列化二叉树 时间限制: 1 秒 空间限制: 64M 知识点: 队列 树
描述
请实现两个函数,分别用来序列化和反序列化二叉树,不对序列化之后的字符串进行约束,但要求能够根据序列化之后的字符串重新构造出一棵与原二叉树相同的树。
二叉树的序列化(Serialize)是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树等遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#)
二叉树的反序列化(Deserialize)是指:根据某种遍历顺序得到的序列化字符串结果 str,重构二叉树。
例如,可以根据层序遍历的方案序列化,如下图:
层序序列化(即用函数 Serialize 转化) 如上的二叉树转为 "{1,2,3,#,#,6,7}"
,再能够调用反序列化(Deserialize) 将 "{1,2,3,#,#,6,7}"
构造成如上的二叉树。
当然你也可以根据满二叉树结点位置的标号规律来序列化,还可以根据先序遍历和中序遍历的结果来序列化。不对序列化之后的字符串进行约束,所以欢迎各种奇思妙想。
数据范围:节点数 n≤100,树上每个节点的值满足 0≤val≤150 要求:序列化和反序列化都是空间复杂度 O(n),时间复杂度 O(n)
示例 1 输入: {1,2,3,#,#,6,7}
返回值: {1,2,3,#,#,6,7}
说明: 如题面图示例 2 输入: {8,6,10,5,7,9,11}
返回值: {8,6,10,5,7,9,11}
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 import java.util.*;public class Solution { public int index = 0 ; private void SerializeFunction (TreeNode root, StringBuilder str) { if (root == null ){ str.append('#' ); return ; } str.append(root.val).append('!' ); SerializeFunction(root.left, str); SerializeFunction(root.right, str); } public String Serialize (TreeNode root) { if (root == null ) return "#" ; StringBuilder res = new StringBuilder (); SerializeFunction(root, res); return res.toString(); } private TreeNode DeserializeFunction (String str) { if (str.charAt(index) == '#' ){ index++; return null ; } int val = 0 ; while (str.charAt(index) != '!' && index != str.length()){ val = val * 10 + ((str.charAt(index)) - '0' ); index++; } TreeNode root = new TreeNode (val); if (index == str.length()) return root; else index++; root.left = DeserializeFunction(str); root.right = DeserializeFunction(str); return root; } public TreeNode Deserialize (String str) { if (str == "#" ) return null ; TreeNode res = DeserializeFunction(str); return res; } }
JZ84 二叉树中和为某一值的路径(三) 时间限制: 1 秒 空间限制: 64M 知识点: 树
描述
给定一个二叉树 root 和一个整数值 sum ,求该树有多少路径的的节点值之和等于 sum 。 1.该题路径定义不需要从根节点开始,也不需要在叶子节点结束,但是一定是从父亲节点往下到孩子节点 2.总节点数目为 n 3.保证最后返回的路径个数在整形范围内(即路径个数小于 231 -1)
数据范围: 0<=n<=1000 -10^9<=节点值<=10^9
假如二叉树 root 为 {1,2,3,4,5,4,3,#,#,-1}
,sum=6,那么总共如下所示,有 3 条路径符合要求
示例 1 输入: {1,2,3,4,5,4,3,#,#,-1},6
返回值: 3 说明: 如图所示,有 3 条路径符合示例 2 输入: {0,1},1
返回值: 2示例 3 输入: {1,#,2,#,3},3
返回值: 2
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import java.util.*;public class Solution { private int res = 0 ; public int FindPath (TreeNode root, int sum) { if (root == null ) { return res; } dfs(root, sum); FindPath(root.left, sum); FindPath(root.right, sum); return res; } private void dfs (TreeNode root, int sum) { if (root == null ) { return ; } if (sum == root.val) { res ++; } dfs(root.left, sum - root.val); dfs(root.right, sum - root.val); } }
JZ86 在二叉树中找到两个节点的最近公共祖先 时间限制: 1 秒 空间限制: 64M 知识点: 树
描述
给定一棵二叉树(保证非空)以及这棵树上的两个节点对应的 val 值 o1 和 o2,请找到 o1 和 o2 的最近公共祖先节点。
数据范围:树上节点数满足 1≤n≤10^5, 节点值 val 满足区间 [0,n) 要求:时间复杂度 O(n)
注:本题保证二叉树中每个节点的 val 值均不相同。
如当输入 {3,5,1,6,2,0,8,#,#,7,4},5,1
时,二叉树 {3,5,1,6,2,0,8,#,#,7,4}
如下图所示:
所以节点值为 5 和节点值为 1 的节点的最近公共祖先节点的节点值为 3,所以对应的输出为 3。 节点本身可以视为自己的祖先
示例 1 输入: {3,5,1,6,2,0,8,#,#,7,4},5,1
返回值: 3示例 2 输入: {3,5,1,6,2,0,8,#,#,7,4},2,7
返回值: 2
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 import java.util.*;public class Solution { private boolean flag = false ; public int lowestCommonAncestor (TreeNode root, int o1, int o2) { ArrayList<Integer> path1 = new ArrayList <>(); ArrayList<Integer> path2 = new ArrayList <>(); dfs(root, path1, o1); flag = false ; dfs(root, path2, o2); int res = 0 ; for (int i = 0 ; i < path1.size() && i < path2.size(); i ++) { int x = path1.get(i); int y = path2.get(i); if (x == y) { res = x; } else { break ; } } return res; } private void dfs (TreeNode root, List<Integer> path, int o) { if (flag || root == null ) { return ; } path.add(root.val); if (root.val == o) { flag = true ; return ; } dfs(root.left, path, o); dfs(root.right, path, o); if (flag) { return ; } path.remove(path.size() - 1 ); } }
JZ68 二叉搜索树的最近公共祖先 时间限制: 1 秒 空间限制: 64M 知识点: 树 递归
描述
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
对于该题的最近的公共祖先定义:对于有根树 T 的两个节点 p、q,最近公共祖先 LCA(T,p,q)表示一个节点 x,满足 x 是 p 和 q 的祖先且 x 的深度尽可能大。在这里,一个节点也可以是它自己的祖先.
二叉搜索树是若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值; 若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值
所有节点的值都是唯一的。
p、q 为不同节点且均存在于给定的二叉搜索树中。
数据范围: 3<=节点总数<=10000 0<=节点值<=10000
如果给定以下搜索二叉树: {7,1,12,0,4,11,14,#,#,3,5}
,如下图:
示例 1 输入: {7,1,12,0,4,11,14,#,#,3,5},1,12
返回值: 7 说明: 节点 1 和 节点 12 的最近公共祖先是 7示例 2 输入: {7,1,12,0,4,11,14,#,#,3,5},12,11
返回值: 12 说明: 因为一个节点也可以是它自己的祖先.所以输出 12
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import java.util.*;public class Solution { public int lowestCommonAncestor (TreeNode root, int p, int q) { TreeNode cur = root; while (cur != null ) { if (p < cur.val && q < cur.val) { cur = cur.left; } else if (p > cur.val && q > cur.val) { cur = cur.right; } else { break ; } } return cur.val; } }
队列 & 栈 JZ9 用两个栈实现队列 时间限制: 1 秒 空间限制: 64M 知识点: 栈
描述
用两个栈来实现一个队列,使用 n 个元素来完成 n 次在队列尾部插入整数(push)和 n 次在队列头部删除整数(pop)的功能。 队列中的元素为 int 类型。保证操作合法,即保证 pop 操作时队列内已有元素。
数据范围: n≤1000 要求:存储 n 个元素的空间复杂度为 O(n) ,插入与删除的时间复杂度都是 O(1)
示例 1
输入: ["PSH1","PSH2","POP","POP"]
返回值: 1,2 说明: “PSH1”:代表将 1 插入队列尾部 “PSH2”:代表将 2 插入队列尾部 “POP”:代表删除一个元素,先进先出=>返回 1 “POP”:代表删除一个元素,先进先出=>返回 2
示例 2 输入: ["PSH2","POP","PSH1","POP"]
返回值: 2,1
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import java.util.Stack;public class Solution { Stack<Integer> stack1 = new Stack <Integer>(); Stack<Integer> stack2 = new Stack <Integer>(); public void push (int node) { stack1.push(node); } public int pop () { if (stack2.empty()) { while (!stack1.empty()) { stack2.push(stack1.pop()); } } return stack2.pop(); } }
JZ30 包含 min 函数的栈 时间限制: 1 秒 空间限制: 64M 知识点: 栈
描述
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的 min 函数,输入操作时保证 pop、top 和 min 函数操作时,栈中一定有元素。
此栈包含的方法有: push(value):将 value 压入栈中 pop():弹出栈顶元素 top():获取栈顶元素 min():获取栈中最小元素
数据范围:操作数量满足 0≤n≤300,输入的元素满足 ∣val∣≤10000 进阶:栈的各个操作的时间复杂度是 O(1),空间复杂度是 O(n)
示例: 输入: ["PSH-1","PSH2","MIN","TOP","POP","PSH1","TOP","MIN"]
输出: -1,2,1,-1 解析: “PSH-1”表示将-1 压入栈中,栈中元素为-1 “PSH2”表示将 2 压入栈中,栈中元素为 2,-1 “MIN”表示获取此时栈中最小元素==>返回-1 “TOP”表示获取栈顶元素==>返回 2 “POP”表示弹出栈顶元素,弹出 2,栈中元素为-1 “PSH1”表示将 1 压入栈中,栈中元素为 1,-1 “TOP”表示获取栈顶元素==>返回 1 “MIN”表示获取此时栈中最小元素==>返回-1
示例 1 输入: ["PSH-1","PSH2","MIN","TOP","POP","PSH1","TOP","MIN"]
返回值: -1,2,1,-1
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import java.util.Stack;public class Solution { Stack<Integer> s1 = new Stack <Integer>(); Stack<Integer> s2 = new Stack <Integer>(); public void push (int node) { s1.push(node); if (s2.isEmpty() || node <= s2.peek()) { s2.push(node); } } public void pop () { int v = s1.pop(); if (s2.peek() == v) { s2.pop(); } } public int top () { return s1.peek(); } public int min () { return s2.peek(); } }
JZ31 栈的压入、弹出序列 时间限制: 1 秒 空间限制: 64M 知识点: 栈
描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列 1,2,3,4,5 是某栈的压入顺序,序列 4,5,3,2,1 是该压栈序列对应的一个弹出序列,但 4,3,5,1,2 就不可能是该压栈序列的弹出序列。
0 <= pushV.length == popV.length <=1000
-1000 <= pushV[i] <= 1000
pushV 的所有数字均不相同
示例 1 输入: [1,2,3,4,5],[4,5,3,2,1]
返回值: true 说明: 可以通过 push(1)=>push(2)=>push(3)=>push(4)=>pop()=>push(5)=>pop()=>pop()=>pop()=>pop() 这样的顺序得到 [4,5,3,2,1]
这个序列,返回 true示例 2 输入: [1,2,3,4,5],[4,3,5,1,2]
返回值: false 说明: 由于是 [1,2,3,4,5]
的压入顺序,[4,3,5,1,2]
的弹出顺序,要求 4,3,5 必须在 1,2 前弹出,且 1,2 不能弹出,但是这样压入的顺序,1 又不能在 2 之前弹出,所以无法形成的,返回 false
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import java.util.*;public class Solution { public boolean IsPopOrder (int [] pushA,int [] popA) { int n = pushA.length; Stack<Integer> s = new Stack <>(); int j = 0 ; for (int i = 0 ; i < n; i ++) { while (j < n && (s.isEmpty() || s.peek() != popA[i])) { s.push(pushA[j]); j ++; } if (s.peek() == popA[i]) { s.pop(); } else { return false ; } } return true ; } }
JZ73 翻转单词序列 时间限制: 1 秒 空间限制: 64M 知识点: 字符串 双指针
描述
牛客最近来了一个新员工 Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事 Cat 对 Fish 写的内容颇感兴趣,有一天他向 Fish 借来翻看,但却读不懂它的意思。例如,“nowcoder. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a nowcoder.”。Cat 对一一的翻转这些单词顺序可不在行,你能帮助他么?
数据范围:1≤n≤100 进阶:空间复杂度 O(n),时间复杂度 O(n),保证没有只包含空格的字符串
示例 1 输入: “nowcoder. a am I” 返回值: “I am a nowcoder.”示例 2 输入: “” 返回值: “”
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import java.util.*;public class Solution { public String ReverseSentence (String str) { int n = str.length(); char [] cs = str.toCharArray(); reverse(cs, 0 , n - 1 ); for (int i = 0 ; i < n; i ++) { int j = i + 1 ; while (j < n && cs[i] != ' ' && cs[j] != ' ' ) { j ++; } reverse(cs, i, j - 1 ); i = j; } return new String (cs); } private void reverse (char [] cs, int l, int r) { while (l < r) { swap(cs, l ++, r --); } } private void swap (char [] cs, int l, int r) { char tmp = cs[l]; cs[l] = cs[r]; cs[r] = tmp; } }
JZ59 滑动窗口的最大值 时间限制: 1 秒 空间限制: 64M 知识点: 堆 双指针 队列
描述
给定一个长度为 n 的数组 nums 和滑动窗口的大小 size ,找出所有滑动窗口里数值的最大值。
例如,如果输入数组 {2,3,4,2,6,2,5,1}
及滑动窗口的大小 3,那么一共存在 6 个滑动窗口,他们的最大值分别为 {4,4,6,6,6,5}
;针对数组 {2,3,4,2,6,2,5,1}
的滑动窗口有以下 6 个:{[2,3,4],2,6,2,5,1}
, {2,[3,4,2],6,2,5,1}
, {2,3,[4,2,6],2,5,1}
, {2,3,4,[2,6,2],5,1}
, {2,3,4,2,[6,2,5],1}
, {2,3,4,2,6,[2,5,1]}
。
数据范围:1≤size≤n≤10000,数组中每个元素的值满足 ∣val∣≤10000 要求:空间复杂度 O(n),时间复杂度 O(n)
示例 1 输入: [2,3,4,2,6,2,5,1],3
返回值: [4,4,6,6,6,5]
示例 2 输入: [9,10,9,-7,-3,8,2,-6],5
返回值: [10,10,9,8]
示例 3 输入: [1,2,3,4],3
返回值: [3,4]
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 import java.util.*;public class Solution { public ArrayList<Integer> maxInWindows (int [] num, int size) { ArrayList<Integer> res = new ArrayList <>(); if (size <= 0 ) { return res; } int maxIndex = -1 ; for (int i = 0 ; i <= num.length - size; i ++) { int r = i + size - 1 ; if (i > maxIndex) { maxIndex = findMax(num, i, r); } else if (num[r] >= num[maxIndex]) { maxIndex = r; } res.add(num[maxIndex]); } return res; } private int findMax (int [] num, int l, int r) { int minIndex = l; for (int i = l + 1 ; i <= r; i ++) { if (num[i] >= num[minIndex]) { minIndex = i; } } return minIndex; } } public class Solution { public ArrayList<Integer> maxInWindows (int [] num, int size) { ArrayList<Integer> res = new ArrayList <>(); if (size <= 0 || size > num.length) { return res; } ArrayDeque<Integer> dq = new ArrayDeque <Integer>(); for (int i = 0 ; i < size; i ++) { addMaxDeque(num, dq, i); } for (int i = 0 ; i <= num.length - size; i ++) { int r = i + size - 1 ; while (!dq.isEmpty() && i > dq.peekFirst()) { dq.pollFirst(); } addMaxDeque(num, dq, r); res.add(num[dq.peekFirst()]); } return res; } private void addMaxDeque (int [] num, ArrayDeque<Integer> dq, int i) { while (!dq.isEmpty() && num[dq.peekLast()] <= num[i]) { dq.pollLast(); } dq.add(i); } }
搜索算法 JZ53 数字在升序数组中出现的次数 时间限制: 1 秒 空间限制: 64M 知识点: 数组 二分
描述
给定一个长度为 n 的非降序数组和一个非负数整数 k ,要求统计 k 在数组中出现的次数
数据范围:0≤n≤1000,0≤k≤100,数组中每个元素的值满足 0≤val≤100 要求:空间复杂度 O(1),时间复杂度 O(logn)
示例 1 输入: [1,2,3,3,3,3,4,5],3
返回值: 4示例 2 输入: [1,3,4,5],6
返回值: 0
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public class Solution { public int GetNumberOfK (int [] array , int k) { int index = getIndex(array, k, 0 , array.length - 1 ); if (index == -1 ) { return 0 ; } int num = 1 ; for (int i = index + 1 ; i < array.length && array[i] == array[index]; i ++) { num ++; } for (int i = index - 1 ; i >= 0 && array[i] == array[index]; i --) { num ++; } return num; } private int getIndex (int [] array, int k, int l, int r) { if (l > r) { return -1 ; } int mid = (r - l) / 2 + l; if (array[mid] == k) { return mid; } else if (array[mid] > k) { return getIndex(array, k, l, mid - 1 ); } else { return getIndex(array, k, mid + 1 , r); } } } public class Solution { private int bisearch (int [] data, double k) { int left = 0 ; int right = data.length - 1 ; while (left <= right){ int mid = (left + right) / 2 ; if (data[mid] < k) left = mid + 1 ; else if (data[mid] > k) right = mid - 1 ; } return left; } public int GetNumberOfK (int [] array , int k) { return bisearch(array, k + 0.5 ) - bisearch(array, k - 0.5 ); } }
JZ4 二维数组中的查找 时间限制: 1 秒 空间限制: 64M 本题知识点: 数组 题目描述 在一个二维数组 array 中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
1 2 3 4 5 6 [ [1,2,8,9], [2,4,9,12], [4,7,10,13], [6,8,11,15] ]
给定 target = 7,返回 true。 给定 target = 3,返回 false。
数据范围:矩阵的长宽满足 0≤n,m≤500 , 矩阵中的值满足 0≤val≤109 进阶:空间复杂度 O(1),时间复杂度 O(n+m)
示例 1 输入: 7,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]
返回值: true 说明:存在 7,返回 true示例 2 输入: 1,[[2]]
返回值: false示例 3 输入:3,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]
返回值:false 说明:不存在 3,返回 false
答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Solution { public boolean Find (int target, int [][] array) { int rows = array.length; int cols = array[0 ].length; int row = rows - 1 ; int col = 0 ; while (row >= 0 && col < cols) { if (array[row][col] > target) { row --; } else if (array[row][col] < target) { col ++; } else { return true ; } } return false ; } }
JZ11 旋转数组的最小数字 时间限制: 1 秒 空间限制: 64M 知识点: 二分
描述
有一个长度为 n 的非降序数组,比如 [1,2,3,4,5]
,将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了 [3,4,5,1,2]
,或者 [4,5,1,2,3]
这样的。请问,给定这样一个旋转数组,求数组中的最小值。
数据范围:1≤n≤10000,数组中任意元素的值: 0≤val≤10000 要求:空间复杂度:O(1),时间复杂度:O(logn)
示例 1 输入: [3,4,5,1,2]
返回值: 1示例 2 输入: [3,100,200,3]
返回值: 3
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 import java.util.*;public class Solution { public int minNumberInRotateArray (int [] array) { return solution2(array); } public int solution1 (int [] array) { if (array.length == 0 ) { return 0 ; } return Arrays.stream(array).min().getAsInt(); } public int solution2 (int [] array) { if (array.length == 0 ) { return 0 ; } if (array.length == 1 ) { return array[0 ]; } int first = 0 ; int last = array.length - 1 ; while (first < last) { if (array[first] < array[last]) { return array[first]; } int mid = (last - first) / 2 + first; if (array[mid] > array[last]) { first = mid + 1 ; } else if (array[mid] < array[last]) { last = mid; } else { last --; } } return array[first]; } }
JZ38 字符串的排列 时间限制: 1 秒 空间限制: 64M 知识点: 字符串 递归
描述
输入一个长度为 n 字符串,打印出该字符串中字符的所有排列,你可以以任意顺序返回这个字符串数组。 例如输入字符串 ABC,则输出由字符 A,B,C 所能排列出来的所有字符串 ABC,ACB,BAC,BCA,CBA 和 CAB。
数据范围:n < 10 要求:空间复杂度 O(n!),时间复杂度 O(n!) 输入描述: 输入一个字符串,长度不超过 10,字符只包括大小写字母。
示例 1 输入: “ab” 返回值: ["ab","ba"]
说明: 返回 ["ba","ab"]
也是正确的示例 2 输入: “aab” 返回值: ["aab","aba","baa"]
示例 3 输入: “abc” 返回值: ["abc","acb","bac","bca","cab","cba"]
示例 4 输入: “” 返回值: []
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import java.util.*;public class Solution { public ArrayList<String> Permutation (String str) { ArrayList<String> res = new ArrayList <>(); if (str == null || str.length() == 0 ) { return res; } char [] charStr = str.toCharArray(); Arrays.sort(charStr); boolean [] vis = new boolean [str.length()]; Arrays.fill(vis, false ); StringBuilder tmp = new StringBuilder (); recursion(res, charStr, tmp, vis); return res; } public void recursion (ArrayList<String> res, char [] str, StringBuilder tmp, boolean [] vis) { if (tmp.length() == str.length) { res.add(new String (tmp)); return ; } for (int i = 0 ; i < str.length; i ++) { if (vis[i] || (i > 0 && str[i - 1 ] == str[i] && !vis[i - 1 ])) { continue ; } vis[i] = true ; tmp.append(str[i]); recursion(res, str, tmp, vis); vis[i] = false ; tmp.deleteCharAt(tmp.length() - 1 ); } } }
JZ44 数字序列中某一位的数字 时间限制: 1 秒 空间限制: 64M 知识点: 模拟
描述
数字以 0123456789101112131415… 的格式作为一个字符序列,在这个序列中第 2 位(从下标 0 开始计算)是 2 ,第 10 位是 1 ,第 13 位是 1 ,以此类题,请你输出第 n 位对应的数字。
数据范围:0<=n<=10^9
示例 1 输入: 0 返回值: 0示例 2 输入: 2 返回值: 2示例 3 输入: 10 返回值: 1示例 4 输入: 13 返回值: 1
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import java.util.*;public class Solution { public int findNthDigit (int n) { int digit = 1 ; long start = 1 ; long sum = 9 ; while (n > sum) { n -= sum; start *= 10 ; digit ++; sum = 9 * start * digit; } String num = "" + (start + (n - 1 ) / digit); int index = (n - 1 ) % digit; return (int )(num.charAt(index)) - (int )('0' ); } }
动态规划 JZ42 连续子数组的最大和 时间限制: 1 秒 空间限制: 64M 知识点: 动态规划 贪心
描述
输入一个长度为 n 的整型数组 array,数组中的一个或连续多个整数组成一个子数组,子数组最小长度为 1。求所有子数组的和的最大值。 数据范围:
1 2 1 <= n <= 2 * 10^5 -100 <= a[i] <= 100
要求:时间复杂度为 O(n),空间复杂度为 O(n) 进阶:时间复杂度为 O(n),空间复杂度为 O(1)
示例 1 输入: [1,-2,3,10,-4,7,2,-5]
返回值: 18 说明: 经分析可知,输入数组的子数组 [3,10,-4,7,2]
可以求得最大和为 18示例 2 输入: [2]
返回值: 2示例 3 输入: [-10]
返回值: -10
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Solution { public int FindGreatestSumOfSubArray (int [] array) { int lastMax = array[0 ]; int max = lastMax; for (int i = 1 ; i < array.length; i ++) { lastMax = Math.max(lastMax + array[i], array[i]); if (lastMax > max) { max = lastMax; } } return max; } }
JZ85 连续子数组的最大和(二) 时间限制: 1 秒 空间限制: 64M 知识点: 贪心 动态规划 数组 双指针
描述
输入一个长度为 n 的整型数组 array,数组中的一个或连续多个整数组成一个子数组,找到一个具有最大和的连续子数组。
子数组是连续的,比如 [1,3,5,7,9]
的子数组有 [1,3],[3,5,7]
等等,但是 [1,3,7]
不是子数组
如果存在多个最大和的连续子数组,那么返回其中长度最长的,该题数据保证这个最长的只存在一个
该题定义的子数组的最小长度为 1,不存在为空的子数组,即不存在[]是某个数组的子数组
返回的数组不计入空间复杂度计算
数据范围:
1 2 1<=n<=10^5 -100 <= a[i] <= 100
要求:时间复杂度 O(n),空间复杂度 O(n) 进阶:时间复杂度 O(n),空间复杂度 O(1)
示例 1 输入: [1,-2,3,10,-4,7,2,-5]
返回值: [3,10,-4,7,2]
说明: 经分析可知,输入数组的子数组 [3,10,-4,7,2]
可以求得最大和为 18,故返回 [3,10,-4,7,2]
示例 2 输入: [1]
返回值: [1]
示例 3 输入: [1,2,-3,4,-1,1,-3,2]
返回值: [1,2,-3,4,-1,1]
说明: 经分析可知,最大子数组的和为 4,有 [4],[4,-1,1],[1,2,-3,4],[1,2,-3,4,-1,1]
,故返回其中长度最长的 [1,2,-3,4,-1,1]
示例 4 输入: [-2,-1]
返回值: [-1]
说明: 子数组最小长度为 1,故返回 [-1]
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import java.util.*;public class Solution { public int [] FindGreatestSumOfSubArray (int [] array) { int lastMax = array[0 ]; int max = lastMax; int lastStartIndex = 0 ; int startIndex = 0 ; int maxIndex = 0 ; for (int i = 1 ; i < array.length; i ++) { int num = array[i]; if (lastMax + num >= num) { lastMax += num; } else { lastMax = num; lastStartIndex = i; } if (lastMax >= max) { max = lastMax; startIndex = lastStartIndex; maxIndex = i; } } return Arrays.copyOfRange(array, startIndex, maxIndex + 1 ); } }
JZ69 跳台阶 时间限制: 1 秒 空间限制: 64M 知识点: 递归 动态规划 记忆化搜索
描述
一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
数据范围:1≤n≤40 要求:时间复杂度:O(n),空间复杂度:O(1)
示例 1 输入: 2 返回值: 2 说明: 青蛙要跳上两级台阶有两种跳法,分别是:先跳一级,再跳一级或者直接跳两级。因此答案为 2示例 2 输入: 7 返回值: 21
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Solution { public int jumpFloor (int target) { if (target <= 1 ) { return target; } int c = 1 ; int a = 1 ; int b = 1 ; for (int i = 2 ; i <= target; i ++) { c = a + b; a = b; b = c; } return c; } }
JZ10 斐波那契数列 时间限制: 1 秒 空间限制: 64M 知识点: 数组 动态规划 记忆化搜索 快速幂 递归
描述
大家都知道斐波那契数列,现在要求输入一个正整数 n ,请你输出斐波那契数列的第 n 项。 要求:空间复杂度 O(1),时间复杂度 O(n) ,本题也有时间复杂度 O(logn) 的解法
示例 1 输入: 4 返回值: 3示例 2 输入: 1 返回值: 1示例 3 输入: 2 返回值: 1
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class Solution { public int Fibonacci (int n) { return solution1(n); } public int solution1 (int n) { if (n <= 1 ) { return n; } int c = 0 ; int a = 0 ; int b = 1 ; for (int i = 2 ; i <= n; i++){ c = a + b; a = b; b = c; } return c; } public int solution2 (int n) { int ans[] = new int [n + 1 ]; ans[0 ] = 0 ; ans[1 ] = 1 ; for (int i=2 ;i<=n;i++){ ans[i] = ans[i-1 ] + ans[i-2 ]; } return ans[n]; } public int solution3 (int n) { if (n <= 1 ) { return n; } return solution2(n - 2 ) + solution2(n - 1 ); } }
JZ19 正则表达式匹配 时间限制: 1 秒 空间限制: 64M 知识点: 字符串 动态规划 递归
描述
请实现一个函数用来匹配包括’.’和’‘的正则表达式。 1.模式中的字符’.’表示任意一个字符 2.模式中的字符’ ‘表示它前面的字符可以出现任意次(包含 0 次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串”aaa”与模式”a.a”和”abac a”匹配,但是与”aa.a”和”ab*a”均不匹配
数据范围: 1.str 只包含从 a-z 的小写字母。 2.pattern 只包含从 a-z 的小写字母以及字符 . 和 ,无连续的 ‘ ‘。 3. 0≤str.length≤26 4. 0≤pattern.length≤26
示例 1 输入: “aaa”,”aa” 返回值: true 说明: 中间的 可以出现任意次的 a,所以可以出现 1 次 a,能匹配上示例 2 输入: “aad”,”ca d” 返回值: true 说明: 因为这里 c 为 0 个,a 被重复一次, _ 表示零个或多个 a。因此可以匹配字符串 “aad”。示例 3 输入: “a”,”.“ 返回值: true 说明: “. “ 表示可匹配零个或多个(’_’)任意字符(’.’)示例 4 输入: “aaab”,”aa a*c” 返回值: false
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 import java.util.*;import java.util.regex.*;public class Solution { public boolean match (String str, String pattern) { return Pattern.matches(pattern, str); } } public class Solution { public boolean match (String str, String pattern) { int n1 = str.length(); int n2 = pattern.length(); boolean [][] dp = new boolean [n1 + 1 ][n2 + 1 ]; for (int i = 0 ; i <= n1; i ++) { for (int j = 0 ; j <= n2; j ++) { if (j == 0 ) { dp[i][j] = i == 0 ? true : false ; } else { if (pattern.charAt(j - 1 ) != '*' ) { if (i > 0 && (str.charAt(i - 1 ) == pattern.charAt(j - 1 ) || pattern.charAt(j - 1 ) == '.' )) { dp[i][j] = dp[i - 1 ][j - 1 ]; } } else { if (j >= 2 ) { dp[i][j] |= dp[i][j - 2 ]; } if (i >= 1 && j >= 2 && (str.charAt(i - 1 ) == pattern.charAt(j - 2 ) || pattern.charAt(j - 2 ) == '.' )) { dp[i][j] |= dp[i - 1 ][j]; } } } } } return dp[n1][n2]; } }
JZ71 跳台阶扩展问题 时间限制: 1 秒 空间限制: 64M 知识点: 动态规划 递归 记忆化搜索
描述
一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级……它也可以跳上 n 级。求该青蛙跳上一个 n 级的台阶(n 为正整数)总共有多少种跳法。
数据范围:1≤n≤20 进阶:空间复杂度 O(1) , 时间复杂度 O(1)
示例 1 输入: 3 返回值: 4示例 2 输入: 1 返回值: 1
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public class Solution { public int jumpFloorII (int n) { if (n == 0 || n == 1 ) return 1 ; int [] f = new int [n+1 ]; f[0 ] = f[1 ] = 1 ; for (int i = 2 ; i <= n; ++ i) { for (int j = 0 ; j < i; ++ j) { f[i] += f[j]; } } return f[n]; } } public class Solution { public int jumpFloorII (int n) { if (n == 0 || n == 1 ) return 1 ; int a = 1 ; int b = 1 ; for (int i = 2 ; i <= n; ++ i) { b = a << 1 ; a = b; } return b; } }; public class Solution { public int jumpFloorII (int n) { if (n == 0 || n == 1 ) return 1 ; return 1 << (n - 1 ); } };
JZ70 矩形覆盖 时间限制: 1 秒 空间限制: 64M 知识点: 递归 动态规划
描述
我们可以用 21 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n 个 2 1 的小矩形无重叠地覆盖一个 2*n 的大矩形,从同一个方向看总共有多少种不同的方法?
数据范围:0≤n≤38 进阶:空间复杂度 O(1),时间复杂度 O(n)
注意:约定 n == 0 时,输出 0
比如 n=3 时,2*3 的矩形块有 3 种不同的覆盖方法(从同一个方向看):
示例 1 输入: 0 返回值: 0示例 2 输入: 1 返回值: 1示例 3 输入: 4 返回值: 5
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Solution { public int rectCover (int target) { if (target <= 2 ) { return target; } int c = 0 ; int a = 1 ; int b = 2 ; for (int i = 3 ; i <= target; i ++) { c = a + b; a = b; b = c; } return c; } }
JZ63 买卖股票的最好时机(一) 时间限制: 1 秒 空间限制: 64M 知识点: 动态规划 贪心
描述
假设你有一个数组 prices,长度为 n,其中 prices[i]
是股票在第 i 天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益 1.你可以买入一次股票和卖出一次股票,并非每天都可以买入或卖出一次,总共只能买入和卖出一次,且买入必须在卖出的前面的某一天 2.如果不能获取到任何利润,请返回 0 3.假设买入卖出均无手续费
数据范围: 0≤n≤10^5, 0≤val≤10^4
要求:空间复杂度 O(1),时间复杂度 O(n)
示例 1 输入: [8,9,2,5,4,7,1]
返回值: 5 说明: 在第 3 天(股票价格 = 2)的时候买入,在第 6 天(股票价格 = 7)的时候卖出,最大利润 = 7-2 = 5 ,不能选择在第 2 天买入,第 3 天卖出,这样就亏损 7 了;同时,你也不能在买入前卖出股票。示例 2 输入: [2,4,1]
返回值: 2示例 3 输入: [3,2,1]
返回值: 0
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import java.util.*;public class Solution { public int maxProfit (int [] prices) { int n = prices.length; int [][] dp = new int [n][2 ]; dp[0 ][0 ] = 0 ; dp[0 ][1 ] = -prices[0 ]; for (int i = 1 ; i < n; i++){ dp[i][0 ] = Math.max(dp[i - 1 ][0 ], dp[i - 1 ][1 ] + prices[i]); dp[i][1 ] = Math.max(dp[i - 1 ][1 ], -prices[i]); } return dp[n - 1 ][0 ]; } }
JZ47 礼物的最大价值 时间限制: 1 秒 空间限制: 64M 知识点: 动态规划 数组
描述
在一个 m * n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物? 如输入这样的一个二维数组,
1 2 3 4 5 [ [1,3,1], [1,5,1], [4,2,1] ]
那么路径 1→3→5→2→1 可以拿到最多价值的礼物,价值为 12
1 2 0<grid.length≤200 0<grid[0].length≤200
示例 1 输入: [[1,3,1],[1,5,1],[4,2,1]]
返回值: 12
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import java.util.*;public class Solution { public int maxValue (int [][] grid) { int row = grid.length; int col = grid[0 ].length; int [][] dp = new int [row + 1 ][col + 1 ]; for (int i = 0 ; i < row; i ++) { for (int j = 0 ; j < col; j ++) { dp[i + 1 ][j + 1 ] = Math.max(dp[i][j + 1 ], dp[i + 1 ][j]) + grid[i][j]; } } return dp[row][col]; } }
JZ48 最长不含重复字符的子字符串 时间限制: 1 秒 空间限制: 64M 知识点: 字符串 哈希 双指针
描述
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。 数据范围: s.length≤40000
示例 1 输入: “abcabcbb” 返回值: 3 说明: 因为无重复字符的最长子串是”abc”,所以其长度为 3。示例 2 输入: “bbbbb” 返回值: 1 说明: 因为无重复字符的最长子串是”b”,所以其长度为 1。示例 3 输入: “pwwkew” 返回值: 3 说明: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。 请注意,你的答案必须是子串的长度,”pwke” 是一个子序列,不是子串。
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 import java.util.*;public class Solution { public int lengthOfLongestSubstring (String s) { Map<Character, Integer> map = new HashMap <>(); int res = 0 ; char [] cs = s.toCharArray(); for (int left = 0 , right = 0 ; right < cs.length; right ++) { map.merge(cs[right], 1 , Integer::sum); while (map.get(cs[right]) > 1 ) { map.merge(cs[left ++], -1 , Integer::sum); } res = Math.max(res, right - left + 1 ); } return res; } } public class Solution { public int lengthOfLongestSubstring (String s) { HashMap<Character, Integer> mp = new HashMap <>(); int res = 0 ; int [] dp = new int [s.length() + 1 ]; for (int i = 1 ; i <= s.length(); i++){ dp[i] = 1 ; if (!mp.containsKey(s.charAt(i - 1 ))) dp[i] = dp[i - 1 ] + 1 ; else dp[i] = Math.min(dp[i - 1 ] + 1 , i - mp.get(s.charAt(i - 1 ))); mp.put(s.charAt(i - 1 ), i); res = Math.max(res, dp[i]); } return res; } }
JZ46 把数字翻译成字符串 时间限制: 1 秒 空间限制: 64M 知识点: 动态规划
描述
有一种将字母编码成数字的方式:’a’->1, ‘b->2’, … , ‘z->26’。
现在给一串数字,返回有多少种可能的译码结果
数据范围:字符串长度满足 0<n≤90 进阶:空间复杂度 O(n),时间复杂度 O(n)
示例 1 输入: “12” 返回值: 2 说明: 2 种可能的译码结果(”ab” 或”l”)示例 2 输入: “31717126241541717” 返回值: 192 说明: 192 种可能的译码结果
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import java.util.*;public class Solution { public int solve (String nums) { if (nums.equals("0" )) { return 0 ; } int [] dp = new int [nums.length() + 1 ]; Arrays.fill(dp, 1 ); for (int i = 2 ; i <= nums.length(); i ++) { if (nums.charAt(i - 1 ) == '0' && nums.charAt(i - 2 ) != '1' && nums.charAt(i - 2 ) != '2' ) { return 0 ; } else if ((nums.charAt(i - 2 ) == '1' && nums.charAt(i - 1 ) != '0' ) || (nums.charAt(i - 2 ) == '2' && nums.charAt(i - 1 ) > '0' && nums.charAt(i - 1 ) < '7' )) { dp[i] = dp[i - 1 ] + dp[i - 2 ]; } else { dp[i] = dp[i - 1 ]; } } return dp[nums.length()]; } }
回溯 JZ12 矩阵中的路径 时间限制: 1 秒 空间限制: 64M 知识点: dfs
描述
请设计一个函数,用来判断在一个 n 乘 m 的矩阵中是否存在一条包含某长度为 len 的字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 a b c e s f c s a d e e 矩阵中包含一条字符串”bcced”的路径,但是矩阵中不包含”abcb”路径,因为字符串的第一个字符 b 占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。 数据范围:0≤n,m≤20, 1≤len≤25
示例 1 输入: [[a,b,c,e],[s,f,c,s],[a,d,e,e]],"abcced"
返回值: true示例 2 输入: [[a,b,c,e],[s,f,c,s],[a,d,e,e]],"abcb"
返回值:false
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 import java.util.*;public class Solution { public boolean hasPath (char [][] matrix, String word) { int row = matrix.length; if (row <= 0 ) { return false ; } int col = matrix[0 ].length; if (col <= 0 ) { return false ; } for (int i = 0 ; i < matrix.length; i ++) { for (int j = 0 ; j < matrix[0 ].length; j ++) { boolean [][] path = new boolean [row][col]; boolean find = findPath(matrix, word, path, 0 , i, j); if (find) { return true ; } } } return false ; } private boolean findPath (char [][] matrix, String word, boolean [][] path, int start, int i, int j) { if (start >= word.length()) { return true ; } if (i < 0 || i >= matrix.length || j < 0 || j >= matrix[0 ].length) { return false ; } char c = word.charAt(start); if (path[i][j] || matrix[i][j] != c) { return false ; } path[i][j] = true ; boolean find = findPath(matrix, word, path, start + 1 , i, j - 1 ) || findPath(matrix, word, path, start + 1 , i, j + 1 ) || findPath(matrix, word, path, start + 1 , i - 1 , j) || findPath(matrix, word, path, start + 1 , i + 1 , j); if (!find) { path[i][j] = false ; } return find; } }
JZ13 机器人的运动范围 时间限制: 1 秒 空间限制: 64M 知识点: 递归
描述
地上有一个 rows 行和 cols 列的方格。坐标从 [0,0]
到 [rows-1,cols-1]
。一个机器人从坐标 [0,0]
的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于 threshold 的格子。 例如,当 threshold 为 18 时,机器人能够进入方格 [35,37]
,因为 3+5+3+7 = 18。但是,它不能进入方格 [35,38]
,因为 3+5+3+8 = 19 。请问该机器人能够达到多少个格子?
数据范围:0≤threshold≤15, 1≤rows,cols≤100
进阶:空间复杂度 O(nm) , 时间复杂度 O(nm)
示例 1 输入: 1,2,3 返回值: 3示例 2 输入: 0,1,3 返回值: 1示例 3 输入: 10,1,100 返回值: 29 说明: [0,0],[0,1],[0,2],[0,3],[0,4],[0,5],[0,6],[0,7],[0,8],[0,9],[0,10],[0,11],[0,12],[0,13],[0,14],[0,15],[0,16],[0,17],[0,18],[0,19],[0,20],[0,21],[0,22],[0,23],[0,24],[0,25],[0,26],[0,27],[0,28] 这29种,后面的[0,29],[0,30]以及[0,31]等等是无法到达的
示例 4 输入: 5,10,10 返回值: 21
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public class Solution { private int count = 0 ; private int [][] dxy = new int [][] {{-1 , 0 }, {0 , 1 }, {1 , 0 }, {0 , -1 }}; public int movingCount (int threshold, int rows, int cols) { boolean [][] path = new boolean [rows][cols]; dfs(threshold, rows, cols, path, 0 , 0 ); return count; } private void dfs (int theshold, int rows, int cols, boolean [][] path, int i, int j) { if (i < 0 || i >= rows || j < 0 || j >= cols) { return ; } if (path[i][j]) { return ; } if (!canEnter(theshold, i, j)) { return ; } path[i][j] = true ; count ++; for (int k = 0 ; k < 4 ; k ++) { dfs(theshold, rows, cols, path, i + dxy[k][0 ], j + dxy[k][1 ]); } } private boolean canEnter (int theshold, int row, int col) { return getBitSum(row) + getBitSum(col) <= theshold; } private int getBitSum (int num) { int res = 0 ; while (num > 0 ) { res += num % 10 ; num /= 10 ; } return res; } }
排序 JZ3 数组中重复的数字 时间限制: 1 秒 空间限制: 64M 知识点: 数组
描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组[2,3,1,0,2,5,3]
,那么对应的输出是2或者3。存在不合法的输入的话输出-1
数据范围:0≤n≤10000 进阶:时间复杂度 O(n) ,空间复杂度 O(n)
示例 1 输入: [2,3,1,0,2,5,3]
返回值: 2 说明: 2或3都是对的
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import java.util.*;public class Solution { public int duplicate (int [] numbers) { Map<Integer, Integer> tmp = new HashMap <>(); for (int num : numbers) { int count = tmp.merge(num, 1 , Integer::sum); if (count > 1 ) { return num; } } return -1 ; } }
JZ51 数组中的逆序对 时间限制: 1 秒 空间限制: 64M 知识点: 数组
描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P mod 1000000007
数据范围:对于 50% 的数据, size≤10^4 对于 100% 的数据, size≤10^5 数组中所有数字的值满足 0≤val≤10^9
要求:空间复杂度 O(n),时间复杂度 O(nlogn)
输入描述:题目保证输入的数组中没有的相同的数字
示例 1 输入: [1,2,3,4,5,6,7,0]
返回值: 7示例 2 输入: [1,2,3]
返回值: 0
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 public class Solution { int count = 0 ; public int InversePairs (int [] array) { if (array.length < 2 ) return 0 ; mergeSort(array, 0 , array.length - 1 ); return count; } public void mergeSort (int [] array, int left, int right) { int mid = left + (right - left) / 2 ; if (left < right) { mergeSort(array, left, mid); mergeSort(array, mid + 1 , right); merge(array, left, mid, right); } } public void merge (int [] array, int left, int mid, int right) { int [] arr = new int [right - left + 1 ]; int c = 0 ; int s = left; int l = left; int r = mid + 1 ; while (l <= mid && r <= right) { if (array[l] <= array[r]) { arr[c++] = array[l++]; } else { arr[c++] = array[r++]; count += mid + 1 - l; count %= 1000000007 ; } } while (l <= mid) arr[c++] = array[l++]; while (r <= right) arr[c++] = array[r++]; for (int num : arr) { array[s++] = num; } } } public class Solution { public int InversePairs2 (int [] array) { long res = 0 ; int length = array.length; for (int start = length - 2 ; start >= 0 ; start --) { int dp = 0 ; for (int i = start + 1 ; i < length; i ++) { if (array[start] > array[i]) { dp ++; } } res += dp; } return (int ) (res % 1000000007 ); } }
JZ40 最小的K个数 时间限制: 1 秒 空间限制: 64M 知识点: 堆 排序 分治
描述
给定一个长度为 n 的可能有重复值的数组,找出其中不去重的最小的 k 个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4(任意顺序皆可)。 数据范围:0≤k,n≤10000,数组中每个数的大小 0≤val≤1000 要求:空间复杂度 O(n) ,时间复杂度 O(nlogk)
示例 1 输入: [4,5,1,6,2,7,3,8],4
返回值: [1,2,3,4]
说明: 返回最小的4个数即可,返回 [1,3,2,4]
也可以示例 2 输入: [1],0
返回值: []
示例 3 输入: [0,1,2,1,2],3
返回值: [0,1,1]
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import java.util.*;public class Solution { public ArrayList<Integer> GetLeastNumbers_Solution (int [] input, int k) { PriorityQueue<Integer> queue = new PriorityQueue <>((n1, n2) -> n2 - n1); for (int num : input) { queue.offer(num); if (queue.size() > k) { queue.poll(); } } return new ArrayList <>(queue); } public ArrayList<Integer> GetLeastNumbers_Solution (int [] input, int k) { if (k <= 0 ) { return new ArrayList <>(); } PriorityQueue<Integer> queue = new PriorityQueue <>((n1, n2) -> n2 - n1); for (int num : input) { if (queue.size() < k) { queue.offer(num); } else if (num < queue.peek()) { queue.poll(); queue.offer(num); } } return new ArrayList <>(queue); } }
JZ41 数据流中的中位数 时间限制: 1 秒 空间限制: 64M 知识点: 排序 堆
描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
数据范围:数据流中数个数满足 1≤n≤1000, 大小满足 1≤val≤1000 进阶: 空间复杂度 O(n), 时间复杂度 O(nlogn)
示例 1 输入: [5,2,3,4,1,6,7,0,8]
返回值: “5.00 3.50 3.00 3.50 3.00 3.50 4.00 3.50 4.00 “ 说明: 数据流里面不断吐出的是5,2,3…,则得到的平均数分别为5,(5+2)/2,3…示例 2 输入: [1,1,1]
返回值: “1.00 1.00 1.00 “
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 import java.util.*;public class Solution { private List<Integer> val = new ArrayList <Integer>(); public void Insert (Integer num) { int i = 0 ; for (; i < val.size(); i++) { if (num <= val.get(i)) { break ; } } val.add(i, num); } public Double GetMedian () { int n = val.size(); if (n % 2 == 1 ) { return (double ) val.get(n / 2 ); } else { return (val.get(n / 2 ) + val.get(n / 2 - 1 )) / 2.0 ; } } } import java.util.*;public class Solution { private PriorityQueue<Integer> max = new PriorityQueue <>(); private PriorityQueue<Integer> min = new PriorityQueue <>((o1, o2) -> o2.compareTo(o1)); public void Insert (Integer num) { min.offer(num); max.offer(min.poll()); if (min.size() < max.size()) { min.offer(max.poll()); } } public Double GetMedian () { if (min.size() > max.size()) { return (double ) min.peek(); } else { return (double ) (min.peek() + max.peek()) / 2 ; } } }
位运算 JZ65 不用加减乘除做加法 时间限制: 1 秒 空间限制: 64M 知识点: 基础数学
描述
写一个函数,求两个整数之和,要求在函数体内不得使用 +、-、*、/ 四则运算符号。
数据范围:两个数都满足 −10≤n≤1000 进阶:空间复杂度 O(1),时间复杂度 O(1)
示例 1 输入: 1,2 返回值: 3示例 2 输入: 0,0 返回值: 0
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Solution { public int Add (int num1,int num2) { while (num2 != 0 ) { int carry = (num1 & num2) << 1 ; num1 = num1 ^ num2; num2 = carry; } return num1; } }
JZ15 二进制中 1 的个数 时间限制: 1 秒 空间限制: 64M 知识点: 基础数学
描述
输入一个整数 n ,输出该数 32 位二进制表示中 1 的个数。其中负数用补码表示。
数据范围:−231 <=n<=231 −1 即范围为: −2147483648<=n<=2147483647
示例 1 输入: 10 返回值: 2 说明: 十进制中 10 的 32 位二进制表示为 0000 0000 0000 0000 0000 0000 0000 1010,其中有两个 1。示例 2 输入: -1 返回值: 32 说明: 负数使用补码表示,-1 的 32 位二进制表示为 1111 1111 1111 1111 1111 1111 1111 1111,其中 32 个 1
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class Solution { public int NumberOf1 (int n) { return solution1(n); } public int solution1 (int n) { return Integer.bitCount(n); } public int solution2 (int n) { int num = 0 ; while (n != 0 ) { num ++; n &= (n - 1 ); } return num; } public int solution3 (int n) { int num = 0 ; int mark = 1 ; while (mark != 0 ) { if ((n & mark) != 0 ) { num ++; } mark <<= 1 ; } return num; } }
JZ16 数值的整数次方 时间限制: 1 秒 空间限制: 64M 知识点: 基础数学
描述
实现函数 double Power(double base, int exponent),求 base 的 exponent 次方。
注意: 1.保证 base 和 exponent 不同时为 0。 2.不得使用库函数,同时不需要考虑大数问题 3.有特殊判题,不用考虑小数点后面 0 的位数。
数据范围:|base|≤100,|exponent|≤100,保证最终结果一定满足 |val|≤104 进阶:空间复杂度 O(1),时间复杂度 O(n)
示例 1 输入: 2.00000,3 返回值: 8.00000示例 2 输入: 2.10000,3 返回值: 9.26100示例 3 输入: 2.00000,-2 返回值: 0.25000 说明: 2 的-2 次方等于 1/4=0.25
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 public class Solution { public double Power (double b, int n) { if (n < 0 ) { b = 1 / b; n = -n; } double ret = 1.0 ; for (int i=0 ; i<n; ++i) { ret *= b; } return ret; } } public class Solution { public double Power (double b, int n) { if (n < 0 ) { b = 1 / b; n = -n; } return q_power(b, n); } public double q_power (double b, int n) { if (n == 0 ) return 1.0 ; double ret = q_power(b, n/2 ); if ((n & 1 ) == 1 ) { return ret * ret * b; } else { return ret * ret; } } } public class Solution { double Power (double b, int n) { if (n < 0 ) { b = 1 / b; n = -n; } double x = b; double ret = 1.0 ; while (n != 0 ) { if ((n & 1 ) == 1 ) { ret *= x; } x *= x; n >>= 1 ; } return ret; } }
JZ56 时间限制: 1 秒 空间限制: 64M 知识点: 位运算 哈希
描述
一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
数据范围:数组长度 2≤n≤1000
,数组中每个数的大小 0<val≤1000000
要求:空间复杂度 O(1),时间复杂度 O(n)
提示:输出时按非降序排列。
示例 1 输入: [1,4,1,6]
返回值: [4,6]
说明: 返回的结果中较小的数排在前面示例 2 输入: [1,2,3,3,2,9]
返回值: [1,9]
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import java.util.*;public class Solution { public int [] FindNumsAppearOnce (int [] array) { int xorResult = 0 ; for (int num : array) { xorResult ^= num; } int bit = 1 ; while ((bit & xorResult) == 0 ) { bit <<= 1 ; } int a = 0 , b = 0 ; for (int num : array) { if ((num & bit) == 0 ) { a ^= num; } else { b ^= num; } } return a < b ? new int [] {a, b} : new int [] {b, a}; } }
JZ64 求1+2+3+…+n 时间限制: 1 秒 空间限制: 64M 知识点: 基础数学
描述
求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
数据范围: 0<n≤200
进阶: 空间复杂度 O(1) ,时间复杂度 O(n)
示例 1 输入: 5 返回值: 15示例 2 输入: 1 返回值: 1
答案
1 2 3 4 5 6 7 public class Solution { public int Sum_Solution (int n) { boolean flag = (n > 1 ) && ((n += Sum_Solution(n - 1 )) > 0 ); return n; } }
模拟 JZ29 顺时针打印矩阵 时间限制: 1 秒 空间限制: 64M 知识点: 数组
描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵:
1 2 3 4 [[1,2,3,4], [5,6,7,8], [9,10,11,12], [13,14,15,16]]
则依次打印出数字[1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10]
数据范围:0 <= matrix.length <= 100
0 <= matrix[i].length <= 100
示例 1 输入: [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]
返回值: [1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10]
示例 2 输入: [[1,2,3,1],[4,5,6,1],[4,5,6,1]]
返回值: [1,2,3,1,1,1,6,5,4,4,5,6]
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import java.util.ArrayList;public class Solution { public ArrayList<Integer> printMatrix (int [][] matrix) { if (matrix == null || matrix.length == 0 || matrix[0 ].length == 0 ) { return new ArrayList <>(); } int m = matrix.length; int n = matrix[0 ].length; int left = 0 , right = n - 1 , top = 0 , bottom = m - 1 ; ArrayList<Integer> res = new ArrayList <>(); while (left <= right && top <= bottom) { for (int j = left; j <= right; j++) { res.add(matrix[top][j]); } for (int i = top + 1 ; i <= bottom; i++) { res.add(matrix[i][right]); } if (left < right && top < bottom) { for (int j = right - 1 ; j >= left; j--) { res.add(matrix[bottom][j]); } for (int i = bottom - 1 ; i > top; i--) { res.add(matrix[i][left]); } } left++; right--; top++; bottom--; } return res; } }
JZ61 扑克牌顺子 时间限制: 1 秒 空间限制: 64M 知识点: 模拟
描述
现在有2副扑克牌,从扑克牌中随机五张扑克牌,我们需要来判断一下是不是顺子。 有如下规则:
A为1,J为11,Q为12,K为13,A不能视为14
大、小王为 0,0可以看作任意牌
如果给出的五张牌能组成顺子(即这五张牌是连续的)就输出true,否则就输出false。
数据保证每组5个数字,每组最多含有4个零,数组的数取值为 [0, 13]
要求:空间复杂度 O(1),时间复杂度 O(nlogn),本题也有时间复杂度 O(n) 的解法 输入描述: 输入五张扑克牌的值 返回值描述: 五张扑克牌能否组成顺子。
示例 1 输入: [6,0,2,0,4]
返回值: true 说明: 中间的两个0一个看作3,一个看作5 。即:[6,3,2,5,4]
这样这五张牌在 [2,6]
区间连续,输出true示例 2 输入: [0,3,2,6,4]
返回值: true示例 3 输入: [1,0,0,1,0]
返回值: false示例 4 输入: [13,12,11,0,1] 返回值: false
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import java.util.*;public class Solution { public boolean IsContinuous (int [] numbers) { Arrays.sort(numbers); int n = numbers.length; int min = numbers[0 ]; int max = numbers[n - 1 ]; int p = 0 ; while (min == 0 && p < n) { p ++; min = numbers[p]; } int zeroNum = p; int gap = max - min - (n - p) + 1 ; int last = min; for (int i = p + 1 ; i < n; i++) { if (numbers[i] == last) { return false ; } last = numbers[i]; } return zeroNum >= gap; } }
JZ67 把字符串转换成整数(atoi) 时间限制: 1 秒 空间限制: 64M 知识点: 字符串
描述
写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。传入的字符串可能有以下部分组成:
若干空格
(可选)一个符号字符(’+’ 或 ‘-‘)
数字,字母,符号,空格组成的字符串表达式
若干空格
转换算法如下:
去掉无用的前导空格
第一个非空字符为+或者-号时,作为该整数的正负号,如果没有符号,默认为正数
判断整数的有效部分:
确定符号位之后,与之后面尽可能多的连续数字组合起来成为有效整数数字,如果没有有效的整数部分,那么直接返回0
将字符串前面的整数部分取出,后面可能会存在存在多余的字符(字母,符号,空格等),这些字符可以被忽略,它们对于函数不应该造成影响
整数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231的整数应该被调整为 −231 ,大于 231 − 1 的整数应该被调整为 231 − 1
去掉无用的后导空格
数据范围: 1.0 <=字符串长度<= 100 2.字符串由英文字母(大写和小写)、数字(0-9)、’ ‘、’+’、’-‘ 和 ‘.’ 组成
示例 1 输入: “82” 返回值: 82示例 2 输入: “ -12 “ 返回值: -12 说明: 去掉前后的空格,为-12示例 3 输入: “4396 clearlove” 返回值: 4396 说明: 6后面的字符不属于有效的整数部分,去除,但是返回前面提取的有效部分示例 4 输入: “clearlove 4396” 返回值: 0示例 5 输入: “-987654321111” 返回值: -2147483648
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import java.util.*;public class Solution { public int StrToInt (String str) { if (str == null || str.length() == 0 ) { return 0 ; } int index = 0 ; int sign = 1 ; int result = 0 ; while (index < str.length() && str.charAt(index) == ' ' ) { index++; } if (index < str.length() && (str.charAt(index) == '+' || str.charAt(index) == '-' )) { sign = (str.charAt(index) == '-' ) ? -1 : 1 ; index++; } while (index < str.length()) { char c = str.charAt(index); if (c < '0' || c > '9' ) { break ; } int digit = c - '0' ; if (result > Integer.MAX_VALUE / 10 || (result == Integer.MAX_VALUE / 10 && digit > Integer.MAX_VALUE % 10 )) { return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE; } result = result * 10 + digit; index++; } return result * sign; } }
JZ20 表示数值的字符串 时间限制: 1 秒 空间限制: 64M 知识点: 字符串
描述
请实现一个函数用来判断字符串str是否表示数值(包括科学计数法的数字,小数和整数)。
科学计数法的数字(按顺序)可以分成以下几个部分:
若干空格
一个整数或者小数
(可选)一个 ‘e’ 或 ‘E’ ,后面跟着一个整数(可正可负)
若干空格
小数(按顺序)可以分成以下几个部分:
若干空格
(可选)一个符号字符(’+’ 或 ‘-‘)
可能是以下描述格式之一:
至少一位数字,后面跟着一个点 ‘.’
至少一位数字,后面跟着一个点 ‘.’ ,后面再跟着至少一位数字
一个点 ‘.’ ,后面跟着至少一位数字
若干空格
整数(按顺序)可以分成以下几个部分:
若干空格
(可选)一个符号字符(’+’ 或 ‘-‘)
至少一位数字
若干空格
例如,字符串 ["+100","5e2","-123","3.1416","-1E-16"]
都表示数值。 但是 ["12e","1a3.14","1.2.3","+-5","12e+4.3"]
都不是数值。
提示: 1.1 <= str.length <= 25 2.str 仅含英文字母(大写和小写),数字(0-9),加号 ‘+’ ,减号 ‘-‘ ,空格 ‘ ‘ 或者点 ‘.’ 。 3.如果怀疑用例是不是能表示为数值的,可以使用python的print(float(str))去查看 进阶:时间复杂度 O(n) ,空间复杂度 O(n)
示例 1 输入: “123.45e+6” 返回值: true示例 2 输入: “1.2.3” 返回值: false 说明:示例 3 输入: “.” 返回值: false示例 4 输入: “ .2 “ 返回值: true
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 import java.util.*;import java.util.regex.Pattern;public class Solution { public boolean isNumeric (String str) { String p = "^[+-]?(\\d*\\.\\d+|\\d+(\\.\\d*)?)(?:[eE][+-]?\\d+)?$" ; return Pattern.matches(p, str.trim()); } } import java.util.*;public class Solution { public boolean isNumeric (String str) { if (str == null || str.length() == 0 ) { return false ; } char [] cs = str.toCharArray(); int index = 0 ; int len = cs.length; while (index < len && cs[index] == ' ' ) { index++; } if (index < len && (cs[index] == '+' || cs[index] == '-' )) { index++; } boolean isNum = false ; while (index < len && Character.isDigit(cs[index])) { index++; isNum = true ; } if (index < len && cs[index] == '.' ) { index++; while (index < len && Character.isDigit(cs[index])) { index++; isNum = true ; } } if (isNum && index < len && (cs[index] == 'e' || cs[index] == 'E' )) { index++; if (index < len && (cs[index] == '+' || cs[index] == '-' )) { index++; } if (index < len && Character.isDigit(cs[index])) { while (index < len && Character.isDigit(cs[index])) { index++; } } else { return false ; } } while (index < len && cs[index] == ' ' ) { index++; } return isNum && index == len; } }
其他算法 JZ66 构建乘积数组 时间限制: 1 秒 空间限制: 64M 知识点: 数组
描述
给定一个数组 A[0,1,...,n-1]
,请构建一个数组 B[0,1,...,n-1]
,其中 B 的元素 B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]
(除 A[i]
以外的全部元素的的乘积)。程序中不能使用除法。(注意:规定 B[0] = A[1] * A[2] * ... * A[n-1],B[n-1] = A[0] * A[1] * ... * A[n-2]
) 对于 A 长度为 1 的情况,B 无意义,故而无法构建,用例中不包括这种情况。
数据范围: 1≤n≤10 ,数组中元素满足 ∣val∣≤10
示例 1 输入: [1,2,3,4,5]
返回值: [120,60,40,30,24]
示例 2 输入: [100,50]
返回值: [50,100]
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import java.util.ArrayList;public class Solution { public int [] multiply(int [] A) { int [] B = new int [A.length]; B[0 ] = 1 ; for (int i = 1 ; i < A.length; i++) { B[i] = B[i - 1 ] * A[i - 1 ]; } int temp = 1 ; for (int i = A.length - 1 ; i >= 0 ; i--) { B[i] *= temp; temp *= A[i]; } return B; } }
JZ50 第一个只出现一次的字符 时间限制: 1 秒 空间限制: 64M 知识点: 字符串
描述
在一个长为 字符串中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)
数据范围: 0≤n≤10000,且字符串只有字母组成。 要求:空间复杂度 O(n),时间复杂度 O(n)
示例 1 输入: “google” 返回值: 4示例 2 输入: “aa” 返回值: -1
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import java.util.*;public class Solution { public int FirstNotRepeatingChar (String str) { HashMap<Character, Integer> mp = new HashMap <>(); for (int i = 0 ; i < str.length(); i++) { mp.put(str.charAt(i), mp.getOrDefault(str.charAt(i), 0 ) + 1 ); } for (int i = 0 ; i < str.length(); i++) { if (mp.get(str.charAt(i)) == 1 ) { return i; } } return -1 ; } }
JZ5 替换空格 时间限制: 1 秒 空间限制: 64M 知识点: 字符串
描述
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为 We Are Happy.则经过替换之后的字符串为 We%20Are%20Happy。
数据范围: 0≤len(s)≤1000 。保证字符串中的字符为大写英文字母、小写英文字母和空格中的一种。
示例 1 输入: “We Are Happy” 返回值: “We%20Are%20Happy”示例 2 输入: “ “ 返回值: “%20”
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import java.util.*;public class Solution { public String replaceSpace (String s) { return s.replaceAll(" " , "%20" ); } } import java.util.*;public class Solution { public String replaceSpace (String s) { StringBuilder stringBuilder = new StringBuilder (); for (int i = 0 ; i < s.length(); i++) { if (s.charAt(i) == ' ' ) stringBuilder.append("%20" ); else stringBuilder.append(s.charAt(i)); } return stringBuilder.toString(); } }
JZ21 调整数组顺序使奇数位于偶数前面(一) 时间限制: 1 秒 空间限制: 64M 知识点: 数组
描述
输入一个长度为 n 整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前面部分,所有的偶数位于数组的后面部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
数据范围:0≤n≤5000,数组中每个数的值 0≤val≤10000 要求:时间复杂度 O(n),空间复杂度 O(n) 进阶:时间复杂度 O(n2 ),空间复杂度 O(1)
示例 1 输入: [1,2,3,4]
返回值: [1,3,2,4]
示例 2 输入: [2,4,6,5,7]
返回值: [5,7,2,4,6]
示例 3 输入: [1,3,5,6,7]
返回值: [1,3,5,7,6]
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 public class Solution { public int [] reOrderArray(int [] array) { return Arrays.stream(array).boxed().sorted(Comparator.comparing(Integer::intValue, (a, b) -> { if (a % 2 == 1 && b % 2 == 0 ) { return -1 ; } else if (a % 2 == 0 && b % 2 == 1 ) { return 1 ; } else { return 0 ; } })).mapToInt(Integer::intValue).toArray(); } } public class Solution { public int [] reOrderArray(int [] array) { if (array == null || array.length == 0 ) { return array; } int j = 0 ; int temp = 0 ; for (int i = 0 ; i < array.length; i ++){ temp = array[i]; if (array[i] % 2 == 0 ){ continue ; } else { int k = i; while (k > j) { array[k] = array[k - 1 ]; k --; } array[k] = temp; j ++; } } return array; } } public class Solution { public int [] reOrderArray(int [] array) { int len = array.length; int [] num = new int [len]; int left = 0 ; int right = len - 1 ; for (int numLeft = left, numRight = right; left < len || right >= 0 ; left ++, right --) { if (array[left] % 2 == 1 ) { num[numLeft] = array[left]; numLeft++; } if (array[right] % 2 == 0 ) { num[numRight] = array[right]; numRight--; } } return num; } }
JZ39 数组中出现次数超过一半的数字 时间限制: 1 秒 空间限制: 64M 知识点: 哈希 数组
描述
给一个长度为 n 的数组,数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 例如输入一个长度为9的数组 [1,2,3,2,2,2,5,4,2]
。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。
数据范围: n≤50000,数组中元素的值 0≤val≤10000 要求:空间复杂度: O(1),时间复杂度 O(n)
示例 1 输入: [1,2,3,2,2,2,5,4,2]
返回值: 2示例 2 输入: [3,3,3,3,2,2,2]
返回值: 3示例 3 输入: [1]
返回值: 1
答案
1 2 3 4 5 6 7 8 9 10 import java.util.*;public class Solution { public int MoreThanHalfNum_Solution (int [] array) { Map<Integer, Integer> map = new HashMap <>(); for (int num : array) { map.merge(num, 1 , Integer::sum); } return map.entrySet().stream().max(Comparator.comparingInt(e -> e.getValue())).get().getKey(); } }
JZ43 整数中1出现的次数(从1到n整数中1出现的次数) 时间限制: 1 秒 空间限制: 64M 知识点: 基础数学
描述
输入一个整数 n ,求 1~n 这 n 个整数的十进制表示中 1 出现的次数 例如, 1~13 中包含 1 的数字有 1 、 10 、 11 、 12 、 13 因此共出现 6 次
注意:11 这种情况算两次
数据范围: 1≤n≤30000 进阶:空间复杂度 O(1) ,时间复杂度 O(lognn)
示例 1 输入: 13 返回值: 6示例 2 输入: 0 返回值: 0
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class Solution { public int NumberOf1Between1AndN_Solution (int n) { int count = 0 , bitNum = 1 , high = n / 10 , cur = n % 10 , low = 0 ; while (cur != 0 || high != 0 ) { if (cur < 1 ) { count += high * bitNum; } else if (cur == 1 ) { count += high * bitNum + low + 1 ; } else { count += (high + 1 ) * bitNum; } low += cur * bitNum; cur = high % 10 ; high = high / 10 ; bitNum = bitNum * 10 ; } return count; } }
JZ45 把数组排成最小的数 时间限制: 1 秒 空间限制: 64M 知识点: 数组 贪心 排序
描述
输入一个非负整数数组numbers,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。 例如输入数组 [3,32,321]
,则打印出这三个数字能排成的最小数字为321323。
输出结果可能非常大,所以你需要返回一个字符串而不是整数
拼接起来的数字可能会有前导 0,最后结果不需要去掉前导 0
数据范围: 0<=len(numbers)<=100
示例 1 输入: [11,3]
返回值: “113”示例 2 输入: []
返回值: “”示例 3 输入: [3,32,321]
返回值: “321323”
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import java.util.*;public class Solution { public String PrintMinNumber (int [] numbers) { if (numbers == null || numbers.length == 0 ) { return "" ; } String[] nums = new String [numbers.length]; for (int i = 0 ; i < numbers.length; i++) { nums[i] = String.valueOf(numbers[i]); } Comparator<String> comparator = new Comparator <String>() { @Override public int compare (String s1, String s2) { String order1 = s1 + s2; String order2 = s2 + s1; return order1.compareTo(order2); } }; Arrays.sort(nums, comparator); StringBuilder sb = new StringBuilder (); for (String num : nums) { sb.append(num); } return sb.toString(); } }
JZ49 丑数 时间限制: 1 秒 空间限制: 64M 知识点: 基础数学 二分
描述
把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第 n个丑数。
数据范围: 0≤n≤2000 要求:空间复杂度 O(n) , 时间复杂度 O(n)
示例 1 输入: 7 返回值: 8
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Solution { public int GetUglyNumber_Solution (int index) { if (index <= 6 ) { return index; } int i2 = 0 , i3 = 0 , i5 = 0 ; int [] res = new int [index]; res[0 ] = 1 ; for (int i = 1 ; i < index; i++) { res[i] = Math.min(res[i2] * 2 , Math.min(res[i3] * 3 , res[i5] * 5 )); if (res[i] == res[i2] * 2 ) i2++; if (res[i] == res[i3] * 3 ) i3++; if (res[i] == res[i5] * 5 ) i5++; } return res[index - 1 ]; } }
JZ74 和为S的连续正数序列 时间限制: 1 秒 空间限制: 64M 知识点: 穷举
描述
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列?
数据范围: 0<n≤100 进阶:时间复杂度 O(n) 返回值描述: 输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序
示例 1 输入: 9 返回值: [[2,3,4],[4,5]]
示例 2 输入: 0 返回值: []
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import java.util.*;public class Solution { public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) { ArrayList<ArrayList<Integer>> res = new ArrayList <>(); LinkedList<Integer> track = new LinkedList <>(); int nSum = 0 ; for (int i = 1 ; i <= sum / 2 + 3 ; i++) { if (nSum == sum && track.size() > 1 ) { res.add(new ArrayList <>(track)); } else if (nSum > sum) { while (nSum >= sum) { int num = track.removeFirst(); nSum -= num; if (nSum == sum && track.size() > 1 ) { res.add(new ArrayList <>(track)); } } } track.add(i); nSum += i; } return res; } }
JZ57 和为S的两个数字 时间限制: 1 秒 空间限制: 64M 知识点: 数学 数组 双指针
描述
输入一个升序数组 array 和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,返回任意一组即可,如果无法找出这样的数字,返回一个空数组即可。
数据范围: 0≤len(array)≤10^5, 1≤array[i]≤10^6
示例 1 输入: [1,2,4,7,11,15],15
返回值: [4,11]
说明: 返回 [4,11]
或者 [11,4]
都是可以的示例 2 输入: [1,5,11],10
返回值: []
说明: 不存在,返回空数组示例 3 输入: [1,2,3,4],5
返回值: [1,4]
说明: 返回 [1,4],[4,1],[2,3],[3,2]
都是可以的示例 4 输入: [1,2,2,4],4
返回值: [2,2]
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import java.util.ArrayList;public class Solution { public ArrayList<Integer> FindNumbersWithSum (int [] array,int sum) { ArrayList<Integer> res = new ArrayList <>(); if (array.length <= 1 || sum < 1 ) { return res; } int left = 0 ; int right = array.length - 1 ; while (left < right) { int leftNum = array[left]; int rightNum = array[right]; int nSum = leftNum + rightNum; if (nSum == sum) { res.add(leftNum); res.add(rightNum); break ; } else if (nSum > sum) { right --; } else { left ++; } } return res; } }
JZ58 左旋转字符串 时间限制: 1 秒 空间限制: 64M 知识点: 字符串
描述
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列 S ,请你把其循环左移 K 位后的序列输出。例如,字符序列 S = ”abcXYZdef” , 要求输出循环左移 3 位后的结果,即 “XYZdefabc”
数据范围:输入的字符串长度满足 0≤len≤100, 0≤n≤100 进阶:空间复杂度 O(n) ,时间复杂度 O(n)
示例 1 输入: “abcXYZdef”,3 返回值: “XYZdefabc”示例 2 输入: “aab”,10 返回值: “aba”
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Solution { public String LeftRotateString (String str,int n) { if (str == null || str.length() <= 1 ) { return str; } int rotateNum = n % str.length(); if (rotateNum == 0 ) { return str; } return str.substring(rotateNum) + str.substring(0 , rotateNum); } }
JZ62 孩子们的游戏(圆圈中最后剩下的数) 时间限制: 1 秒 空间限制: 64M 知识点: 基础数学
描述
每年六一儿童节,牛客都会准备一些小礼物和小游戏去看望孤儿院的孩子们。其中,有个游戏是这样的:首先,让 n 个小朋友们围成一个大圈,小朋友们的编号是0~n-1。然后,随机指定一个数 m ,让编号为0的小朋友开始报数。每次喊到 m-1 的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0… m-1报数….这样下去….直到剩下最后一个小朋友,可以不用表演,并且拿到牛客礼品,请你试着想下,哪个小朋友会得到这份礼品呢?
数据范围: 1≤m≤10000 要求:空间复杂度 O(1),时间复杂度 O(n)
示例 1 输入: 5,3 返回值: 3示例 2 输入: 2,3 返回值: 1 说明: 有2个小朋友编号为0,1,第一次报数报到3的是0号小朋友,0号小朋友出圈,1号小朋友得到礼物示例 3 输入: 10,17 返回值: 2
答案
1 2 3 4 5 6 7 8 9 10 11 12 public int LastRemaining_Solution (int n, int m) { if (n <= 0 || m <= 0 ) { return -1 ; } int lastRemaining = 0 ; for (int i = 2 ; i <= n; i++) { lastRemaining = (lastRemaining + m) % i; } return lastRemaining; }
JZ75 字符流中第一个不重复的字符 时间限制: 1 秒 空间限制: 64M 知识点: 字符串
描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符 “go” 时,第一个只出现一次的字符是 “g” 。当从该字符流中读出前六个字符 “google” 时,第一个只出现一次的字符是”l”。
数据范围:字符串长度满足 1≤n≤1000 ,字符串中出现的字符一定在 ASCII 码内。 进阶:空间复杂度 O(n) ,时间复杂度 O(n)
后台会用以下方式调用 Insert 和 FirstAppearingOnce 函数
string caseout = “”; 1.读入测试用例字符串casein 2.如果对应语言有Init()函数的话,执行Init() 函数 3.循环遍历字符串里的每一个字符ch { Insert(ch); caseout += FirstAppearingOnce() } 4.输出caseout,进行比较。
返回值描述: 如果当前字符流没有存在出现一次的字符,返回#字符。
示例 1 输入: “google” 返回值: “ggg#ll”示例 2 输入: “abcdee” 返回值: “aaaaaa”
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import java.util.*;public class Solution { Map<Character, Integer> chCountMap = new HashMap <>(); LinkedList<Character> singleList = new LinkedList <>(); public void Insert (char ch) { int count = chCountMap.merge(ch, 1 , Integer::sum); if (count == 1 ) { singleList.add(ch); } } public char FirstAppearingOnce () { while (singleList.size() > 0 ) { char ch = singleList.getFirst(); if (chCountMap.get(ch) == 1 ) { return ch; } singleList.removeFirst(); } return '#' ; } }
JZ14 剪绳子 时间限制: 1 秒 空间限制: 64M 知识点: 基础数学
描述
给你一根长度为 n 的绳子,请把绳子剪成整数长的 m 段( m 、 n 都是整数, n > 1 并且 m > 1 , m <= n ),每段绳子的长度记为 k[1],...,k[m]
。请问 k[1]*k[2]*...*k[m]
可能的最大乘积是多少?例如,当绳子的长度是 8 时,我们把它剪成长度分别为 2、3、3 的三段,此时得到的最大乘积是 18 。
数据范围: 2≤n≤60 进阶:空间复杂度 O(1) ,时间复杂度 O(n) 输入描述: 输入一个数n,意义见题面。 返回值描述: 输出答案。
示例 1 输入: 8 返回值: 18 说明: 8 = 2 +3 +3 , 23 3=18示例 2 输入: 2 返回值: 1 说明: m>1,所以切成两段长度是1的绳子
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class Solution { public int cutRope (int target) { if (target <= 1 ) { return 0 ; } int [] dp = new int [target + 1 ]; dp[0 ] = 0 ; dp[1 ] = 1 ; for (int i = 2 ; i <= target; i++) { for (int j = 1 ; j <= i / 2 ; j++) { int product = Math.max(j, dp[j]) * Math.max(i - j, dp[i - j]); dp[i] = Math.max(dp[i], product); } } return dp[target]; } }
JZ81 调整数组顺序使奇数位于偶数前面(二) 时间限制: 1 秒 空间限制: 64M 知识点: 数组 排序
描述
输入一个长度为 n 整数数组,数组里面可能含有相同的元素,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前面部分,所有的偶数位于数组的后面部分,对奇数和奇数,偶数和偶数之间的相对位置不做要求,但是时间复杂度和空间复杂度必须如下要求。
数据范围: 0≤n≤50000,数组中每个数的值 0≤val≤10000 要求:时间复杂度 O(n),空间复杂度 O(1)
示例 1 输入: [1,2,3,4]
返回值: [1,3,2,4]
说明: [3,1,2,4]
或者 [3,1,4,2]
也是正确答案示例 2 输入: [1,3,5,6,7]
返回值: [1,3,5,7,6]
说明: [3,1,5,7,6]
等也是正确答案示例 3 输入: [1,4,4,3]
返回值: [1,3,4,4]
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import java.util.*;public class Solution { public int [] reOrderArrayTwo (int [] array) { int left = 0 ; int right = array.length - 1 ; while (left < right) { while (left < right && isOddNum(array[left])) { left++; } while (left < right && !isOddNum(array[right])) { right--; } if (left < right) { int tmp = array[left]; array[left] = array[right]; array[right] = tmp; } } return array; } private boolean isOddNum (int num) { return num % 2 == 1 ; } }
JZ83 剪绳子(进阶版) 时间限制: 1 秒 空间限制: 64M 知识点: 基础数学 快速幂
描述
给你一根长度为 n 的绳子,请把绳子剪成整数长的 m 段( m 、 n 都是整数, n > 1 并且 m > 1 , m <= n ),每段绳子的长度记为 k[1],...,k[m]
。请问 k[1]*k[2]*...*k[m]
可能的最大乘积是多少?例如,当绳子的长度是 8 时,我们把它剪成长度分别为 2、3、3 的三段,此时得到的最大乘积是 18 。
由于答案过大,请对 998244353 取模。
数据范围: 2≤n≤10^14
进阶:空间复杂度 O(1) , 时间复杂度 O(logn)
示例 1 输入: 4 返回值: 4 说明: 拆分成 2 个长度为 2 的绳子,2 * 2 = 4示例 2 输入: 5 返回值: 6 说明: 剪成一个长度为 2 的绳子和一个长度为 3 的绳子,答案为2*3=6示例 3 输入: 874520 返回值: 908070737
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 import java.util.*;public class Solution { private long mod = 998244353 ; public long cutRope (long number) { if (number <= 3 ) return number - 1 ; if (number % 3 == 0 ) return pow(3 , number / 3 ); else if (number % 3 == 1 ) return fast(pow(3 , number / 3 - 1 ), 4 ); else return fast(pow(3 , number / 3 ), 2 ); } long pow (long x, long y) { long res = 1 ; while (y != 0 ) { if ((y & 1L ) != 0 ) res = fast(res, x); x = fast(x, x); y = y >> 1 ; } return res; } private long fast (long x, long y) { long res = 0 ; x %= mod; y %= mod; while (y != 0 ) { if ((y & 1L ) != 0 ) { res += x; if (res >= mod) res -= mod; } y = y >> 1 ; x = x << 1 ; if (x >= mod) x -= mod; } return res; } }
JZ17 打印从1到最大的n位数 时间限制: 1 秒 空间限制: 64M 知识点: 数组
描述
输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。
用返回一个整数列表来代替打印
n 为正整数,0 < n <= 5
示例 1 输入: 1 返回值: [1,2,3,4,5,6,7,8,9]
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import java.util.*;public class Solution { public int [] printNumbers (int n) { int end = 1 ; for (int i = 1 ; i <= n; i++) { end *= 10 ; } int [] res = new int [end - 1 ]; for (int i = 1 ; i < end; i++) { res[i - 1 ] = i; } return res; } }
华为机试 较难 HJ3 明明的随机数 时间限制: 1 秒 空间限制: 32M 知识点: 数组
描述 明明生成了 N 个 1 到 500 之间的随机整数。请你删去其中重复的数字,即相同的数字只保留一个,把其余相同的数去掉,然后再把这些数从小到大排序,按照排好的顺序输出。
数据范围: 1≤n≤1000,输入的数字大小满足 1≤val≤500
输入描述: 第一行先输入随机整数的个数 N 。 接下来的 N 行每行输入一个整数,代表明明生成的随机数。 具体格式可以参考下面的”示例”。
输出描述: 输出多行,表示输入数据处理后的结果
示例 1 输入: 3 2 2 1 返回值: 1 2 说明: 输入解释: 第一个数字是 3,也即这个小样例的 N=3,说明用计算机生成了 3 个 1 到 500 之间的随机整数,接下来每行一个随机数字,共 3 行,也即这 3 个随机数字为: 2 2 1 所以样例的输出为: 1 2
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import java.util.*;import java.util.stream.*;public class Main { public static void main (String[] args) { Scanner sc = new Scanner (System.in); int n = sc.nextInt(); Set<Integer> numSet = new HashSet <>(); for (int i = 0 ; i < n; i ++) { numSet.add(sc.nextInt()); } List<Integer> numList = numSet.stream().sorted().collect(Collectors.toList()); numList.forEach(num -> { System.out.println(num); }); } } import java.util.*;public class Test { public static void main (String[] args) { Scanner sc = new Scanner (System.in); int num = sc.nextInt(); TreeSet set = new TreeSet (); for (int i = 0 ; i < num ;i++){ set.add(sc.nextInt()); } Iterator iterator = set.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } } }
困难 HJ28 素数伴侣 时间限制: 1 秒 空间限制: 32M 知识点: 查找 排序
描述
题目描述 若两个正整数的和为素数,则这两个正整数称之为“素数伴侣”,如 2 和 5、6 和 13,它们能应用于通信加密。现在密码学会请你设计一个程序,从已有的 N ( N 为偶数)个正整数中挑选出若干对组成“素数伴侣”,挑选方案多种多样,例如有 4 个正整数:2,5,6,13,如果将 5 和 6 分为一组中只能得到一组“素数伴侣”,而将 2 和 5、6 和 13 编组将得到两组“素数伴侣”,能组成“素数伴侣”最多的方案称为“最佳方案”,当然密码学会希望你寻找出“最佳方案”。
输入: 有一个正偶数 n ,表示待挑选的自然数的个数。后面给出 n 个具体的数字。
输出: 输出一个整数 K ,表示你求得的“最佳方案”组成“素数伴侣”的对数。
数据范围:1≤n≤100,输入的数据大小满足 2≤val≤30000
输入描述: 输入说明 1 输入一个正偶数 n 2 输入 n 个整数
输出描述: 求得的“最佳方案”组成“素数伴侣”的对数。
示例 1 输入: 4 2 5 6 13 返回值: 2示例 2 输入: 2 3 6 返回值: 0示例 3 输入: 2 3 6 返回值: 0
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 import java.util.*;public class Main { public static void main (String[] args) { Scanner sc = new Scanner (System.in); while (sc.hasNextInt()) { int n = sc.nextInt(); int [] arr=new int [n]; ArrayList<Integer> odds = new ArrayList <>(); ArrayList<Integer> evens = new ArrayList <>(); for (int i = 0 ; i < n; i ++) { int num = sc.nextInt(); if ((num & 1 ) == 1 ) { odds.add(num); } else { evens.add(num); } } int [] evenMatch = new int [evens.size()]; int count = 0 ; for (int i = 0 ; i < odds.size(); i ++) { boolean [] evenUsed = new boolean [evens.size()]; if (find(odds.get(i), evens, evenMatch, evenUsed)) { count ++; } } System.out.println(count); } } private static boolean find (int odd, ArrayList<Integer> evens, int [] evenMatch, boolean [] evenUsed) { for (int i = 0 ; i < evens.size(); i ++) { if (isPrime(odd + evens.get(i)) && !evenUsed[i]) { evenUsed[i] = true ; if (evenMatch[i] == 0 || find(evenMatch[i], evens, evenMatch, evenUsed)) { evenMatch[i] = odd; return true ; } } } return false ; } private static boolean isPrime (int num) { if (num == 1 ) return false ; for (int i = 2 ; i * i <= num; i ++) { if (num % i == 0 ) return false ; } return true ; } }
HJ44 Sudoku 时间限制: 1 秒 空间限制: 32M 知识点: 思维 基础数学 搜索
描述 问题描述:数独(Sudoku)是一款大众喜爱的数字逻辑游戏。玩家需要根据 9X9 盘面上的已知数字,推算出所有剩余空格的数字,并且满足每一行、每一列、每一个 3X3 粗线宫内的数字均含 1-9,并且不重复。
数据范围:输入一个 9*9 的矩阵
输入描述: 包含已知数字的 9X9 盘面数组 [空缺位以数字 0 表示]
输出描述: 完整的 9X9 盘面数组
示例 1 输入: 0 9 2 4 8 1 7 6 3 4 1 3 7 6 2 9 8 5 8 6 7 3 5 9 4 1 2 6 2 4 1 9 5 3 7 8 7 5 9 8 4 3 1 2 6 1 3 8 6 2 7 5 9 4 2 7 1 5 3 8 6 4 9 3 8 6 9 1 4 2 5 7 0 4 5 2 7 6 8 3 1 返回值: 5 9 2 4 8 1 7 6 3 4 1 3 7 6 2 9 8 5 8 6 7 3 5 9 4 1 2 6 2 4 1 9 5 3 7 8 7 5 9 8 4 3 1 2 6 1 3 8 6 2 7 5 9 4 2 7 1 5 3 8 6 4 9 3 8 6 9 1 4 2 5 7 9 4 5 2 7 6 8 3 1
答案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 import java.util.*;public class Main { public static void main (String[] args) { Scanner sc = new Scanner (System.in); while (sc.hasNextInt()) { int [][] sd = new int [9 ][9 ]; for (int i = 0 ; i < 9 ; i ++) { for (int j = 0 ; j < 9 ; j ++) { sd[i][j] = sc.nextInt(); } } dfs(sd, 0 , 0 ); for (int i = 0 ; i < 9 ; i ++) { for (int j = 0 ; j < 9 ; j ++) { System.out.print(sd[i][j]); System.out.print(" " ); } System.out.println(); } } } public static boolean dfs (int [][] sd, int i, int j) { if (j == 9 ) { i ++; j = 0 ; } if (i == 9 && j == 0 ) { return true ; } if (sd[i][j] == 0 ) { for (int k = 1 ; k <= 9 ; k ++) { if (check(sd, i, j, k)) { sd[i][j] = k; if (dfs(sd, i, j + 1 )) { return true ; } sd[i][j] = 0 ; } } return false ; } else { return dfs(sd, i, j + 1 ); } } public static boolean check (int [][] sd, int i, int j, int val) { for (int k = 0 ; k < 9 ; k ++) { if (sd[i][k] == val) { return false ; } } for (int k = 0 ; k < 9 ; k ++) { if (sd[k][j] == val) { return false ; } } int limitRow = i / 3 * 3 ; int limitCol = j / 3 * 3 ; for (int k = limitRow; k < limitRow + 3 ; k ++) { for (int l = limitCol; l < limitCol + 3 ; l ++) { if (sd[k][l] == val) { return false ; } } } return true ; } }
链接
https://www.nowcoder.com/ta/coding-interviews
https://leetcode.com/problemset/top-100-liked-questions/