更新文件夹结构并且重新更新链接
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
# 算法
|
||||
在应聘技术岗位的时候,可能都会有一个让绝大部分人都比较讨厌的算法环节。
|
||||
|
||||
也不知道是谁搞出这么无聊的东西,但没有办法现实总是残酷的,在抱怨的同时也只能在找工作之前复习下这些在实际工作中基本上不用的算法题了。
|
||||
|
||||
因为 MD 文档格式的限制,所以我们在这里只放一个索引并且维护这个索引,有关的具体内容,请通过索引访问我们的社区进行查看,我们也非常欢迎您参与讨论,
|
||||
您可以在社区注册后进行讨论。
|
||||
|
||||
## 算法问题
|
||||
在这里我将按照我遇到算法问题的先后顺序进行排列。很多算法题目都是题库上面的原题,你可以到题库上面去找找。
|
||||
|
||||
我这里只是结合我在面试的时候遇到的题目来进行解答和进行一些点评,就当时总结和消遣吧,如果你希望有什么问题一起讨论的话,欢迎 Fork 这个项目并且登录社区进行讨论。
|
||||
|
||||
最新的内容在最前面。
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
- [二叉树的序列化和反序列化(Serialize and Deserialize Binary Tree)](/algorithm/serialize-and-deserialize-binary-tree.md)
|
||||
- [岛屿个数](/algorithm/number-of-islands.md)
|
||||
- [合并 2 个有序链表](/algorithm/merge-two-sorted-lists.md)
|
||||
- [带环链表](/algorithm/linked-list-cycle.md)
|
||||
- [电话号码的字母组合](/algorithm/letter-combinations-of-a-phone-number.md)
|
||||
- [格雷编码](/algorithm/gray-code.md)
|
||||
- [点积](/algorithm/dot-product.md)
|
||||
- [二叉树](/algorithm/binary-tree.md)
|
||||
- [二叉树的层次遍历](/algorithm/binary-tree-level-order-traversal.md)
|
||||
- [下一个斐波拉契数](/algorithm/next-fibonacci-number.md)
|
||||
- [一个字符串包裹函数](/algorithm/a-word-wrap-functionality.md)
|
||||
- [打印 100 以内的素数](/algorithm/prime-numbers-from-1-to-100.md)
|
||||
- [二进制空白](/algorithm/binary-gap.md)
|
||||
- [Lambda 偶数](/algorithm/lambda-evens-lambda.md)
|
||||
- [上下计数](/algorithm/count-up-down.md)
|
||||
- [展平嵌套数组](/algorithm/flatten-nested-arrays.md)
|
||||
- [机器人移动](/algorithm/robot-movement.md)
|
||||
- [构建城堡](/algorithm/build-castles.md)
|
||||
@@ -0,0 +1,96 @@
|
||||
# A "word-wrap" functionality(一个字符串包裹函数)
|
||||
|
||||
> 🔔 参与讨论:https://www.isharkfly.com/t/a-word-wrap-functionality/13452
|
||||
|
||||
|
||||
## 问题描述
|
||||
|
||||
下面是有关这个问题的描述部分。
|
||||
|
||||
### 英文
|
||||
|
||||
We are building a word processor and we would like to implement a “word-wrap” functionality.
|
||||
|
||||
Given a list of words followed by a maximum number of characters in a line, return a collection of strings where each
|
||||
string element represents a line that contains as many words as possible, with the words in each line being concatenated
|
||||
with a single ‘-’ (representing a space, but easier to see for testing). The length of each string must not exceed the
|
||||
maximum character length per line.
|
||||
|
||||
Your function should take in the maximum characters per line and return a data structure representing all lines in the
|
||||
indicated max length.
|
||||
|
||||
### 中文
|
||||
|
||||
我们希望构建一个字符串处理函数,这个字符串处理函数将会对给定输入的字符串和参数进行处理。
|
||||
|
||||
我们首先将会定义一个字符串的数组,在这个字符串的数组中每一个元素都是存储为一个单词,同时我们将会给出一个整数类型的参数。你的方法将会对上面输入的
|
||||
2 个参数进行运算,在每一个单词和每一个单词之间会添加一个字符 ”-“ 来进行区分,同时新生成的数组或者 List
|
||||
每一元素的字符串长度将不能超过给出的字符串的长度。
|
||||
|
||||
如果你新生成的元素是以横杠结尾的话,那么你需要删除这个横杠。
|
||||
|
||||
### 样例
|
||||
|
||||
下面给出了这个问题的示例,以便于你参考。
|
||||
|
||||
| 输入参数 1 | 输入参数 2 | 输出 |
|
||||
|------------------------------------------------------------------------------------------------------------------------------|--------|---------------------------------------------------------------------------------------------------|
|
||||
| words1 = [ “The”, “day”, “began”, “as”, “still”, “as”, “the”, “night”, “abruptly”, “lighted”, “with”, “brilliant”, “flame” ] | 13 | [ “The-day-began”, “as-still-as”, “the-night”, “abruptly”, “lighted-with”, “brilliant”, “flame” ] |
|
||||
| words1 = [ “The”, “day”, “began”, “as”, “still”, “as”, “the”, “night”, “abruptly”, “lighted”, “with”, “brilliant”, “flame” ] | 20 | [ “The-day-began-as”, “still-as-the-night”, “abruptly-lighted”, “with-brilliant-flame” ] |
|
||||
| words2 = [ “Hello” ] | 5 | [ “Hello” ] |
|
||||
| words3 = [ “Hello”, “world” ] | 5 | [ “Hello”, “world” ] |
|
||||
| words4 = [“Well”, “Hello”, “world” ] | 5 | [ “Well”, “Hello”, “world” ] |
|
||||
| words5 = [“Hello”, “HelloWorld”, “Hello”, “Hello”] | 20 | [ “Hello-HelloWorld”, “Hello-Hello” ] |
|
||||
|
||||
上面给出的是是测试用的示例,其中输入参数 words1 到 5 就是定义的变量名而已,不需要过度关注。
|
||||
|
||||
## 思路点评和源代码
|
||||
|
||||
这个题目的难度还是比较大的,尤其是在没有开发工具进行编译的时情况下。
|
||||
|
||||
这个题目是 indeed.com 的一个在线面试测试题。这个公司的在线面试测试使用的是第三方公司提供的评估工具,主持面试的人可能是对技术并不是非常了解的人,或者是对技术比较了解的人,我们不清楚具体的情况。
|
||||
|
||||
但是在面试过程中,他们只注重程序的输出和面试的结果,至于你的思路或者你的想法,主持面试的人可能并不十分关注,同时也不怎么会听你的解释,很多时候你都会是在自言自语。
|
||||
|
||||
整体感觉面试互动很少,更多的时候是你在对着屏幕说话。
|
||||
|
||||
最开始的时候,我的思路是首先对给出的数组进行遍历,当取得第一个元素的时候,将元素后面添加横杠,然后与长度进行对比,如果长度超过了给定的长度的话那么就删除横杠后压入需要返回的列表中。
|
||||
|
||||
如果长度少于返回的长度,那么再取出下一个元素,同时再结尾再添加横杆后进行判断,然后再确定横杠的处理。
|
||||
|
||||
这个题目的主要问题就在于横杠的处理,有时候横杠在结尾,有时候横杠在开头,你需要一个一个判读。
|
||||
|
||||
在随后的测试中,我发现一直是横杠处理不好,结果导致没有完全通过最后的测试,就是上面测试用例的第二行。
|
||||
|
||||
### 思路 2
|
||||
|
||||
因为这个题目时间有限,并且我们还不能使用 StringUtils
|
||||
来进行一些快速的字符串处理,因此我没有在规定的时间内完成所有的测试。在随后结束面试后,我再仔细思考了下问题后发现其实我们还可以有其他的办法来进行操作。
|
||||
|
||||
我使用下面的思路,并且完成了代码的修改。
|
||||
|
||||
首先我们需要将输入的数组变成一个长的字符串,单词之间使用横杠分隔。例如,[ “Hello”, “world” ] 将会变成字符串为:Hello-world。
|
||||
|
||||
在完成上面的操作后,我们需要使用一个 while 循环来做。
|
||||
|
||||
首先在 while 循环中判读整个字符串长度小于给定的长度,这个时候需要直接返回,然后中断循环。
|
||||
|
||||
下一步,对字符串,从头到给定的长度进行截断后获得子字符串,随后对子字符串进行判断,如果这个子字符串是以横杠结尾的话,删除横杠然后压入需要返回的数组,然后更新需要处理的字符串为截断后余下的字符串。
|
||||
|
||||
如果按照给定的长度进行截断后,你获得最后的一个字符不是横杠,那么我们就知道你截断到了单词上,获得的子字符串中,找到最后一个横杠,然后获得索引的
|
||||
ID,在获得这个索引的 ID 后对需要处理的字符串按照索引 ID 进行截断。
|
||||
|
||||
然后删除掉最后的横杠压入需要返回的列表中。
|
||||
|
||||
在余下的字符串中可能遇到的情况是目前你将会是横杠开头的,因此你还需要删除掉余下字符串中开头和结尾的横杠。
|
||||
|
||||
继续上面的处理,直到需要处理的字符串长度小于给定的长度后中断循环。
|
||||
|
||||

|
||||
|
||||
上图是对上面思路 2 中的算法进行测试后的返回结果,从结果中可以看到满足需要输出的预期。
|
||||
|
||||
### 代码
|
||||
|
||||
请访问
|
||||
GitHub:https://github.com/cwiki-us-docs/java-tutorials/blob/master/toolkits/codebank/src/test/java/com/ossez/toolkits/codebank/tests/algorithm/WordWrapTest.java
|
||||
@@ -0,0 +1,66 @@
|
||||
# Binary Gap(二进制空白)
|
||||
|
||||
> 🔔 参与讨论:https://www.isharkfly.com/t/binary-gap/311
|
||||
|
||||
|
||||
**英文描述**
|
||||
|
||||
A binary gap within a positive integer N is any maximal sequence of consecutive zeros that is surrounded by ones at both
|
||||
ends in the binary representation of N.
|
||||
|
||||
For example, number 9 has binary representation 1001 and contains a binary gap of length 2. The number 529 has binary
|
||||
representation 1000010001 and contains two binary gaps: one of length 4 and one of length 3. The number 20 has binary
|
||||
representation 10100 and contains one binary gap of length 1. The number 15 has binary representation 1111 and has no
|
||||
binary gaps. The number 32 has binary representation 100000 and has no binary gaps.
|
||||
|
||||
Write a function:
|
||||
|
||||
class Solution { public int solution(int N); }
|
||||
|
||||
that, given a positive integer N, returns the length of its longest binary gap. The function should return 0 if N
|
||||
doesn't contain a binary gap.
|
||||
|
||||
For example, given N = 1041 the function should return 5, because N has binary representation 10000010001 and so its
|
||||
longest binary gap is of length 5. Given N = 32 the function should return 0, because N has binary representation '
|
||||
100000' and thus no binary gaps.
|
||||
|
||||
Write an efficient algorithm for the following assumptions:
|
||||
|
||||
N is an integer within the range [1..2,147,483,647].
|
||||
|
||||
**中文描述**
|
||||
|
||||
这里我不按照原文一字一字的翻译,但是尽量按照题目的要求把题目解释清楚。
|
||||
|
||||
这里题目的要求是,将 N 为一个整数类型的数据,转换为一个 2 进制的字符串,然后在返回的字符串中返回最大的 0 的间隔数字。
|
||||
|
||||
例如 529 转换为 2 进制的字符串为:1000010001,在这里,将会存在以 1 为分割的字符串 0000 和 000,这 2 个字符串的长度分别为 4 和
|
||||
3。
|
||||
|
||||
我们的算法需要返回的值为 4。
|
||||
|
||||
### 思路和点评
|
||||
|
||||
这个题目的思路其实比较简单,你需要首先将 N 这个整数,转换为 0 和 1 的字符串。然后在转换成功的字符串中返回以 1 分分割的 0
|
||||
的长度。
|
||||
|
||||
这里可能需要考虑下面的几种情况。
|
||||
|
||||
| 情况 | 结果 |
|
||||
|----|---------------------------------|
|
||||
| 11 | 这个情况应该返回的长度为 0 |
|
||||
| 10 | 这个情况因为没有被 1 这个字符串封闭,因此应该返回长度为 0 |
|
||||
|
||||
传统的思路应该是采取字符串分割的方式,进行遍历后获得结果。
|
||||
|
||||
我们在这里采取一种相对不是非常常规的方式,例如在 10000010001 字符串中插入 #,将字符串变为 #1#00000#1#000#1#。
|
||||
|
||||
然后将字符串按照 1 进行分割,那么分割后的数组应该分别存储的数据为:#,#0000#,#000#,#
|
||||
|
||||
这里我们只需要找到 #...# 中值最大的连续 0 字符串就可以了。基本上可以使用 1 个字符串替换函数和一个字符串分割函数就可以了,并不需要多次存储和遍历。
|
||||
|
||||
**源代码**
|
||||
|
||||
源代码和有关代码的更新请访问 GitHub:
|
||||
|
||||
https://github.com/cwiki-us/java-tutorial/blob/master/src/test/java/com/ossez/lang/tutorial/tests/codility/CodilityBinaryGapTest.java
|
||||
@@ -0,0 +1,47 @@
|
||||
# 二叉树的层次遍历(Binary Tree Level Order Traversal)
|
||||
|
||||
> 🔔 参与讨论:https://www.isharkfly.com/t/binary-tree-level-order-traversal/15118
|
||||
|
||||
## 中文描述
|
||||
|
||||
给出一棵二叉树,返回其节点值的层次遍历(逐层从左往右访问)
|
||||
|
||||
## 样例
|
||||
|
||||
给一棵二叉树 `{3,9,20,#,#,15,7}` :
|
||||
|
||||
```
|
||||
3
|
||||
/ \
|
||||
9 20
|
||||
/ \
|
||||
15 7
|
||||
```
|
||||
|
||||
返回他的分层遍历结果:
|
||||
|
||||
```
|
||||
[
|
||||
[3],
|
||||
[9,20],
|
||||
[15,7]
|
||||
]
|
||||
```
|
||||
|
||||
## 挑战
|
||||
|
||||
挑战1:只使用一个队列去实现它
|
||||
|
||||
挑战2:用BFS算法来做
|
||||
|
||||
## 思路和点评
|
||||
|
||||
这个程序可以使用队列的广度优先算法来进行遍历。
|
||||
|
||||
需要注意的是,因为在输出结果的时候需要按照层级来进行输出,那么需要考虑的一个算法就是二叉树的层级遍历算法。
|
||||
|
||||
这个算法要求在遍历的时候记录树的层级。
|
||||
|
||||
## 源代码
|
||||
GitHub 的源代码,请访问下面的链接:
|
||||
https://github.com/honeymoose/codebank-algorithm/blob/main/src/test/java/com/ossez/codebank/algorithm/tests/lintcode/LintCode0069LevelOrderTest.java
|
||||
@@ -0,0 +1,80 @@
|
||||
# 二叉树(Binary Tree)
|
||||
|
||||
> 🔔 参与讨论:https://www.isharkfly.com/t/binary-tree/15117
|
||||
|
||||
二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的。对于二叉树,有深度遍历和广度遍历,深度遍历有前序、中序以及后序三种遍历方法,广度遍历即我们平常所说的层次遍历。
|
||||
|
||||
因为树的定义本身就是递归定义,因此采用递归的方法去实现树的三种遍历不仅容易理解而且代码很简洁,而对于广度遍历来说,需要其他数据结构的支撑,比如队列。
|
||||
|
||||
## 遍历
|
||||
|
||||
二叉树的遍历你可能会在各种面试和算法题目中遇到。
|
||||
|
||||
这里主要讨论下面几种种遍历算法。在遍历 深度遍历算法(depth-first search,缩写为 DFS)和 广度遍历算法(breadth-first search,
|
||||
简称 BFS)
|
||||
|
||||

|
||||
|
||||
相对来说,深度遍历算法一般都可以在程序中使用递归来实现。
|
||||
|
||||
广度遍历算法一般都需要解借助一个其他的存储结构,一般来说会考虑使用队列来实现。
|
||||
|
||||
遍历的逻辑按照上面的说明的方法,使用下面 4 种遍历的方法。
|
||||
|
||||
(DFS)前序遍历:根结点 ---> 左子树 ---> 右子树
|
||||
|
||||
(DFS)中序遍历:左子树---> 根结点 ---> 右子树
|
||||
|
||||
(DFS)后序遍历:左子树 ---> 右子树 ---> 根结点
|
||||
|
||||
(BFS)层次遍历:只需按层次遍历即可
|
||||
|
||||

|
||||
|
||||
上面这个树的按照各种不同的遍历方法的遍历结果为:
|
||||
|
||||
前序遍历:1 2 4 5 7 8 3 6
|
||||
|
||||
中序遍历:4 2 7 5 8 1 3 6
|
||||
|
||||
后序遍历:4 7 8 5 2 6 3 1
|
||||
|
||||
层次遍历:1 2 3 4 5 6 7 8
|
||||
|
||||
针对上面的提出的各种遍历方法,我们对程序中使用的遍历代码进行分析。
|
||||
|
||||
首先需要将上面的数据结构初始化为数组。可以将上面的树初始化为数组:[1,2,3,4,5,#,6,#,#,7,8,#,#],这个数字中的 # 符号表示的为
|
||||
NULL,就是没有子节点的意思。
|
||||
|
||||
### 前序遍历
|
||||
|
||||
递归方法,我们可以很容易的使用前序的递归来对树进行遍历。
|
||||
|
||||
```
|
||||
/**
|
||||
*
|
||||
* @param root
|
||||
*/
|
||||
public void preOrderTraverselRecursion(TreeNode root) {
|
||||
if (root != null) {
|
||||
loopList.add(root.val);
|
||||
preOrderTraverselRecursion(root.left);
|
||||
preOrderTraverselRecursion(root.right);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
中序遍历
|
||||
|
||||
```
|
||||
/**
|
||||
*
|
||||
* @param root
|
||||
*/
|
||||
public void preOrderTraverselRecursion(TreeNode root) {
|
||||
if (root != null) {
|
||||
preOrderTraverselRecursion(root.left);
|
||||
loopList.add(root.val);
|
||||
preOrderTraverselRecursion(root.right);
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,61 @@
|
||||
# 构建城堡(Build Castles)
|
||||
|
||||
> 🔔 参与讨论:https://www.isharkfly.com/t/build-castles/315
|
||||
|
||||
中文标题【构建城堡】
|
||||
|
||||
Charlemagne, the King of Frankie,
|
||||
|
||||
## 英文描述
|
||||
|
||||
请参考图片中的说明。
|
||||
|
||||

|
||||
|
||||
## 中文描述
|
||||
|
||||
根据给出的数组确定能够盖多少城堡。
|
||||
|
||||
## 思路和点评
|
||||
|
||||
我不能确定我的思路是正确的,也欢迎大家参与讨论。
|
||||
|
||||
根据给出的数组,因为有重复的值,我首先想到的是将给出的数组进行一次过滤和处理,去掉重复的值。
|
||||
|
||||
例如,给出的数组为:int[] A = { 2, 2, 3, 4, 3, 3, 2, 2, 1, 1, 2, 5 };,那么我希望处理为:int[] A = { 2, 3, 4, 3, 2, 1, 2,
|
||||
5 }; 去掉重复的值,因为重复的值在这里没有意义。
|
||||
|
||||

|
||||
|
||||
然后根据新的数组进行判断,需要判断的是 2 个端点,你需要将 2 个端点考虑为 0。
|
||||
|
||||
那么根据上面已经处理过的数组,你在进行遍历的时候,针对第一个值 2 ,你需要判读左侧的值和右侧的值,因为默认左侧的值一直为 0
|
||||
,那么右侧的值为 3 的话,那么这里需要 v 需要 +1;
|
||||
|
||||
第 2 个值,因为第二个值的左侧,3 > 2, 但右侧 3 < 4。因此这个值不适合。
|
||||
|
||||
第 3 个值,左侧:4 > 3, 右侧 4 >3 这个值是合适的。
|
||||
|
||||
从这里我们找到的规律是,进行一次遍历,找到,如果只的左侧和右侧同时小于这值,或者左侧和右侧都同时大于这个值,那么这个值是合适的取值。
|
||||
|
||||
需要注意一个情况就是 {-3, -3},你初始化数组的时候,这个值为 {-3},那么这个地方是最少有一个合适的值。
|
||||
|
||||
## 源代码
|
||||
|
||||
源代码和有关代码的更新请访问 GitHub:
|
||||
|
||||
https://github.com/cwiki-us/codebank-algorithm/blob/master/src/test/java/com/ossez/codebank/interview/tests/WayfairTest.java
|
||||
|
||||
测试类请参考:
|
||||
|
||||
https://github.com/cwiki-us/codebank-algorithm/blob/master/src/test/java/com/ossez/codebank/interview/tests/WayfairTest.java
|
||||
|
||||
## 测试结果
|
||||
|
||||
上面程序的测试结果如下:
|
||||
|
||||
```
|
||||
2018/12/29 00:43:16 DEBUG [com.ossez.codebank.interview.tests.WayfairTest] - V - [2]
|
||||
2018/12/29 00:43:16 DEBUG [com.ossez.codebank.interview.tests.WayfairTest] - H - [2]
|
||||
2018/12/29 00:43:16 DEBUG [com.ossez.codebank.interview.tests.WayfairTest] - H + V - [4]
|
||||
```
|
||||
@@ -0,0 +1,87 @@
|
||||
# 上下计数(Count Up Down)
|
||||
|
||||
> 🔔 参与讨论:https://www.isharkfly.com/t/count-up-down/313
|
||||
|
||||
这个题目是 Kayak 发布的代码挑战题目。
|
||||
|
||||
最简单的描述就是不使用循环,输出 0 到 5,然后同样不是会用循环的方式再次输出 5 到 0。
|
||||
|
||||
## 英文描述
|
||||
|
||||
### Part 1
|
||||
|
||||
Write a program that counts in sequential order when given a start and end value - without using any iterative programing loops, i.e. while, for, do, for-each, etc.
|
||||
|
||||
You can assume that both the start and end values will always be positive and that the start value will always be less then the end value. There should only be one method with the following signature:
|
||||
|
||||
void countUp(int start, int end) {
|
||||
// All code exercise code should go here
|
||||
}
|
||||
|
||||
Here is example output with start=0 and end=5:
|
||||
|
||||
[ 0,1,2,3,4,5]
|
||||
|
||||
### Part 2
|
||||
|
||||
Continuing with part 1 change the output of the test, so that it now prints out in sequential order to the end value (only once), but then also counts down to the start value.
|
||||
|
||||
Again, using no iterative loops, and assuming that both the start and end values will always be positive and that start value will always be less then the end value. There should only be one method with the following signature:
|
||||
|
||||
void countUpAndDown(int start, int end) {
|
||||
// All code exercise code should go here
|
||||
}
|
||||
|
||||
Here is example output with start=0 and end=5:
|
||||
|
||||
[0,1,2,3,4,5,4,3,2,1,0]
|
||||
|
||||
## 中文描述
|
||||
|
||||
这里我不按照原文一字一字的翻译,但是尽量按照题目的要求把题目解释清楚。
|
||||
|
||||
最简单的描述就是不使用循环,输出 0 到 5,然后同样不是会用循环的方式再次输出 5 到 0。
|
||||
|
||||
本题目分 2 部分,第一部分是不使用循环的方式输出 0 到 5,第二部分是不使用循环的方式输出 0 到 5 以后,再输出 5 到 0。
|
||||
|
||||
其中需要注意的是 5 只能输出一次。
|
||||
|
||||
## 思路和点评
|
||||
|
||||
不使用 For 循环的方式输出 0 到 5 ,我们可以想到有几个方法。
|
||||
|
||||
第一个方法可能比较容易想到的就是递归调用,你可以根据输入的值,递归调用需要的次数就可以输出值了。你还可以采用计算机时钟的方式进行输出。
|
||||
|
||||
在这里我们采用递归调用的方式进行输出。
|
||||
|
||||
## 源代码
|
||||
|
||||
源代码和有关代码的更新请访问 GitHub:
|
||||
|
||||
https://github.com/cwiki-us/codebank-algorithm/blob/master/src/main/java/com/ossez/codebank/interview/KayakCountUpDown.java
|
||||
|
||||
测试类请参考:
|
||||
|
||||
https://github.com/cwiki-us/codebank-algorithm/blob/master/src/test/java/com/ossez/codebank/interview/tests/KayakTest.java
|
||||
|
||||
## 测试结果
|
||||
|
||||
上面程序的测试结果如下:
|
||||
```
|
||||
2018/12/25 21:23:54 DEBUG [com.ossez.codebank.interview.tests.KayakTest] - TEST Count Up and Down
|
||||
2018/12/25 21:23:54 DEBUG [com.ossez.codebank.interview.tests.KayakTest] - [2 -> 5]
|
||||
2018/12/25 21:23:54 DEBUG [com.ossez.codebank.interview.KayakCountUpDown] - BEGIN
|
||||
2018/12/25 21:23:54 DEBUG [com.ossez.codebank.interview.tests.KayakTest] - UP - [2, 3, 4, 5]
|
||||
2018/12/25 21:23:54 DEBUG [com.ossez.codebank.interview.KayakCountUpDown] - BEGIN
|
||||
2018/12/25 21:23:54 DEBUG [com.ossez.codebank.interview.tests.KayakTest] - UP & DOWN - [2, 3, 4, 5, 4, 3, 2]
|
||||
2018/12/25 21:23:54 DEBUG [com.ossez.codebank.interview.tests.KayakTest] - [0 -> 5]
|
||||
2018/12/25 21:23:54 DEBUG [com.ossez.codebank.interview.KayakCountUpDown] - BEGIN
|
||||
2018/12/25 21:23:54 DEBUG [com.ossez.codebank.interview.tests.KayakTest] - UP - [0, 1, 2, 3, 4, 5]
|
||||
2018/12/25 21:23:54 DEBUG [com.ossez.codebank.interview.KayakCountUpDown] - BEGIN
|
||||
2018/12/25 21:23:54 DEBUG [com.ossez.codebank.interview.tests.KayakTest] - UP & DOWN - [0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0]
|
||||
2018/12/25 21:23:54 DEBUG [com.ossez.codebank.interview.tests.KayakTest] - [-1 -> 5]
|
||||
2018/12/25 21:23:54 DEBUG [com.ossez.codebank.interview.KayakCountUpDown] - BEGIN
|
||||
2018/12/25 21:23:54 DEBUG [com.ossez.codebank.interview.tests.KayakTest] - UP - [-1, 0, 1, 2, 3, 4, 5]
|
||||
2018/12/25 21:23:54 DEBUG [com.ossez.codebank.interview.KayakCountUpDown] - BEGIN
|
||||
2018/12/25 21:23:54 DEBUG [com.ossez.codebank.interview.tests.KayakTest] - UP & DOWN - [-1, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0, -1]
|
||||
```
|
||||
@@ -0,0 +1,49 @@
|
||||
# 点积(Dot Product)
|
||||
|
||||
> 🔔 参与讨论:https://www.isharkfly.com/t/dot-product/15119
|
||||
|
||||
### 描述
|
||||
|
||||
给出两个数组,求它们的点积。([Wikipedia](https://en.wikipedia.org/wiki/Dot_product))
|
||||
|
||||
如果没有点积则返回-1
|
||||
|
||||
### 样例
|
||||
|
||||
```
|
||||
Input:A = [1,1,1]
|
||||
B = [2,2,2]
|
||||
Output:6
|
||||
```
|
||||
|
||||
### 代码
|
||||
|
||||
请单击下面链接在 GitHub 上查看最新的源代码:
|
||||
|
||||
https://github.com/honeymoose/codebank-algorithm/blob/main/src/test/java/com/ossez/codebank/algorithm/tests/lintcode/LintCode1480DotProductTest.java
|
||||
|
||||
### 点评
|
||||
|
||||
这个问题的关键是要了解点积是如何进行计算的。
|
||||
|
||||
对输入的数组还需要进行 NULL 的校验和计算。
|
||||
|
||||
摘录点积的计算方法如下:
|
||||
|
||||
向量是由n个实数组成的一个n行1列(n*1)或一个1行n列(1*n)的有序数组;
|
||||
|
||||
向量的点乘,也叫向量的内积、数量积,对两个向量执行点乘运算,就是对这两个向量对应位一一相乘之后求和的操作,点乘的结果是一个标量。
|
||||
|
||||
点乘公式
|
||||
|
||||
对于向量 a 和向量 b:
|
||||
|
||||

|
||||
|
||||
a和b的点积公式为:
|
||||
|
||||

|
||||
|
||||
要求一维向量 a 和向量 b 的行列数相同。
|
||||
|
||||
因为要求向量 a 和 b 的行列数相同,因此在程序中需要先进行判断才可以。
|
||||
@@ -0,0 +1,54 @@
|
||||
# 展平嵌套数组(Flatten Nested Arrays)
|
||||
|
||||
> 🔔 参与讨论:https://www.isharkfly.com/t/flatten-nested-arrays/314
|
||||
|
||||
这个题目是在 PillPack 现场面谈的时候的一个题目。
|
||||
|
||||
## 中文描述
|
||||
|
||||
题目要求比较简单:[1,2,[3],[[4]],5,6] -> [1,2,3,4,5,6]
|
||||
|
||||
就是数组中嵌套数组,考察一个数组[1,2,[3],[[4]],5,6]。 你怎么能够输出 1,2,3,4,5,6(并不要求按照顺序输出)。
|
||||
|
||||
这里是一个嵌套数组,你需要将这个数组中的值全部取出来。
|
||||
|
||||
## 思路和点评
|
||||
|
||||
不清楚其他语言中这个数据结构怎么存储,我假设的是在 Java 中存储的对象。
|
||||
|
||||
可以采用队列的方式来实现,例如,在 Java 中存储了整数,1, 2, 对象,[3] 为一个数组对象。
|
||||
|
||||
你可以先遍历一次 List,将所有的 List 的对象都压入队列中,然后进行出队。
|
||||
|
||||
在出队时候,判断对象是否为整数对象,如果是整数对象,就输出,如果不是整数对象,然后将数组对象继续进行遍历,然后压入队列,然后再出队。
|
||||
|
||||
在这里讨论的问题比较多,还有 [[[2]5]] 这种多层嵌套的问题。
|
||||
|
||||
经过网站上的考古,这里有 2 个方法可以更快的实现。1 是递归的方法,2 是 利用 Java 8 的 Stream 特性。
|
||||
|
||||
在写测试代码之前,你需要明白下数据结构的定义,要不然你没有办法测试。在 Java 中你可以定义为对象数组,如下:
|
||||
|
||||
`Object[] array = { ` `1` `, ` `2` `, ` `new` `Object[] { ` `3` `, ` `4` `, ` `new` `Object[] { ` `5` `, ` `new` `Object[] { ` `new` `Object[] { ` `6` `} } }, ` `7` `}, ` `8` `, ` `9` `, ` `10` `};`
|
||||
|
||||
然后可以利用递归,在对对象数组进行遍历的时候,如果你遇到了对象,那么你需要再次调用你的方法,对对象中的内容进行遍历,如果这个时候已经没有对象了,可以返回第二层遍历的结果,并且插入到上层 List 列表中。
|
||||
|
||||
如果你使用的 Java 8 的 Stream,你需要对 Stream 的使用和方法比较了解才可以。这里也涉及到了递归,只是写法有点不同罢了。
|
||||
|
||||
还有一个更加简单粗暴的方法,当然我不认为这个方法是出题人希望考察的目标,在 Java 中你可以将数组直接转换成 String 字符串进行输出,比如说上面的对象队列,你可以转换为:` [1, 2, [3, 4, [5, [[6]]], 7], 8, 9, 10]` 字符串进行输出,然后使用 Java 的 Split 函数,进行按照逗号拆分后,然后将多余 [ 和 ] 符号去掉,然后再将内容重新放回 List。 这个有点简单粗暴,但是也一样能够达到目的。
|
||||
|
||||
## 源代码
|
||||
源代码和有关代码的更新请访问 GitHub:
|
||||
|
||||
https://github.com/cwiki-us/codebank-algorithm/blob/master/src/test/java/com/ossez/codebank/interview/tests/PillPackTest.java
|
||||
|
||||
## 测试类请参考:
|
||||
|
||||
https://github.com/cwiki-us/codebank-algorithm/blob/master/src/test/java/com/ossez/codebank/interview/tests/PillPackTest.java
|
||||
|
||||
## 测试结果
|
||||
上面程序的测试结果如下:
|
||||
```
|
||||
2018/12/27 13:39:22 DEBUG [com.ossez.codebank.interview.tests.PillPackTest] - Test FlattenNestedArrays
|
||||
2018/12/27 13:39:22 DEBUG [com.ossez.codebank.interview.tests.PillPackTest] - LOOP: [1, 2, [3, 4, [5, [[6]]], 7], 8, 9, 10] - > [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
2018/12/27 13:39:22 DEBUG [com.ossez.codebank.interview.tests.PillPackTest] - Java 8: [1, 2, [3, 4, [5, [[6]]], 7], 8, 9, 10] - > [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
```
|
||||
@@ -0,0 +1,89 @@
|
||||
# 格雷编码(Gray Code)
|
||||
|
||||
> 🔔 参与讨论:https://www.isharkfly.com/t/gray-code/15120
|
||||
|
||||
## 描述
|
||||
|
||||
格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个二进制的差异。
|
||||
|
||||
给定一个非负整数 `n` ,表示该代码中所有二进制的总数,请找出其格雷编码顺序。一个格雷编码顺序必须以 `0` 开始,并覆盖所有的 2n 个整数。
|
||||
|
||||
对于给定的 `n`,其格雷编码顺序并不唯一。
|
||||
|
||||
根据以上定义, `[0,2,3,1]` 也是一个有效的格雷编码顺序。
|
||||
|
||||
## 样例
|
||||
|
||||
给定 `n = 2`, 返回 `[0,1,3,2]`。其格雷编码顺序为:
|
||||
|
||||
```
|
||||
00 - 0
|
||||
01 - 1
|
||||
11 - 3
|
||||
10 - 2
|
||||
```
|
||||
|
||||
## 挑战
|
||||
|
||||
O(2n) 时间复杂度。
|
||||
|
||||
## 代码
|
||||
|
||||
```
|
||||
/**
|
||||
* 411 https://www.lintcode.com/problem/gray-code/description
|
||||
*/
|
||||
@Test
|
||||
public void test0411GrayCode() {
|
||||
int n = 2;
|
||||
|
||||
List<Integer> retArray = new ArrayList<>();
|
||||
|
||||
if (n == 0) {
|
||||
retArray.add(0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < (2 << (n - 1)); i++) {
|
||||
int g = i ^ (i / 2);
|
||||
retArray.add(g);
|
||||
}
|
||||
|
||||
System.out.println(retArray);
|
||||
}
|
||||
```
|
||||
|
||||
## 点评
|
||||
|
||||
你需要对 Java 的位运算之间的关系和数据结构有比较深的了解。
|
||||
|
||||
在计算机语言中,数字的存储是按照位存储的。比如说 int 数据类型,在 Java 中定义的是 32 位。
|
||||
|
||||
那么 int 能在 Java 中存储的 2 进制格式为:
|
||||
|
||||
0000 0000 0000 0000 0000 0000 0000 0000
|
||||
|
||||
这里一共是 8 组,一共是 32 位。
|
||||
|
||||
如果你希望了解 Java int 是如何在 2 进制中表示的,你可以使用方法:Integer.toBinaryString(0);
|
||||
|
||||
这个方法不会输出完整的 32 位字符串,你需要在前面补上 0。
|
||||
|
||||
【0】 转换为 2 进制为:【0000 0000 0000 0000 0000 0000 0000 0000】
|
||||
|
||||
【2】 转换为 2 进制为:【0000 0000 0000 0000 0000 0000 0000 0010】
|
||||
|
||||
【5】 转换为 2 进制为:【0000 0000 0000 0000 0000 0000 0000 0101】
|
||||
|
||||
在这个题目中哟 2 个地方比较关键,第一个地方是需要循环多少次,就是 2 的 n 次方,应该是多少?第二个地方就是进行位运算了,你需要需要什么样的位运算符。
|
||||
|
||||
对于 2 的 n 次方的结果,基本上可以使用下面的公式 2 << (n-1),比如说 2 的 3 次方,那么就需要将 2 向左位移 2 位。
|
||||
|
||||
移动前:
|
||||
|
||||
【2】 转换为 2 进制为:【0000 0000 0000 0000 0000 0000 0000 0010】
|
||||
|
||||
移动后
|
||||
|
||||
【0000 0000 0000 0000 0000 0000 0000 1000】,这个 2 进制转换后的结果就是 【8】 正好是 2 的 3 次方。
|
||||
|
||||
随后,对 i 取整后进行一次异或运算就可以得到你要的结果的 2 进制结果了。
|
||||
@@ -0,0 +1,27 @@
|
||||
# Lambda Evens(Lambda 偶数)
|
||||
|
||||
> 🔔 参与讨论:https://www.isharkfly.com/t/lambda-evens-lambda/317
|
||||
|
||||
## 英文题目
|
||||
|
||||
题目的英文表述成参考:
|
||||
|
||||

|
||||
|
||||
## 中文描述
|
||||
|
||||
题目要求比较简单,使用 Lambda 表达式写一个函数,找到给出字符串中的偶数。
|
||||
|
||||
## 思路点评
|
||||
|
||||
你需要对 Lambda 表达式比较熟悉,需要知道 Lambda 是什么。在 Java 世界中,Lambda 是在 Java 8 中引进的一个表达式。属于函数式。
|
||||
|
||||
近来也用得越来越多,最好对 Lambda 有所了解。
|
||||
|
||||
同时,你还要有基本的Java 字符拆分 API 的了解。很多题目可能不能允许你用第三方 API,所以你需要了解 String.split 有关的算法。
|
||||
|
||||
## 源代码
|
||||
|
||||
源代码和有关代码的更新请访问 GitHub:
|
||||
|
||||
https://github.com/cwiki-us/codebank-algorithm/blob/master/src/test/java/com/ossez/codebank/interview/tests/others/LambdaEvensTest.java
|
||||
@@ -0,0 +1,31 @@
|
||||
# 电话号码的字母组合(Letter Combinations of a Phone Number)
|
||||
|
||||
> 🔔 参与讨论:https://www.isharkfly.com/t/letter-combinations-of-a-phone-number/15121
|
||||
|
||||
## 描述
|
||||
|
||||
给定一个字符串,字符串中不包含 01。返回这个字符串中所有可能的字母组合返回结果。
|
||||
|
||||
数字和字母的的映射图片,请参考下面的电话拨号盘。
|
||||
|
||||

|
||||
|
||||
## 样例
|
||||
|
||||
给定 `"23"`
|
||||
|
||||
返回 `["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]`
|
||||
|
||||
## 代码
|
||||
|
||||
GitHub 的源代码,请访问下面的链接:
|
||||
|
||||
https://github.com/honeymoose/codebank-algorithm/blob/main/src/test/java/com/ossez/codebank/algorithm/tests/lintcode/LintCode0425LetterCombinationsTest.java
|
||||
|
||||
## 点评
|
||||
|
||||
本题目主要考察你对递归调用的熟练使用程度。
|
||||
|
||||
有关递归调用的方法,请自行脑补,或者 Google 查询。
|
||||
|
||||
思路主要分 2 部分,第一部分存储使用的数据结构,你可以使用 Map 也可以使用数组,效果都是一样的。
|
||||
@@ -0,0 +1,56 @@
|
||||
# 带环链表(Linked List Cycle)
|
||||
|
||||
> 🔔 参与讨论:https://www.isharkfly.com/t/linked-list-cycle/15122
|
||||
|
||||
## 描述
|
||||
|
||||
给定一个链表,判断它是否有环。
|
||||
|
||||
## 样例
|
||||
|
||||
给出 -21->10->4->5, tail connects to node index 1,返回 true。
|
||||
|
||||
这里解释下,题目的意思,在英文原题中,tail connects to node index 1 表示的是节点 5 还要链接回索引号 为 1 的节点。
|
||||
|
||||
一个典型的带环链表如下:
|
||||
|
||||

|
||||
|
||||
## 挑战
|
||||
|
||||
不要使用额外的空间
|
||||
|
||||
## 代码
|
||||
|
||||
GitHub 的源代码,请访问下面的链接:
|
||||
https://github.com/honeymoose/codebank-algorithm/blob/main/src/test/java/com/ossez/codebank/algorithm/tests/lintcode/LintCode0102HasCycleTest.java
|
||||
|
||||
## 点评
|
||||
|
||||
链表(Linked
|
||||
list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(
|
||||
Pointer)。由于不必须按顺序存储,链表在插入的时候可以达到O(1)
|
||||
的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)
|
||||
的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。
|
||||
|
||||
使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。
|
||||
|
||||
在计算机科学中,链表作为一种基础的数据结构可以用来生成其它类型的数据结构。链表通常由一连串节点组成,每个节点包含任意的实例数据(data
|
||||
fields)和一或两个用来指向上一个/或下一个节点的位置的链接(”links”)。链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序,数据的访问往往要在不同的排列顺序中转换。而链表是一种自我指示数据类型,因为它包含指向另一个相同类型的数据的指针(链接)。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。链表有很多种不同的类型:单向链表,双向链表以及循环链表。
|
||||
|
||||
要判断一个链表中是否有循环,可以借助额外的存储空间,将链表插入到 HashSet
|
||||
中。创建一个以节点ID为键的HashSet集合,用来存储曾经遍历过的节点。然后同样是从头节点开始,依次遍历单链表的每一个节点。每遍历到一个新节点,就用新节点和HashSet集合当中存储的节点作比较,如果发现HashSet当中存在相同节点ID,则说明链表有环,如果HashSet当中不存在相同的节点ID,就把这个新节点ID存入HashSet,之后进入下一节点,继续重复刚才的操作。
|
||||
|
||||
这个方法在流程上和方法一类似,本质的区别是使用了HashSet作为额外的缓存。
|
||||
|
||||
假设从链表头节点到入环点的距离是D,链表的环长是S。而每一次HashSet查找元素的时间复杂度是O(1), 所以总体的时间复杂度是1*(D+S)
|
||||
=D+S,可以简单理解为O(N)。而算法的空间复杂度还是D+S-1,可以简单地理解成O(N)。
|
||||
|
||||
也可以采用指针的方式。
|
||||
|
||||
首先创建两个指针1和2(在java里就是两个对象引用),同时指向这个链表的头节点。然后开始一个大循环,在循环体中,让指针1每次向下移动一个节点,让指针2每次向下移动两个节点,然后比较两个指针指向的节点是否相同。如果相同,则判断出链表有环,如果不同,则继续下一次循环。
|
||||
|
||||
例如链表A->B->C->D->B->C->
|
||||
D,两个指针最初都指向节点A,进入第一轮循环,指针1移动到了节点B,指针2移动到了C。第二轮循环,指针1移动到了节点C,指针2移动到了节点B。第三轮循环,指针1移动到了节点D,指针2移动到了节点D,此时两指针指向同一节点,判断出链表有环。
|
||||
|
||||
此方法也可以用一个更生动的例子来形容:在一个环形跑道上,两个运动员在同一地点起跑,一个运动员速度快,一个运动员速度慢。当两人跑了一段时间,速度快的运动员必然会从速度慢的运动员身后再次追上并超过,原因很简单,因为跑道是环形的。
|
||||
@@ -0,0 +1,22 @@
|
||||
# 合并 2 个有序链表(Merge Two Sorted Lists)
|
||||
|
||||
> 🔔 参与讨论:https://www.isharkfly.com/t/2-merge-two-sorted-lists/15123
|
||||
|
||||
## 描述
|
||||
|
||||
将两个排序链表合并为一个新的排序链表。
|
||||
|
||||
## 样例
|
||||
|
||||
给出 `1->3->8->11->15->null`,`2->null`, 返回 `1->2->3->8->11->15->null`。
|
||||
|
||||
## 代码
|
||||
|
||||
GitHub 的源代码,请访问下面的链接:
|
||||
https://github.com/honeymoose/codebank-algorithm/blob/main/src/test/java/com/ossez/codebank/algorithm/tests/lintcode/LintCode0165MergeTwoListsTest.java
|
||||
|
||||
## 点评
|
||||
|
||||
本题主要涉及到链表的遍历和比较,基本上不会太难。
|
||||
|
||||
可能在写法上有点绕。
|
||||
@@ -0,0 +1,99 @@
|
||||
# 下一个斐波拉契数(Next Fibonacci Number)
|
||||
|
||||
> 🔔 参与讨论:https://www.isharkfly.com/t/next-fibonacci-number/15116
|
||||
|
||||
中文标题【下一个斐波拉契数】
|
||||
|
||||
Write a program that takes input of integer N, followed by N more integers.
|
||||
|
||||
For each integer, output the next fibonacci number after it.
|
||||
|
||||
Fibonacci number: Any number that belongs to the fibonacci series.
|
||||
|
||||
Constraints:
|
||||
|
||||
Your program should run correctly for the first 69 Fibonacci numbers.
|
||||
|
||||
Your output lines should not have any trailing or leading whitespace.
|
||||
|
||||
```
|
||||
Input
|
||||
3
|
||||
1
|
||||
9
|
||||
22
|
||||
|
||||
|
||||
Output
|
||||
2
|
||||
13
|
||||
34
|
||||
```
|
||||
|
||||
Explanation: 2 is the next fibonacci number greater than 1, the fibonacci number that comes after 9 is 13. 34 is the
|
||||
next fibonacci number after 22.
|
||||
|
||||
## 英文描述
|
||||
|
||||
英文描述请参考下面的图。
|
||||
|
||||

|
||||
|
||||
## 中文描述
|
||||
|
||||
根据给定的值,返回这个值后面的下一个斐波拉契数列中的下一个数。
|
||||
|
||||
在斐波拉契数列中存储 60 个 斐波拉契数。
|
||||
|
||||
例如,给定整数 1,那么应该返回的结果是 2 。因为给定整数 1 后面的下一个斐波拉契数是 2。
|
||||
|
||||
如果给定的数值是 9 的话,那么下一个斐波拉契数应该是 13。
|
||||
|
||||
斐波拉契数列又译为菲波拿契数列、菲波那西数列、斐波那契数列、黄金分割数列。
|
||||
|
||||
用文字来说,就是费波那契数列由0和1开始,之后的费波那契系数就是由之前的两数相加而得出。首几个费波那契系数是:
|
||||
|
||||
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233……(OEIS中的数列A000045)
|
||||
|
||||

|
||||
|
||||
## 思路和点评
|
||||
|
||||
首先计算斐波拉契数列,然后将数值存储到数组中。
|
||||
|
||||
定义一个数组,在这个数组中先存储 60 个从小到大的斐波拉契数。
|
||||
|
||||
然后将给定的数值与数值中存储的斐波拉契数进行对比,这个时候你需要对数组中的斐波拉契数进行遍历。当找到大于当前给定的整数以后,可以
|
||||
Break 这次比对并且返回(输出)这个值。
|
||||
|
||||
## 源代码
|
||||
|
||||
源代码和有关代码的更新请访问 GitHub:
|
||||
|
||||
https://github.com/cwiki-us/codebank-algorithm/blob/master/src/main/java/com/ossez/codebank/interview/ManNextFibonacciNumber.java
|
||||
|
||||
运行建议:
|
||||
|
||||
这个方法因为测试平台的问题,我们没有写到测试类中。我们是直接定义了一个可以运行的类。
|
||||
|
||||
你可以在你的 Eclipse 平台上,直接运行这个类。
|
||||
|
||||
在你运行类以后的 Console 控制台窗口,你首先需要输入数字 3 ,这个数字 3 表示这次运行你需要进行 3 次测试。
|
||||
|
||||
然后输入测试数字,例如,你可以输入测试数字 1,那么,程序将会输出 1 Next Fibonacci [2]。
|
||||
|
||||
这个与实际题目要求的有所差异,你需要进行调整,而且题目是需要使用 System.out.println 输出的,请注意我们在我们的源程序中注释掉了这个输出。
|
||||
|
||||
## 测试结果
|
||||
|
||||
上面程序的测试结果如下:
|
||||
|
||||
```
|
||||
3
|
||||
1
|
||||
2019/02/07 20:59:25 DEBUG [com.ossez.codebank.interview.ManNextFibonacciNumber] - 1 Next Fibonacci [2]
|
||||
9
|
||||
2019/02/07 20:59:46 DEBUG [com.ossez.codebank.interview.ManNextFibonacciNumber] - 9 Next Fibonacci [13]
|
||||
22
|
||||
2019/02/07 20:59:49 DEBUG [com.ossez.codebank.interview.ManNextFibonacciNumber] - 22 Next Fibonacci [34]
|
||||
```
|
||||
@@ -0,0 +1,42 @@
|
||||
# 岛屿个数(Number of Islands)
|
||||
|
||||
> 🔔 参与讨论:https://www.isharkfly.com/t/number-of-islands/15124
|
||||
|
||||
## 描述
|
||||
|
||||
给一个01矩阵,求不同的岛屿的个数。
|
||||
|
||||
0代表海,1代表岛,如果两个1相邻,那么这两个1属于同一个岛。我们只考虑上下左右为相邻。
|
||||
|
||||
## 样例
|
||||
|
||||
在矩阵:
|
||||
|
||||
```
|
||||
[
|
||||
[1, 1, 0, 0, 0],
|
||||
[0, 1, 0, 0, 1],
|
||||
[0, 0, 0, 1, 1],
|
||||
[0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 1]
|
||||
]
|
||||
```
|
||||
|
||||
中有 `3` 个岛。
|
||||
|
||||
## 代码
|
||||
|
||||
GitHub 的源代码,请访问下面的链接:
|
||||
https://github.com/honeymoose/codebank-algorithm/blob/main/src/test/java/com/ossez/codebank/algorithm/tests/lintcode/LintCode0433NumIslandsTest.java
|
||||
|
||||
## 点评
|
||||
|
||||
本质是求矩阵中连续区域的个数,很容易想到需要用深度优先搜索 DFS 来解,我们需要建立一个 visited 数组用来记录某个位置是否被访问过,对于一个为
|
||||
true 且未被访问过的位置,我们递归进入其上下左右位置上为 true 的数,将其 visited 对应值赋为
|
||||
true,继续进入其所有相连的邻位置,这样可以将这个连通区域所有的数找出来,并将其对应的 visited 中的值赋 true,找完次区域后,我们将结果
|
||||
res 自增 1,然后我们在继续找下一个为 true 且未被访问过的位置,以此类推直至遍历完整个原数组即可得到最终结果。
|
||||
|
||||
这里需要有一个递归的调用。在递归调用之前需要进行判断是否超出边际,如果超出边际的话,就要跳出循环。
|
||||
|
||||
在一个节点进行遍历的时候,需要在递归调用的时候,同时针对这个节点搜索上下左右 4 个节点,如果找到需要了满足条件的
|
||||
true,就继续查找,如果没有找到就退出。在这个过程的时候,需要将访问过的节点保存到访问控制的 2 维数组中。以便于在下次查找的时候跳过这个节点。
|
||||
@@ -0,0 +1,50 @@
|
||||
# Prime numbers from 1 to 100 (打印 100 以内的素数)
|
||||
|
||||
> 🔔 参与讨论:https://www.isharkfly.com/t/prime-numbers-from-1-to-100-100/13450
|
||||
|
||||
## 问题描述
|
||||
|
||||
下面是有关这个问题的描述部分。
|
||||
|
||||
### 中文
|
||||
|
||||
题目要求非常简单,就是将 100 以内的素数打印出来就可以了。
|
||||
|
||||
在做题之前,需要了解的就是有关素数的定义:质数又称素数。一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;否则称为合数(规定1既不是质数也不是合数)。
|
||||
|
||||
质数被利用在密码学上,所谓的公钥就是将想要传递的信息在编码时加入质数,编码之后传送给收信人,任何人收到此信息后,若没有此收信人所拥有的密钥,则解密的过程中(实为寻找素数的过程),将会因为找质数的过程(分解质因数)过久,使即使取得信息也会无意义。
|
||||
|
||||
在汽车变速箱齿轮的设计上,相邻的两个大小齿轮齿数设计成质数,以增加两齿轮内两个相同的齿相遇啮合次数的最小公倍数,可增强耐用度减少故障。
|
||||
|
||||
质素的用途比较高端,说白了就是在计算机科学中可以被用在密码学上面。比如说 Java 中用的 Hash 算法就用到了质素。
|
||||
|
||||
因此很多面试题喜欢考察 100 以内的素数。
|
||||
|
||||
100以内的质数有 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,在100内共有25个质数。
|
||||
|
||||
## 思路点评和源代码
|
||||
|
||||
这个题目的难度并不大。但是如果你没有遇到过或者没有刷过这个题目的话,可能就会被搞懵逼了。
|
||||
|
||||
这个题目有几个关键点,首先 1 是一个特殊的素数,因此你的循环需要从 2 开始。这个很多时候和我们使用的循环从 0 开始有不同。
|
||||
|
||||
另外这个题目需要 2 个循环来做,因为使用了 2 个循环可能比较容易就把你搞晕了。
|
||||
|
||||
为了避免这种情况,建议你使用函数来做。
|
||||
|
||||
你可以创建一个函数来判断给定的数字是不是素数。这个函数也非常简单,对于给定的值,从数字 2 开始遍历。假设现在这个函数的输入是
|
||||
5,那么你的循环要从 2 开始,然后将 5 %2, 这个是取余的运算符。
|
||||
|
||||
很显然 5%2 =1, 5%3=2, 5%4 =1。在这个遍历过程中,如果你能发现有数字能够被除尽,那么这个被除数就不是素数,如果你发现遍历完成了也不能被除尽,那么这个数就是素数。
|
||||
|
||||
这个函数只返回 T/F 就可以了。
|
||||
|
||||

|
||||
|
||||
这个题目能够快速解答的关键就是需要使用定义的**函数来做**,尽量不要在循环里面嵌套循环,因为这样你会很难跳出这个循环,并且循环的起止判断不好判断。
|
||||
|
||||
另外还有一点需要注意的是,有些在线测试平台可能不会让你使用 List,这个时候你需要考虑使用数组或者直接输出。
|
||||
|
||||
### 代码
|
||||
|
||||
https://github.com/cwiki-us-docs/java-tutorials/blob/master/toolkits/codebank/src/test/java/com/ossez/toolkits/codebank/tests/algorithm/PrimeNumbersTest.java
|
||||
@@ -0,0 +1,89 @@
|
||||
# 机器人移动(Robot Movement)
|
||||
|
||||
> 🔔 参与讨论:https://www.isharkfly.com/t/robot-movement/312
|
||||
|
||||
中文标题【机器人移动】
|
||||
|
||||
这个题目是 Kayak 发布的代码挑战题目。
|
||||
|
||||
我认为题目本身描述的不是十分清楚,方法需要返回结果,但是结果没有说明是机器人最后的坐标位置,还是最后的坐标位置距离原点的距离。同时,机器人的初始化方向等都没有十分明确的定义。
|
||||
|
||||
根据测试数据,机器人应该是从下往上(初始化的方向)。因为如果初始化的方向不确定的话,最后的坐标值可能会不一致。
|
||||
|
||||
我这里假设程序应该返回的是机器人的最后坐标位置,并且初始化的位置为 (0,0),方向为从下往上。
|
||||
|
||||
## 英文描述
|
||||
|
||||
A robot lands on Mars, which happens to be a cartesian grid; assuming that we hand the robot these instructions, such as
|
||||
LFFFRFFFRRFFF, where "L" is a "turn 90 degrees left", "R" is a "turn 90 degrees right", and "F" is "go forward one
|
||||
space。
|
||||
|
||||
please write control code for the robot such that it ends up at the appropriate-and-correct destination, and include
|
||||
unit tests.
|
||||
|
||||
Here is an example output with command "FF":
|
||||
|
||||
[0, 2]
|
||||
|
||||
## 中文描述
|
||||
|
||||
这里我不按照原文一字一字的翻译,但是尽量按照题目的要求把题目解释清楚。
|
||||
|
||||
这里题目的要求是,假设一个机器人降落到火星上了,我们现在需要给机器人发布指令。指令包括有 L,R,F 3 个。
|
||||
|
||||
L 表示的意思是机器人向左转 90 度,R 的意思表示的是机器人向右转 90 度,F 表示的是机器人向前移动一个坐标单位。
|
||||
|
||||
题目的表达并是是十分明确也清晰,比如说 LFFFRFFFRRFFF 应该返回是什么没有说清楚。假设 指令 FF ,返回的结果是 [0, 2]
|
||||
,我默认的是程序需要返回机器人最后的坐标位置,0 表示的是 X 坐标,2 表示的是 Y 坐标。
|
||||
|
||||
## 思路和点评
|
||||
|
||||
这个问题的思路,首先你需要明白几个点。如果需要进行坐标计算的话,请注意 L 和 F 是不会改变当前机器人的坐标位置的。
|
||||
|
||||
只有 F 的操作才会改变机器人的位置。考虑设置一个坐标系,那么这里需要存储 2 个信息,第一个信息为 F 移动时候机器人的位置,另外就是
|
||||
L 和 F 对机器人方向的控制了。
|
||||
|
||||
所以你需要在程序中初始化一个二维数组,这个数组用于存储 F 操作时候的坐标变化。
|
||||
|
||||
同时你还需要存储一个 dir 变量,通常这个变量为每一次 L 和 R 操作的时候方向的变化。
|
||||
|
||||
F 存储的路径数组为:int[][] move = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };
|
||||
|
||||
通过这个数组,你就明白为什么我在这个 WIKI 页面前面说的,初始化方向很重要,请参考下面的图(因为不太好用计算机画图,我们用手画了一个图)。
|
||||
|
||||

|
||||
|
||||
在这个图中比较明确的说明了,我们定义的初始化方向为从下往上,Dir 等于 0 。在 Dir 等于 0 的时候坐标数组为 int[][] move = { {
|
||||
0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } }; 按照顺时针的方向。
|
||||
|
||||
在图中,Dir 有 4 个反向,按照顺时针方向,分别为上,右,下,左,那么方向对应的值就分别为 0,1,2,3 。
|
||||
|
||||
当方向为 L 的时候,需要将方向值减 1 ,当方向为 R 的时候,需要将值 +1。
|
||||
|
||||
这里有个问题为循环,比如说,方向值为 RR,,dir 的值应该为 2。
|
||||
|
||||
如果方向为 RRRRRR,那么值应该也为 2。所以在算法中我们使用了 dir = (dir + 4) % 4;, 对方向进行取 余数。你可以看到 当你旋转
|
||||
RRRRRR 后,dir 的值还是为 2。
|
||||
|
||||
针一次转向 + 移动的操作,不管你转向多少次,调整的方方向无非就是调整 X 或者 Y 的坐标系,在下一次移动的时候应该是 + 还是 -
|
||||
|
||||
所以到这里方法就相对简单了。一次移动的时候,都会改变 X 和 Y 坐标的值,前提是你是希望怎么加减而已。
|
||||
|
||||
## 源代码
|
||||
|
||||
源代码和有关代码的更新请访问 GitHub:
|
||||
|
||||
https://github.com/cwiki-us/codebank-algorithm/blob/master/src/main/java/com/ossez/codebank/interview/KayakRobotMovement.java
|
||||
|
||||
测试类请参考:
|
||||
|
||||
https://github.com/cwiki-us/codebank-algorithm/blob/master/src/test/java/com/ossez/codebank/interview/tests/KayakTest.java
|
||||
|
||||
## 测试结果
|
||||
|
||||
上面程序的测试结果如下:
|
||||
|
||||
```
|
||||
2018/12/25 15:08:50 DEBUG [com.ossez.codebank.interview.tests.KayakTest] - LFFF - [0,2]
|
||||
2018/12/25 15:08:50 DEBUG [com.ossez.codebank.interview.tests.KayakTest] - LFFFRFFFRRFFF - [-3,0]
|
||||
```
|
||||
@@ -0,0 +1,43 @@
|
||||
# 二叉树的序列化和反序列化(Serialize and Deserialize Binary Tree)
|
||||
|
||||
> 🔔 参与讨论:https://www.isharkfly.com/t/serialize-and-deserialize-binary-tree/15125/1
|
||||
|
||||
中文标题【二叉树的序列化和反序列化】
|
||||
|
||||
## 描述
|
||||
|
||||
设计一个算法,并编写代码来序列化和反序列化二叉树。将树写入一个文件被称为“序列化”,读取文件后重建同样的二叉树被称为“反序列化”。
|
||||
|
||||
如何反序列化或序列化二叉树是没有限制的,你只需要确保可以将二叉树序列化为一个字符串,并且可以将字符串反序列化为原来的树结构。
|
||||
|
||||
对二进制树进行反序列化或序列化的方式没有限制,LintCode将您的`serialize`输出作为`deserialize`的输入,它不会检查序列化的结果。
|
||||
|
||||
## 样例
|
||||
|
||||
给出一个测试数据样例, 二叉树`{3,9,20,#,#,15,7}`,表示如下的树结构:
|
||||
|
||||
```
|
||||
3
|
||||
/ \
|
||||
9 20
|
||||
/ \
|
||||
15 7
|
||||
```
|
||||
|
||||
我们的数据是进行 BFS 遍历得到的。当你测试结果 wrong answer时,你可以作为输入调试你的代码。
|
||||
|
||||
你可以采用其他的方法进行序列化和反序列化。
|
||||
|
||||
## 代码
|
||||
|
||||
GitHub 的源代码,请访问下面的链接:
|
||||
|
||||
https://github.com/honeymoose/codebank-algorithm/blob/main/src/test/java/com/ossez/codebank/algorithm/tests/lintcode/LintCode0007SerializeAndDeserializeTest.java
|
||||
|
||||
## 点评
|
||||
|
||||
本题目主要需要你对二叉树的遍历方法有所了解。
|
||||
|
||||
遍历二叉树主要有 2 类方法,分别为深度优先(DFS)和广度优先(BFS)。
|
||||
|
||||
在深度优先中,你有又可以使用前序,中序和后序搜索方法,你可以使用递归或者非递归算法实现。对于广度优先算法,一般都会采用非递归的实现方法进行实现。
|
||||
Reference in New Issue
Block a user