算法题-golang实现第二章


往期文章: 第一章

第二章

题目来源于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)) 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值