往期文章: 第一章
第二章
题目来源于Leetcode题库
1.三数之和
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
package main
import (
"fmt"
"sort"
)
func threeSum(nums []int) [][]int {
res := make([][]int, 0)
if len(nums) < 3 {
return res
}
sort.Ints(nums)
i := 0
last := len(nums) - 1
for i < last {
if nums[i] > 0 {
break
}
j := i + 1
k := last
a := nums[i]
for j < k {
b, c := nums[j], nums[k]
sum := nums[j] + nums[k] + nums[i]
if sum == 0 {
res = append(res, []int {nums[i], nums[j], nums[k]})
}
if sum <= 0 {
for j < k && nums[j] == b {
j++
}
}
if sum >= 0 {
for j < k && nums[k] == c {
k--
}
}
}
for i < last && nums[i] == a {
i++
}
}
return res
}
func main() {
nums := []int {0,0,0}
res := threeSum(nums)
fmt.Println(res)
}
2.岛屿的最大面积
给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合。你可以假设二维矩阵的四个边缘都被水包围着。
找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为0。)
示例 1:
[[0,0,1,0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,1,1,0,1,0,0,0,0,0,0,0,0],
[0,1,0,0,1,1,0,0,1,0,1,0,0],
[0,1,0,0,1,1,0,0,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,0]]
对于上面这个给定矩阵应返回 6。注意答案不应该是11,因为岛屿只能包含水平或垂直的四个方向的‘1’。
示例 2:
[[0,0,0,0,0,0,0,0]]
对于上面这个给定的矩阵, 返回 0。
注意: 给定的矩阵grid 的长度和宽度都不超过 50。
package main
import (
"fmt"
)
func maxAreaOfIsland(grid [][]int) int {
maxArea := 0
temp := 0
for i := 0; i < len(grid); i++ {
for j := 0; j < len(grid[0]); j++ {
temp = getArea(grid, i, j)
if maxArea <= temp {
maxArea = temp
}
}
}
return maxArea
}
func getArea(grid [][]int, x int, y int) int {
if x >= len(grid) || x < 0{
return 0
}
if y >= len(grid[0]) || y < 0{
return 0
}
if grid[x][y] == 0 || grid[x][y] == -1 {
return 0
}
//左右上下
grid[x][y] = -1
return 1 + getArea(grid, x, y+1) + getArea(grid, x, y-1) + getArea(grid, x-1, y) + getArea(grid, x+1, y)
}
func main() {
arr := [][]int {{1}}
fmt.Println(maxAreaOfIsland(arr))
}
3.搜索旋转排序数组
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例 2:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
package main
import (
"fmt"
)
func search(nums []int, target int) int {
lt := 0;
rt := len(nums) - 1;
for lt <= rt {
mid := (rt - lt) / 2 + lt;
if nums[mid] == target {
return mid;
} else if nums[mid] < nums[rt] { // mid在右半部分,代表mid向右的部分有序
if nums[mid] < target && nums[rt] >= target {
lt = mid + 1;
} else {
rt = mid - 1;
}
} else {// mid在左半部分,代表mid向左的部分有序
if nums[lt] <= target && nums[mid] > target {
rt = mid - 1;
} else {
lt = mid + 1;
}
}
}
return -1;
}
func main() {
nums := []int {4,5,6,7,0,1,2}
target := 0
fmt.Println(search(nums, target))
}
4.最长连续递增序列
给定一个未经排序的整数数组,找到最长且连续的的递增序列。
示例 1:
输入: [1,3,5,4,7]
输出: 3
解释: 最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为5和7在原数组里被4隔开。
示例 2:
输入: [2,2,2,2,2]
输出: 1
解释: 最长连续递增序列是 [2], 长度为1。
注意:数组长度不会超过10000。
package main
import (
"fmt"
)
func findLengthOfLCIS(nums []int) int {
if len(nums) == 0 {
return 0
}
count := 1
max := 0
for i := 0; i < len(nums) - 1; i++ {
if nums[i] >= nums[i+1] {
if max < count {
max = count
}
count = 1
} else {
count++
}
// fmt.Println("count: i = ", i, count)
}
if max < count {
max = count
}
return max
}
func main() {
nums := []int {1,3,5,7}
fmt.Println(findLengthOfLCIS(nums))
}
5.数组中的第K个最大元素
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:
你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。
package main
import (
"fmt"
)
func findKthLargest(nums []int, k int) int {
max := 0
index := 0
length := len(nums)
for i := 0; i < k; i++ {
if len(nums) - 1 - i < 0 {
break
}
max = nums[length - 1 - i]
for j := 0; j < length - i; j++ {
if max <= nums[j] {
max = nums[j]
index = j
}
}
nums[length - 1 - i], nums[index] = max, nums[length - 1 - i]
// fmt.Println("nums: ", nums)
}
return nums[length - k]
}
func find2(nums []int, k int) int {
length := len(nums)
for i := 0; i < length - 1; i++ {
for j := 0; j < length - 1 - i; j++ {
if nums[j] > nums[j+1] {
nums[j], nums[j+1] = nums[j+1], nums[j]
}
}
}
fmt.Println(nums)
return nums[length-k]
}
func main() {
nums := []int {3,2,1,5,6,4}
fmt.Println(findKthLargest(nums, 2))
// fmt.Println(find2(nums, 3))
}
6.最长连续序列
给定一个未排序的整数数组,找出最长连续序列的长度。
要求算法的时间复杂度为 O(n)。
示例:
输入: [100, 4, 200, 1, 3, 2]
输出: 4
解释: 最长连续序列是 [1, 2, 3, 4]。它的长度为 4。
package main
import(
"fmt"
)
func longestConsecutive(nums []int) int {
max := 0
left := 0
right := 0
mp := make(map[int]int)
for _, v := range nums {
if mp[v] == 0 {
left = mp[v-1]
right = mp[v+1]
mp[v] = left + right + 1
if left != 0 {
mp[v-left] = left + right + 1
}
if right != 0 {
mp[v+right] = left + right + 1
}
if max < left + right + 1 {
max = left + right + 1
}
}
}
return max
}
7.第k个排列
给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
“123”
“132”
“213”
“231”
“312”
“321”
给定 n 和 k,返回第 k 个排列。
说明:
给定 n 的范围是 [1, 9]。
给定 k 的范围是[1, n!]。
示例 1:
输入: n = 3, k = 3
输出: “213”
示例 2:
输入: n = 4, k = 9
输出: “2314”
思路:1.求出k处于n的第几组数,从未选择的数中按顺序获取可用的数;2.重复1步骤
package main
import(
"fmt"
"strconv"
)
func getPermutation(n int, k int) string {
mp := make(map[int]int, n) //保存选择状态的map,[1:0,2:0,...],key为数,val为0-未,1-已选择
res := "" //结果
group := 0 //商,也就是第几组
offset := 0 //属于第几组,选择位移
second := 0 //余数
total := 1 //阶乘积
times := n //次数
for i := 1; i <= n; i++ {
total *= i
}
for times > 0 {
total = total/times
group = k/total
second = k%total
if second > 0 {
k = second
offset = group + 1
} else {
offset = group
k = k / group //余数为0,说明在当前组内,需要/group作为下一位的数的k,因为group=k/a(每组数的数量)
}
res += strconv.Itoa(getMinNum(mp, offset, n))
fmt.Println(total, group, second, offset, res, mp)
times--
}
return res
}
func getMinNum(mp map[int]int, offset int, n int) int {
for i := 1; i <= n; i++ {
if mp[i] == 0 {
offset--
}
if offset == 0 {
mp[i] = 1
return i
}
}
return 0
}
func main() {
fmt.Println(getPermutation(4,9))
}
8.朋友圈
班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。
给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。
示例 1:
输入:
[[1,1,0],
[1,1,0],
[0,0,1]]
输出: 2
说明:已知学生0和学生1互为朋友,他们在一个朋友圈。
第2个学生自己在一个朋友圈。所以返回2。
示例 2:
输入:
[[1,1,0],
[1,1,1],
[0,1,1]]
输出: 1
说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,所以学生0和学生2也是朋友,所以他们三个在一个朋友圈,返回1。
注意:
N 在[1,200]的范围内。
对于所有学生,有M[i][i] = 1。
如果有M[i][j] = 1,则有M[j][i] = 1。
思路:深度优先DFS,按升序找,找到连接点,再以连接点往下找,直至标记所有尽可能到达的点
package main
import(
"fmt"
)
func findCircleNum(M [][]int) int {
length := len(M)
state := make([]int, length)
i := 0
count := 0
for i < length {
if state[i] ==0 {
findByDfs(i, M, state)
fmt.Println(state)
count++
}
i++
}
return count
}
func findByDfs(index int, M [][]int, state []int) {
if state[index] == 1 {
return
} else {
state[index] = 1
}
for i := 0; i < len(M[index]); i++ {
if i != index && M[index][i] == 1 {
findByDfs(i, M, state)
}
}
}
func main() {
M := [][]int {{1,0,0,1},{0,1,1,0},{0,1,1,1},{1,0,1,1}}
fmt.Println(findCircleNum(M))
}
9.合并区间
给出一个区间的集合,请合并所有重叠的区间。
示例 1:
输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入: [[1,4],[4,5]]
输出: [[1,5]]
解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。
思路:先以每一组的第一位以小到大排序,以mergeState标记处理过的组,每读取一个元素,遍历并且合并后续的元素,合并的标记已处理
func merge(intervals [][]int) [][]int {
res := make([][]int, 0)
left := 0
right := 0
length := len(intervals)
mergeState := make([]int, len(intervals))
intervals = sortNums(intervals)
for i := 0; i < length; i++ {
if mergeState[i] != 0 {
continue
}
mergeState[i] = 1
left = intervals[i][0]
right = intervals[i][1]
left, right = checkMerge(intervals, mergeState, left, right)
res = append(res, []int{left, right})
fmt.Println("res is ", res)
}
return res
}
func sortNums(nums [][]int) [][]int {
for i := 0; i < len(nums) - 1; i++ {
for j := 0; j < len(nums) - 1 - i; j++ {
if nums[j][0] > nums[j+1][0] {
nums[j][0], nums[j+1][0] = nums[j+1][0], nums[j][0]
nums[j][1], nums[j+1][1] = nums[j+1][1], nums[j][1]
}
}
}
return nums
}
func checkMerge(nums [][]int, mergeState []int, left int, right int) (int, int) {
index := 0
for index < len(nums) {
if mergeState[index] != 0 {
index++
continue
}
if right >= nums[index][0] {
mergeState[index] = 1
if right <= nums[index][1] {
right = nums[index][1]
}
}
index++
}
return left, right
}
func main() {
intervals := [][]int {{2,3},{4,6},{5,7},{3,4}}
fmt.Println(merge(intervals))
}
10.接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
func trap(height []int) int {
n := len(height);
result := 0;
if n == 0 || n == 1 {
return result;
}
left := 0;
right := n - 1;
leftHeight := 0;
rightHeight := 0;
for left < right {
if height[left] <= height[right] {
//从左到右,只有呈现下降趋势,才是雨水的地方
if leftHeight < height[left] {
leftHeight = height[left]
}
result += leftHeight - height[left];
left++;
} else {
//从右到左,只有呈现下降趋势,才是雨水的地方
if rightHeight < height[right] {
rightHeight = height[right]
}
result += rightHeight - height[right];
right--;
}
}
return result;
}
func main() {
// height := []int {2,0,2}
height := []int {5,4,1,2}
fmt.Println(trap(height))
}