0%

题目(LeetCode #28)

实现 strStr() 函数。

给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。

示例 1:

输入: haystack = “hello”, needle = “ll”
输出: 2

示例 2:

输入: haystack = “aaaaa”, needle = “bba”
输出: -1

题解

参考资料:

Java

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 KMP {
public static int[] getNextTable(String needle) {
int len = needle.length();
int[] next = new int[len];
next[0] = -1;
int lp = -1, rp = 0; // 左右指针, 即文章中的 k 和 index

while (rp < len - 1) {
if (lp == -1 || needle.charAt(rp) == needle.charAt(lp))
// lp = -1 时, 左右指针均向右移动
// 当左右指针所指元素相等时, 均向右移动
next[++rp] = ++lp;
else
// 左右指针所指元素不等, 左指针移向其所指字符的对应 next 指向的位置
lp = next[lp];
}

return next;
}

public static int getIndex(String haystack, String needle) {
int haystackLen = haystack.length();
int needleLen = needle.length();

if (needleLen == 0) return 0;
int haystackCur = 0, needleCur = 0;
int[] next = getNextTable(needle);

while (haystackCur < haystackLen && needleCur < needleLen) {
if (haystack.charAt(haystackCur) == needle.charAt(needleCur)) {
// 对比两个字符串, 各位相同时两指针右移
haystackCur++;
needleCur++;
} else {
// 不相同则根据 next 表查找 needleCur 移向的位置
// needleCur 移动到了开头, 退无可退, 主串指针继续后移
if (needleCur == 0) haystackCur++;
else needleCur = next[needleCur];
}
}

return needleCur == needleLen ? haystackCur - needleCur : -1;
}
}

Python

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
class KMP:
def get_next_table(self, needle):
length = len(needle)
lp, rp = -1, 0
next_table = [0] * length
next_table[0] = -1

while rp < length - 1:
if needle[lp] == needle[rp] or lp == -1:
rp += 1
lp += 1
next_table[rp] = lp
else:
lp = next_table[lp]

return next_table

def get_index(self, haystack, needle):
haystack_len = len(haystack)
needle_len = len(needle)

if needle_len == 0: return 0
haystack_cur, needle_cur = 0, 0
next_table = self.get_next_table(needle)

while haystack_cur < haystack_len and needle_cur < needle_len:
if haystack[haystack_cur] == needle[needle_cur]:
haystack_cur += 1
needle_cur += 1
else:
if needle_cur == 0: haystack_cur += 1
else: needle_cur = next_table[needle_cur]

return haystack_cur - needle_cur if needle_cur == needle_len else -1

Go

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
func getNextTable(needle string) []int {
length := len(needle)
lp, rp := -1, 0
next := make([]int, length)
next[0] = -1

for rp < length-1 {
if lp == -1 || needle[lp] == needle[rp] {
rp++
lp++
next[rp] = lp
} else {
lp = next[lp]
}
}

return next
}

func getIndex(haystack, needle string) int {
haystackLen := len(haystack)
needleLen := len(needle)

if needleLen == 0 {
return 0
}

haystackCur, needleCur := 0, 0
next := getNextTable(needle)

for haystackCur < haystackLen && needleCur < needleLen {
if haystack[haystackCur] == needle[needleCur] {
haystackCur++
needleCur++
} else {
if needleCur == 0 {
haystackCur++
} else {
needleCur = next[needleCur]
}
}
}

if needleCur == needleLen {
return haystackCur - needleCur
} else {
return -1
}
}

问题

一些项目要占用一个会议室宣讲, 会议室不能同时容纳两个项目的宣讲. 给定每个项目的开始和结束时间 (输入为数组, 其中是一个个具体的项目), 合理安排宣讲日程, 使得会议室进行的宣讲场次最多, 返回这个最多的场次

题解

本题不可按开始时间排序, 若某个宣讲开始时间早但持续时间长, 可能不如多个晚开始的宣讲场次多; 也不可按持续时间长短, 某个短时间的宣讲可能与其它宣讲的起始时间重叠.

Java

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
class Program {
int start;
int end;

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

public class BestArrange {
public static int bestArrange(Program[] programs, int start) {
Arrays.sort(programs, (a, b) -> a.end - b.end);
int result = 0;

for (Program program : programs) {
if (start <= program.start) {
result++;
start = program.end;
}
}

return result;
}
}

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Program:
def __init__(self, start, end):
self.start = start
self.end = end


def get_best_arrange(programs, start):
programs.sort(key=lambda x: x.end)
result = 0

for program in programs:
if start <= program.start:
result += 1
start = program.end

return result

Go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type Program struct {
start, end int
}

func getBestArrange(programs []Program, start int) int {
for i := 0; i < len(programs); i++ {
for j := 0; j < len(programs)-1; j++ {
if programs[j].end > programs[j+1].end {
programs[j], programs[j+1] = programs[j+1], programs[j]
}
}
}
result := 0

for _, program := range programs {
if start <= program.start {
result++
start = program.end
}
}

return result
}

题目

给定一个字符串类型的数组 strs , 找到一种拼接方式, 使得把所有字符串拼接起来之后形成的字符串具有最低的字典序.

题解

不可通过将各个数组元素按字典序排列再拼接的方式来解, 如对数组 ['b', 'ba'] 来说, 若按元素字典序来排列将得到 bba , 而实际上该结果并没有 bab 小, 故这种做法不可取.

本题应比较 str1 + str2str2 + str1 的字典序大小, 以此为标准重写比较函数, 排序后拼接即可.

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class LowestLexicography {
public static String lowestString(String[] strs) {
if (strs == null || strs.length == 0) {
return null;
}

Arrays.sort(strs, (a, b) -> (a + b).compareTo(b + a));

String result = "";
for (String str : strs) {
result += str;
}

return result;
}
}

Python

以冒泡排序为例

1
2
3
4
5
6
7
def get_lowest_lexicography(strings):
for i in range(len(strings)):
for j in range(len(strings) - 1):
if strings[j] + strings[j + 1] > strings[j + 1] + strings[j]:
strings[j], strings[j + 1] = strings[j + 1], strings[j]

return "".join(strings)

Go

1
2
3
4
5
6
7
8
9
10
11
func getLowestLexicography(strs []string) string {
for i := 0; i < len(strs); i++ {
for j := 0; j < len(strs)-1; j++ {
if strs[j]+strs[j+1] > strs[j+1]+strs[j] {
strs[j], strs[j+1] = strs[j+1], strs[j]
}
}
}

return strings.Join(strs, "")
}

题目(LeetCode #239)

给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回滑动窗口中的最大值。

示例:

1
2
3
4
5
6
7
8
9
10
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
[ 1 3 -1 ] -3 5 3 6 7 3
1 [ 3 -1 -3 ] 5 3 6 7 3
1 3 [ -1 -3 5 ] 3 6 7 5
1 3 -1 [ -3 5 3 ] 6 7 5
1 3 -1 -3 [ 5 3 6 ] 7 6
1 3 -1 -3 5 [ 3 6 7 ] 7

提示:

  • 1 <= nums.length <= 10^5
  • -10^4 <= nums[i] <= 10^4
  • 1 <= k <= nums.length

题解

窗口内最值的更新结构

借助单调双端队列实现, 队列由头到尾单调递减, 头指针始终指向窗口内的最大值. 定义两个指针 LR , 初始值为 -1, LR 之间即为窗口范围. 在窗口移动的过程中, 需满足:

  1. LR 不向左移动
  2. L 不能移动到 R 的右侧

定义一个双端队列, 其中保存输入数组中元素的索引.

  • 当窗口增加元素, 即 R 增加时, 需保持队列的单调性. 若新元素比原有元素都小, 则将其索引追加在最后; 若队列中有小于等于新元素的, 则先将所有小于等于新元素的索引弹出, 再加入新元素的索引. 以此实现队列的头指针始终指向最大元素

  • 当窗口减少元素, 即 L 增加时, 依次弹出头结点即可.

java

本题窗口宽度固定, 可用相对位置表示 LR

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
public class SlidingWindowMaximum {
public static int[] maxSlidingWindow(int[] nums, int k) {
if (nums == null || k < 1 || nums.length < k) {
return null;
}

LinkedList<Integer> maxQueue = new LinkedList<>();
// 共有 arr.length - k + 1 个窗口, 即共有arr.length - win + 1个最大值
int[] result = new int[nums.length - k + 1];
int index = 0;

// 用 i 表示 R
for (int i = 0; i < nums.length; i++) {
while (!maxQueue.isEmpty() && nums[maxQueue.peekLast()] <= nums[i]) {
maxQueue.pollLast();
}
maxQueue.addLast(i);

// i - k 为过期的索引, 即窗口的 L 已越过该位置, 需弹出头结点维持窗口大小为 k
if (maxQueue.peekFirst() == i - k) {
System.out.println(i + " " + k);
maxQueue.pollFirst();
}
if (i >= k - 1) {
result[index++] = nums[maxQueue.peekFirst()];
}
}

return result;
}
}

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class SlidingWindowMaximum:
def maxSlidingWindow(self, nums, k):
if not nums or k < 1 or len(nums) < k:
return

max_queue = []
result = []

for i in range(len(nums)):
while max_queue and nums[max_queue[-1]] <= nums[i]:
max_queue.pop()
max_queue.append(i)

if max_queue[0] == i - k:
max_queue = max_queue[1:]

if i >= k - 1:
result.append(nums[max_queue[0]])

return result

Go

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
func maxSlidingWindow(nums []int, k int) []int {
if nums == nil || k < 0 || len(nums) < k {
return nil
}

maxQueue := make([]int, len(nums))
result := make([]int, len(nums)-k+1)
index := 0

for i := 0; i < len(nums); i++ {
for len(maxQueue) > 0 && nums[maxQueue[len(maxQueue)-1]] <= nums[i] {
maxQueue = maxQueue[:len(maxQueue)-1]
}
maxQueue = append(maxQueue, i)

if maxQueue[0] == i-k {
maxQueue = maxQueue[1:]
}

if i >= k-1 {
result[index] =nums[maxQueue[0]]
index++
}
}

return result
}

题目(LeetCode #53)

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

解法

创建两个变量, currentmaxSum , 遍历数组, 将每个元素累加到 current 上, 再与 maxSum 比较, 使 maxSum 总保存 current 的最大值, 若累加过程中 current 变为负数, 则手动将其归零, 继续计算.

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MaxSumOfSubarray {
public static int maxSubArray(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
int current = 0;
int maxSum = Integer.MIN_VALUE;

for (int num : nums) {
current += num;
maxSum = Math.max(current, maxSum);
current = Math.max(current, 0);
}

return maxSum;
}
}

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MaxSumOfSubarray:
def maxSubArray(self, nums):
if not nums or len(nums) == 0:
return 0

current = 0
max_sum = float("-inf")

for num in nums:
current += num
max_sum = max(current, max_sum)
current = max(0, current)

return max_sum

Go

Go 中整型常量可定义如下

1
2
3
4
5
const INT_MAX = int(^uint(0) >> 1) 	// 有符号最大值
const INT_MIN = ^INT_MAX // 有符号最小值

const UINT_MIN uint = 0 // 无符号最小值, 即为 0
const UINT_MAX = ^uint(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
func maxSubArray(nums []int) int {
if nums == nil || len(nums) == 0 {
return 0
}

current := 0
maxSum := ^int(^uint(0) >> 1)

for _, num := range nums {
current += num
maxSum = max(current, maxSum)
current = max(0, current)
}

return maxSum
}

func max(a, b int) int {
if a > b {
return a
} else {
return b
}
}

func min(a, b int) int {
if a < b {
return a
} else {
return b
}
}

题目(LeetCode #42)

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

题解

本题可转化为求每根柱子上可接到的水量的和, 而每根柱子上可留住的水量又取决于其左右两侧最高的柱子的高度, 假设一根柱子高度为 1, 其左右两侧最高的柱子高度为 3 和 5, 则这根柱子上方可留住的水量为 3 - 1 = 2.

预处理数组法

若每次迭代均遍历查找左右两侧的最大值将增加时间复杂度. 故可采用预处理数组的方式, 将数组中每个元素左右两侧的最大值保存起来. 本例中使用两个辅助数组, 分别保存每个位置上的左侧最大值和右侧最大值. 如下例所示:

1
2
3
[3, 2, 4, 5, 4, 3, 1] # 原数组
[3, 3, 4, 5, 5, 5, 5] # 每个元素左侧最大值构成的辅助数组
[5, 5, 5, 5, 4, 3, 1] # 每个元素右侧最大值构成的辅助数组

该种解法的时间复杂度和空间复杂度均为为 O(n)

Java

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
public class TrapRainWater {
public static int trap(int[] heightArr) {
if (heightArr == null || heightArr.length < 0)
return 0;

int arrLen = heightArr.length;
int[] help1 = new int[arrLen];
int[] help2 = new int[arrLen];
int result = 0;

help1[0] = heightArr[0];
help2[arrLen - 1] = heightArr[arrLen - 1];

for (int i = 1; i < arrLen; i++) {
help1[i] = Math.max(help1[i - 1], heightArr[i]);
help2[arrLen - i - 1] = Math.max(help2[arrLen - i], heightArr[arrLen - i - 1]);
}

for (int i = 1; i < arrLen - 1; i++) {
int tmp = Math.min(help1[i - 1], help2[i + 1]) - heightArr[i];
result += Math.max(tmp, 0);
}

return result;
}

public static void main(String[] args) {
int[] heightArr = {0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1};
System.out.println(trap(heightArr));
}
}
1
6

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class TrapRainWater:
def trap(self, height_arr):
if not height_arr or len(height_arr) < 0:
return 0
arr_len = len(height_arr)
help1 = [0] * arr_len
help2 = [0] * arr_len
result = 0

help1[0] = height_arr[0]
help2[arr_len - 1] = height_arr[arr_len - 1]

for i in range(1, arr_len):
help1[i] = max(help1[i - 1], height_arr[i])
help2[arr_len - i - 1] = max(help2[arr_len - i], height_arr[arr_len - i - 1])

for i in range(1, arr_len - 1):
tmp = min(help1[i - 1], help2[i + 1]) - height_arr[i]
result += max(0, tmp)

return result

Go

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
func trap(heightArr []int) int {
if heightArr == nil || len(heightArr) < 0 {
return 0
}

arrLen := len(heightArr)
help1 := make([]int, arrLen)
help2 := make([]int, arrLen)
result := 0

help1[0] = heightArr[0]
help2[arrLen-1] = heightArr[arrLen-1]
for i := 1; i < arrLen; i++ {
help1[i] = max(help1[i-1], heightArr[i])
help2[arrLen-i-1] = max(help2[arrLen-i], heightArr[arrLen-i-1])
}

for i := 1; i < arrLen-1; i++ {
tmp := min(help1[i-1], help2[i+1])
result += max(0, tmp-heightArr[i])
}

return result
}

func max(a, b int) int {
if a > b {
return a
}
return b
}

func min(a, b int) int {
if a < b {
return a
}
return b
}

双指针法

本方法时间复杂度为 O(N) 空间复杂度为 O(1)

Java

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 TrapRainWater {
public static int trap(int[] heightArr) {
if (heightArr == null || heightArr.length < 3)
return 0;

int arrLen = heightArr.length;
int leftMax = heightArr[0];
int rightMax = heightArr[arrLen - 1];
int L = 1;
int R = arrLen - 2;
int result = 0;

while (L <= R) {
if (leftMax <= rightMax) {
result += Math.max(0, leftMax - heightArr[L]);
leftMax = Math.max(leftMax, heightArr[L++]);
} else {
result += Math.max(0, rightMax - heightArr[R]);
rightMax = Math.max(rightMax, heightArr[R--]);
}
}

return result;
}
}

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class TrapRainWater:
def trap(self, height_arr):
if not height_arr or len(height_arr) < 3:
return 0

arr_len = len(height_arr)
left_max = height_arr[0]
right_max = height_arr[arr_len - 1]
l = 1
r = arr_len - 2
result = 0

while l <= r:
if left_max <= right_max:
result += max(0, left_max - height_arr[l])
left_max = max(left_max, height_arr[l])
l += 1
else:
result += max(0, right_max - height_arr[r])
right_max = max(right_max, height_arr[r])
r -= 1

return result

Go

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
func trap(heightArr []int) int {
if heightArr == nil || len(heightArr) < 3 {
return 0
}

arrLen := len(heightArr)
leftMax := heightArr[0]
rightMax := heightArr[arrLen-1]
L := 1
R := arrLen - 2
result := 0

for L <= R {
if leftMax <= rightMax {
result += max(0, leftMax-heightArr[L])
leftMax = max(leftMax, heightArr[L])
L++
} else {
result += max(0, rightMax-heightArr[R])
rightMax = max(rightMax, heightArr[R])
R--
}
}

return result
}

func max(a, b int) int {
if a > b {
return a
}
return b
}

环境部署

在无 root 的情况下可将 cuda 安装到自己的目录中, 配置好环境变量即可使用.

服务器基础信息:

OS: Ubuntu 16.04
Kernel: x86_64 Linux 4.13.0-38-generic
CPU: Intel Xeon CPU E5-2620 v3 @ 3.2GHz
GPU: 双路 GeForce RTX 2080Ti
GPU驱动版本: 430.50
RAM: 128 GB
GCC: 5.4.1

CUDA 10 安装

到英伟达官网下载CUDA toolkit的 runfile, 本文以 10.0 版本为例

1
$ wget https://developer.nvidia.com/compute/cuda/10.0/Prod/local_installers/cuda_10.0.130_410.48_linux

赋予可执行权限

1
$ chmod +x ./cuda_10.0.130_410.48_linux

执行安装程序

1
$ ./cuda_10.0.130_410.48_linux

按空格向下滚动, accept 许可

提示是否安装显卡驱动, 由于已安装驱动, 且无 root 不可覆盖安装, 故输入 n 跳过

按 y 安装 CUDA Toolkit, 在此需输入自己的 CUDA 安装路径, 一定要选择自己有写入权限的目录, 若文件夹不存在将会自动创建, 在此以 /server_space/zhaoym/cuda-10.0 为例.

软链接无需创建, CUDA Samples 也无需安装

若 Toolkit 为 Installed 即为安装成功.

cuDNN 安装

首先下载 cuDNN 库压缩文件, 该链接不可直接通过 wget 下载, 可先使用浏览器打开, 再复制浏览器下载时跳转的地址进行下载

解压压缩文件

1
$ tar -zxvf cudnn-10.0-linux-x64-v7.6.5.32.tgz

得到名为 cuda 的文件夹, 其目录结构与 CUDA 安装路径的目录结构相同

1
2
3
4
5
6
7
8
9
cuda
├── NVIDIA_SLA_cuDNN_Support.txt
├── include
│   └── cudnn.h
└── lib64
├── libcudnn.so -> libcudnn.so.7
├── libcudnn.so.7 -> libcudnn.so.7.6.5
├── libcudnn.so.7.6.5
└── libcudnn_static.a

将该文件夹下的文件分别复制到之前安装 CUDA 10 的对应路径中, 本例中即为 /server_space/zhaoym/cuda-10.0

配置环境变量

打开 ~/.bashrc

1
$ vi ~/.bashrc

在末尾追加

1
2
export PATH=/server_space/zhaoym/cuda-10.0/bin:$PATH
export LD_LIBRARY_PATH=/server_space/zhaoym/cuda-10.0/lib64:$LD_LIBRARY_PATH

若之前有配置过其它版本的 CUDA, 需先将其注释掉

退出编辑器, 使环境变量生效

1
$ source ~/.bashrc

至此 CUDA 和 cuDNN 安装结束

1
$ nvcc -V

输出

1
2
3
4
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2018 NVIDIA Corporation
Built on Sat_Aug_25_21:08:01_CDT_2018
Cuda compilation tools, release 10.0, V10.0.130

YOLO v4 编译

项目主页: https://github.com/AlexeyAB/darknet

预训练权重: yolov4.weights

克隆项目

1
$ git clone https://github.com/AlexeyAB/darknet.git

修改 Makefile

1
$ vi Makefile

将前三行修改为1, RTX 2080 Ti 为图灵架构, 将 CUDNN_HALF 设为 1 可进一步加速计算

1
2
3
GPU=1
CUDNN=1
CUDNN_HALF=1

服务器默认 GCC/G++ 版本为 4.9.4, 这里改为使用更高的 5.4.1, 修改 60 和 62 行

1
2
3
4
5
ifeq ($(USE_CPP), 1)
CC=g++-5
else
CC=gcc-5
endif

全局查找并替换 /usr/local/cuda/server_space/zhaoym/cuda-10.0

按 ESC 进入命令模式, 输入以下内容 , 回车完成替换

1
:%s/\/usr\/local\/cuda/\/server_space\/zhaoym\/cuda-10.0/g

编译

1
$ make -j8

若编译出错, 修改后需 make clean 再重新 make

运行 demo

1
$ ./darknet detector test ./cfg/coco.data ./cfg/yolov4.cfg ./yolov4.weights

YOLO v4 的训练配置方式与 v3 相同, 可参考之前的文章: YOLO v3 环境搭建及训练入门指南

题目

逆序一个栈, 且不能使用额外的数据结构, 只可使用递归函数

题解

可将问题分为两个步骤:

  1. 弹出栈底元素并返回
  2. 获取栈底元素并将其放回栈顶

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ReverseStackUsingRecursive {
public static void reverse(Stack<Integer> stack) {
if (stack.isEmpty()) return;

int ele = getAndRemoveLastElement(stack);
reverse(stack);
stack.push(ele);
}

public static int getAndRemoveLastElement(Stack<Integer> stack) {
int result = stack.pop();
if (stack.isEmpty()) {
return result;
} else {
int last = getAndRemoveLastElement(stack);
stack.push(result);
return last;
}
}
}

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def reverse(stack):
if not stack or len(stack) == 0:
return
ele = get_and_remove_last_element(stack)
reverse(stack)
stack.append(ele)


def get_and_remove_last_element(stack):
result = stack.pop()
if len(stack) == 0:
return result
else:
last = get_and_remove_last_element(stack)
stack.append(result)
return last

Go

若不使用指针的指针则需注意引用传递, 否则会由于 stack 数组不更新进入死循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func reverse(stack []int) {
if stack == nil || len(stack) == 0 {
return
}

ele, stack := getAndRemoveLastElement(stack) // 需将 stack 重新赋值
reverse(stack)
stack = append(stack, ele)
}

func getAndRemoveLastElement(stack []int) (int, []int) {
result := stack[len(stack)-1]
stack = stack[:len(stack)-1]
if len(stack) == 0 {
return result, stack
} else {
last, stack := getAndRemoveLastElement(stack)
stack = append(stack, result)
return last, stack
}
}

题目

母牛每年生一头母牛, 新出生的母牛成长三年后也能每年生一头母牛, 假设牛不会死. 求 N 年后母牛的数量

题解

递归

归纳得 F(N) = F(N-1) + F(N-3)

该式实际上可解释为每年牛的总数等于前一年牛的总数 F(N-1) 加上三年前牛的总数 F(N-3) , 其中 F(N-3) 即为每年新增的牛的数量.

若规定牛的寿命为 10 年, 减去 F(N-10) 即可

Java
1
2
3
4
5
6
7
8
9
10
public class CowNumber {
public static int getCowNum(int N) {
if (N <= 2) return N + 1;
return getCowNum(N - 1) + getCowNum(N - 3);
}

public static void main(String[] args) {
System.out.println(getCowNum(8));
}
}
1
28
Python
1
2
3
4
5
def get_cow_number(n):
if n <= 2:
return n + 1

return get_cow_number(n - 1) + get_cow_number(n - 3)
Go
1
2
3
4
5
6
7
func getCowNum(n int) int {
if n <= 2 {
return n + 1
}

return getCowNum(n-1) + getCowNum(n-3)
}

斐波那契数列的矩阵求法

以上算法的时间复杂度为 O(N²) 本题可参考斐波那契数列的矩阵求法, 该算法时间复杂度仅为 O(logN)

推导如下:

1
2
3
F(n) = a*F(n-1) + b*F(n-2) + c*F(n-3)
F(n-1) = F(n-1)
F(n-2) = F(n-2)

本题中 a = 1, b = 0, c = 1

故可得

Python
1
2
3
4
5
6
7
8
9
10
11
import numpy as np

def fib_matrix(n):
if n <= 2:
return n + 1
result = np.mat([
[1, 0, 1],
[1, 0, 0],
[0, 1, 0]
], dtype='float64') ** (n - 2) * np.mat([3, 2, 1]).T
return int(result[0, 0])

题目

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

题解

每次递归都将后续字符与当前字符交换, 如第 i 次递归, 将 i 位置之后的字符分别与 i 处字符交换

若要去重使用集合即可

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class PrintPermutations {
public static void printPermutations(String str) {
char[] chars = str.toCharArray();
process(chars, 0);
}

public static void process(char[] chars, int i) {
if (i == chars.length) {
System.out.println(String.valueOf(chars));
}

for (int j = i; j < chars.length; j++) {
swap(chars, i, j);
process(chars, i + 1);
}
}

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

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
def print_permutations(str_input):
char_arr = [i for i in str_input]
process(char_arr, 0)


def process(char_arr, i):
arr_len = len(char_arr)
if i == arr_len:
print(''.join(char_arr))

for j in range(i, arr_len):
char_arr[i], char_arr[j] = char_arr[j], char_arr[i]
process(char_arr, i + 1)

Go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func printPermutations(input string) {
charArr := []byte(input)
process(charArr, 0)
}

func process(charArr []byte, i int) {
arrLen := len(charArr)
if i == arrLen {
fmt.Println(string(charArr))
}

for j := i; j < arrLen; j++ {
charArr[i], charArr[j] = charArr[j], charArr[i]
process(charArr, i+1)
}
}