算法学习(Golang实现)

文章目录

题目记录:

# 递归
70、509、226、112

# 分治
169、23

#单调栈
A: 739、239、402、316、901、496、581
B: 84、85、503、42、962

# 并查集
547、684、200、924
737、1102、1135、261、1061、323

# BFS
层序遍历:102、103、199、515、637
最短路径:542、994、1162、310
127、139、130、317、505、529、1263、1197、815、934

# DFS
934、1102、685、531、533、332、337、113
(岛屿问题)463、695、827、130

# 滑动窗口
A:76、438、3、567
B:209、1004、1208
C:340、1151、159、1100

# 区间问题:
A:56、986

# 前缀和
560、523、974

# 差分
1094、1109、121、122
253

# 二分查找
240、4、33

# 拓扑排序
210、269
444

# 字符串
5、93、43、227
1055

# 贪心
452、1231、1247、45、621、376

# 字典树
820、123、648、208

# 动态规划
213、1043、416、123、62、63、651、361、1066、750、1230

回溯

模板

result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return 
    
    for 选择 in 选择列表:
        # 做选择
        将该路径从选择列表中移除
        路径.add(选择)
        backtrack(路径, 选择列表)
        # 撤销选择
        路径.remove(选择)
        将该选择再加入选择列表

排列

46. 全排列

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

func permute(nums []int) [][]int {
	var res [][]int
	used := make([]bool, len(nums))
	var path []int
	dfs(nums, used, path, &res)
	return res
}

func dfs(nums []int, used []bool, path []int, res *[][]int){
    // 触发结束条件
	if len(path) == len(nums) {
		tmp := make([]int, len(nums), len(nums))
		copy(tmp, path)
		*res = append(*res, tmp)
		return
	}

	for i, v := range nums {
		if used[i] {
			continue
		}

		used[i] = true
		path = append(path, v)
		dfs(nums, used, path, res)
		used[i] = false
		path = path[:len(path) - 1]
	}
}


func main() {
	input := []int{1,2,3}
	res := permute(input)
	fmt.Println(res)
}

47.全排列II

给定一个可包含重复数字的序列,返回所有不重复的全排列。

func permuteUnique(nums []int) [][]int {
	var res [][]int
	used := make([]bool, len(nums))
	dfs(nums, used, []int{}, &res)
	return res
}

func dfs(nums []int, used []bool, path []int, res *[][]int) {
	if len(path) == len(nums) {
		tmp := make([]int, len(nums))
		copy(tmp, path)
		*res = append(*res, tmp)
		return
	}

    // rep不需要回滚,每一次调用dfs,此处重新分配内存
	rep := make(map[int]bool, len(nums)) 
	for i,v := range nums {
		if used[i] || rep[v]{
			continue
		}

		rep[v], used[i] = true, true
		path = append(path, v)
		dfs(nums, used, path, res)
		used[i] = false
		path = path[:len(path) - 1]
	}
}

51.N皇后

n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

var res [][]string
func solveNQueens(n int) [][]string {
	res = [][]string{}        // 使用全局变量时要初始化一遍,否则影响结果
	used := make([][]bool, n) // 制造二维bool阵
	for i := 0; i < n; i++ {
		used[i] = make([]bool, n)
	}
	trackBack(used, [][]byte{})
	return res
}

func trackBack(used [][]bool, path [][]byte) {
	if len(path) == len(used) {
		t := make([]string, len(path))
		for k,bs := range path {
			t[k] = string(bs)
		}
		res = append(res,t)
	}

	for j := 0; j < len(used); j++ { //此处j代表每一行的第j个元素,也就是列,每个位置轮流检测是否符合条件
		if !valid(used, len(path), j) { // 减支,path的行数能够代表已经铺到第几层了
			continue
		}
		bs := getLine(len(used))
		bs[j] = 'Q'
		used[len(path)][j] = true  //棋盘放子
		path = append(path,bs)     // 加入选择路径
		trackBack(used, path)      // 递归
		path = path[:len(path)-1]  // 回溯
		used[len(path)][j] = false // used回滚一定要在track回滚之后,len(path)的长度才变化了,否则不是回滚到真正的位置
	}
}

func valid(used [][]bool, row, col int) bool { // 只需要校验左、左上、上、右上
	var i, j int
	for i = 0; i < row; i++ { // 上
		if used[i][col] == true {
			return false
		}
	}

	for j = 0; j < len(used); j++ { //左
		if used[row][j] == true {
			return false
		}
	}

	i, j = row, col
	for i >= 0 && j >= 0 { // 左上
		if used[i][j] == true {
			return false
		}
		i--
		j--
	}

	i,j = row, col
	for i >= 0 && j< len(used) { //右上
		if used[i][j] == true {
			return false
		}
		j++
		i--
	}
	return true
}

func getLine(n int) []byte{
	bs := make([]byte,n)
	for i:=0;i<n;i++{
		bs[i] = '.'
	}
	return bs
}

子集

78.子集

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

func subsets(nums []int) [][]int {
	var res [][]int
	dfs(nums, 0, []int{}, &res)
	return res
}

func dfs(nums []int, start int, path []int, res *[][]int) {
	tmp := make([]int, len(path)) // 子集从最少的时候追加,没有退出条件
	copy(tmp, path)
	*res = append(*res, tmp)

	for i := start; i < len(nums); i++ {
		path = append(path, nums[i])
		dfs(nums, i + 1, path, res)
		path = path[:len(path) - 1]
	}
}

90.子集 II

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

func subsetsWithDup(nums []int) [][]int {
	sort.Ints(nums)
	var res [][]int
	dfs(nums, 0, []int{},&res)
	return res
}

func dfs(nums []int, start int, path []int, res *[][]int) {
	tmp:= make([]int, len(path))
	copy(tmp, path)
	*res =append(*res, tmp)

	for i:= start; i < len(nums); i++ {
        if i > start && nums[i] == nums[i - 1] { //例如{1,2,2},第一个2用过了,就略过第二个2
			continue
		}
		path = append(path, nums[i])
		dfs(nums, i + 1, path, res)
		path = path[: len(path) - 1]
	}
}

组合

77.组合

给定两个整数 nk,返回 1 … n 中所有可能的 k 个数的组合。

func combine(n int, k int) [][]int {
    var res [][]int
    dfs(n, k, 1, []int{}, &res) // 从1开始
    return res
}

func dfs(n, k, start int, path []int, res *[][]int) {
    if len(path) == k {
        tmp := make([]int, len(path))
        copy(tmp, path)
        *res = append(*res, tmp)
        return
    }

    for i := start; i <= n; i++ {
        path = append(path, i)
        dfs(n, k, i + 1, path, res) // 同个元素不能重复使用,传i + 1进去
        path = path[: len(path) - 1]
    }
}

39.组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

func combinationSum(candidates []int, target int) [][]int {
    var res [][]int
    dfs(candidates, 0, []int{}, target, &res)
    return res
}

func dfs(candidates []int, start int, path []int, target int,res *[][]int) {
    if target == 0 {
        tmp := make([]int, len(path))
        copy(tmp, path)
        *res = append(*res, tmp)
        return
    }

    for i := start; i < len(candidates); i++ {
        if target < candidates[i] {
            continue
        }

        path = append(path, candidates[i])
        // 同个元素可以重复使用,传i进去
        // targrt 减去当前元素值,减少求和的步骤
        dfs(candidates, i, path, target - candidates[i], res) 
        path = path[: len(path) - 1]
    }
}

40. 组合总和 II

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

说明:

所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。

func combinationSum2(candidates []int, target int) [][]int {
	sort.Ints(candidates)
	var res [][]int
	dfs(candidates, 0, target, []int{}, &res)
	return res
}

func dfs(candidates []int, start int, target int, path []int, res *[][]int) {
	if target == 0 {
		tmp := make([]int, len(path))
		copy(tmp, path)
		*res = append(*res, tmp)
		return
	}

	for i := start; i < len(candidates); i++ {
		if i > start && candidates[i] == candidates[i - 1] {
			continue
		}

		if target < candidates[i] {
			return
		}
		path = append(path, candidates[i])
		dfs(candidates, i + 1, target - candidates[i], path, res)
		path = path[: len(path) - 1]
	}
}

22. 括号生成

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

func generateParenthesis(n int) []string {
	var res []string
	dfs(n, n, []byte{}, &res)
	return res
}

func dfs(left, right int, path []byte, res *[]string) { // left、right 剩下的括号数量
	if left > right { // 剩下的左括号多说明一定错误
		return
	}
	
	if left < 0 || right < 0 {
		return 
	}

	if left == 0 && right == 0 {
		*res = append(*res, string(path))
        return
	}
	
	path = append(path, '(')
	dfs(left - 1, right, path, res)
	path = path[: len(path) - 1]

	path = append(path, ')')
	dfs(left, right - 1, path, res)
	path = path[: len(path) - 1]
}

DFS

双指针

左右指针

快慢指针

滑动窗口

模板

/* 滑动窗口算法框架 */
void slidingWindow(string s, string t) {
    unordered_map<char, int> need, window;
    for (char c : t) need[c]++;
    
    int left = 0, right = 0;
    int valid = 0; 
    while (right < s.size()) {
        // c 是将移入窗口的字符
        char c = s[right];
        // 右移窗口
        right++;
        // 进行窗口内数据的一系列更新
        ...

        /*** debug 输出的位置 ***/
        printf("window: [%d, %d)\n", left, right);
        /********************/
        
        // 判断左侧窗口是否要收缩
        while (window needs shrink) {
            // d 是将移出窗口的字符
            char d = s[left];
            // 左移窗口
            left++;
            // 进行窗口内数据的一系列更新
            ...
        }
    }
}

76.最小覆盖子串

func minWindow(s string, t string) string {
	left, right, start, end, minLen := 0,0,0,0,math.MaxInt64
	windows, needs := make(map[byte]int, len(s)), make(map[byte]int, len(t))
	valid := 0

	tt := []byte(t) //这里要转成byte数组才能匹配上
	for _, v := range tt {
		needs[v]++
	}

	for right < len(s) {
		ch := s[right]
		right++
		if _,ok := needs[ch]; ok {
			windows[ch]++
			if windows[ch] == needs[ch] {
				valid++
			}
		}

		for valid == len(needs) {
			if right - left < minLen { // 注意一旦匹配上就要先更新长度
				start,end = left, right
				minLen = right - left
			}
			
			dd := s[left]
			left++
			if _,ok := needs[dd]; ok {
                // 此处要先扣除valid的值,否则windows更新完一定会匹配不上
				if windows[dd] == needs[dd] { 
					valid--
				}
				windows[dd]--
			}
		}
	}
	if minLen == math.MaxInt64{
		return ""
	}
	return s[start: end]
}

438. 找到字符串中所有字母异位词

给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。

func findAnagrams(s string, p string) []int {
	res := make([]int, 0)
	left, right, valid := 0,0,0
	windows, needs := make(map[byte]int, len(s)), make(map[byte]int, len(p))

	pp := []byte(p)
	for _, v := range pp {
		needs[v]++
	}

	for right < len(s) {
		ch := s[right]
		if _,ok := needs[ch]; ok {
			windows[ch]++
			if windows[ch] == needs[ch] {
				valid++
			}
		}
		right++

		for valid == len(needs){
			if right - left == len(pp) {
				res = append(res, left)
			}

			dd := s[left]
			if _,ok := needs[dd]; ok {
				if windows[dd] == needs[dd] {
					valid--
				}
				windows[dd]--
			}
			left++
		}
	}
	return res
}

3. 无重复字符的最长子串

func lengthOfLongestSubstring(s string) int {
	if len(s) == 0 || len(s) == 1 {
		return len(s)
	}
	
	str := []byte(s)
	window := make(map[byte]int, len(s))
	
	left, right, res := 0,0,0
	for right < len(str) {
		cur := str[right]
		right++
		window[cur]++
		for window[cur] > 1{
			last := str[left]
			left++
			window[last]--
		}
		res = max(res, right - left)
	}
	return res
}

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

567. 字符串的排列

给定两个字符串 s1s2,写一个函数来判断 s2 是否包含 s1 的排列。

换句话说,第一个字符串的排列之一是第二个字符串的子串。

func checkInclusion(s1 string, s2 string) bool {
	left, right, valid := 0,0,0
	windows,needs := make(map[byte]int, len(s2)), make(map[byte]int, len(s1))

	str := []byte(s1)
	for _, v := range str{
		needs[v]++
	}

	for right < len(s2) {
		cur := s2[right]
		right++
		if _, ok := needs[cur]; ok {
			windows[cur]++
			if windows[cur] == needs[cur] {
				valid++
			}
		}

		for valid == len(needs) {
			if len(s1) == right - left {
				return true
			}

			dd := s2[left]
			left++
			if _,ok := needs[dd];ok {
				if windows[dd] == needs[dd] {
					valid--
				}
				windows[dd]--
			}
		}
	}
	return false
}

区间调度

区间合并

56. 合并区间

给出一个区间的集合,请合并所有重叠的区间

1. 排序
2. 插入第一个
3. 依次判断第二个值是否小于res中的第二个值(注意res长度)
func merge(intervals [][]int) [][]int {
    // 这里一定要判,否则下面会panic
	if len(intervals) <= 1 { 
		return intervals
	}

	res := make([][]int, 0, len(intervals))
	sort.Slice(intervals, func(i, j int) bool {
		return intervals[i][0] < intervals[j][0]
	})
    
	res = append(res, intervals[0])
	for i := 1; i < len(intervals); i++ {
		if intervals[i][0] <= res[len(res) -1][1] {
			res[len(res) -1][1] = max(res[len(res) -1][1],intervals[i][1])
		} else {
			res = append(res, intervals[i])
		}
	}
	return res
}

区间交集

986. 区间列表的交集

func intervalIntersection(A [][]int, B [][]int) [][]int {
	i,j := 0,0
	res := make([][]int, 0, len(A) + len(B))
	for i < len(A) && j < len(B) {
		a1,a2 := A[i][0], A[i][1]
		b1,b2 := B[j][0], B[j][1]

		if a1 <= b2 && a2 >= b1 {
			res = append(res, []int{max(a1, b1), min(a2, b2)})
		}

		if b2 < a2 {
			j++
		}  else {
			i++
		}
	}
	return res
}

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

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

BFS

https://mp.weixin.qq.com/s?__biz=MzA5ODk3ODA4OQ==&mid=2648167212&idx=1&sn=6af5ffe5b69075b21bb4743ddcee4e7c&chksm=88aa236abfddaa7cae70b42edb299d0a52d9f1cc4fc1fdba1116972fc0ca0275b8bfdf10851b&token=1607921395&lang=zh_CN#rd

应用场景:层序遍历(102)、最短路径(1162)

102. 二叉树的层序遍历

func levelOrder(root *TreeNode) [][]int {
	res := make([][]int, 0)
	if root == nil {
		return res
	}
	queue := make([]*TreeNode, 0)
	queue = append(queue, root)

	for len(queue) > 0 {
		var tmp []*TreeNode
		nodeValue := make([]int, 0)
		for _,v := range queue {
			if v == nil {
				continue
			}
			if v.Left != nil {
				tmp = append(tmp, v.Left)
			}

			if v.Right != nil {
				tmp = append(tmp, v.Right)
			}

			nodeValue = append(nodeValue,v.Val)
		}
		res = append(res, nodeValue)
		queue = tmp
	}
	return res
}

1162 地图分析

你现在手里有一份大小为 N x N 的「地图」(网格) grid,上面的每个「区域」(单元格)都用 0 和 1 标记好了。其中 0 代表海洋,1 代表陆地,请你找出一个海洋区域,这个海洋区域到离它最近的陆地区域的距离是最大的。

我们这里说的距离是「曼哈顿距离」( Manhattan Distance):(x0, y0) 和 (x1, y1) 这两个区域之间的距离是 |x0 - x1| + |y0 - y1| 。

如果我们的地图上只有陆地或者海洋,请返回 -1。

func maxDistance(grid [][]int) int {
    queue := []*point{}

    // 首先把最初始的陆地加到辅助队列里
    for i := 0; i < len(grid); i++ {
        for j := 0; j < len(grid[0]); j++ {
            if grid[i][j] == 1 {
                queue = append(queue, &point{x: i, y: j})
            }
        }
    }

    if len(queue) == 0 || len(queue) == len(grid) * len(grid[0]) {
        return -1
    }

    dx := [4]int{1, 0, -1, 0}
    dy := [4]int{0, -1, 0, 1}

    // 初始距离设为-1, 因为进入第一层就加过1了
    distancce := -1 
    for len(queue) > 0 {
        distancce++
        //tmp用来装queue中所有点下一次会污染到的上下左右的区域
        tmp := []*point{} 
        for _, v := range queue {
            for i := 0; i < 4; i++ {
                tx := v.x + dx[i]
                ty := v.y + dy[i]

                // 不符合要求的掠略过
                if tx < 0 || tx >= len(grid) ||
                	ty < 0 || ty >= len(grid[0]) ||
                	grid[tx][ty] != 0 {
                    continue
                }

                tmp = append(tmp, &point{tx, ty})
                grid[tx][ty] = 2
            }
        }
        // 遍历完一边queue了,下一次要遍历被污染到的区域
        queue = tmp
    }
    return distancce
}

type point struct{
    x,y int
}

链表

type ListNode struct {
    Val int
    Next *ListNode
}

反转链表

反转链表

func reverseList(head *ListNode) *ListNode {
    if head == nil || head.Next == nil {
        return head
    }
    
    newHead := reverseList(head.Next)
    head.Next.Next = head
    head.Next = nil
    return newHead
}

反转链表前 N 个节点

func reverseList(head *ListNode, n int) *ListNode {
	if n == 1 {
		successor = head.Next
		return head
	}

	newHead := reverseList(head.Next, n-1)
	head.Next.Next = head
	head.Next = successor
	return newHead
}

反转从位置 mn 的链表

var successor *ListNode

func reverseList(head *ListNode, n int) *ListNode {
	if n == 1 {
		successor = head.Next
		return head
	}

	newHead := reverseList(head.Next, n-1)
	head.Next.Next = head
	head.Next = successor
	return newHead
}

func reverseBetween(head *ListNode, left int, right int) *ListNode {
	if left == 1 {
		return reverseList(head, right)
	}
	
	head.Next = reverseBetween(head.Next, left - 1, right - 1)
	return head
}

K个一组翻转链表

func reverseKGroup(head *ListNode, k int) *ListNode {
    if head == nil {
        return head
    }

    a,b := head, head
    for i := 0; i < k; i++ {
        if (b == nil) {
            return head
        }
        b = b.Next
    }

    new := reverse(a, b)
    a.Next = reverseKGroup(b, k)
    return new
}

func reverse(a *ListNode, b *ListNode) *ListNode {
    var pre *ListNode
    cur, next := a, a
    for cur != b {
        next = cur.Next
        cur.Next = pre
        pre = cur
        cur = next
    }
    return pre
}

前缀和

单调栈

739. 每日温度

解法:单调递增栈

func dailyTemperatures(temperatures []int) []int {
    stack := make([]int, 0, len(temperatures))
    res := make([]int, len(temperatures), len(temperatures))
    
    for i := len(temperatures) - 1;i >= 0; i-- {
        // 栈里面存的是索引值
        for len(stack) > 0 && temperatures[i] >= temperatures[stack[len(stack) -1]] {
            stack = stack[:len(stack) - 1]
        }
        
        // 栈里面最上面一个存的是最近的一个比当前值大的数
        if len(stack) > 0 {
            res[i] = stack[len(stack) - 1] - i
        }
        stack = append(stack, i)
    }
    return res
}
402. 移掉 K 位数字
func removeKdigits(num string, k int) string {
	res := make([]byte, 0, len(num)-k)
	for i := 0; i < len(num); i++ {
		for k > 0 && len(res) > 0 && num[i] < res[len(res)-1] {
			res = res[:len(res)-1]
			k--
		}
		res = append(res, num[i])
	}

    // 一定要下面这步,否则k=1时会跳过
	res = res[:len(res)-k]
	ans := strings.TrimLeft(string(res), "0")
	if ans == "" {
		ans = "0"
	}

	return ans
}
316. 去除重复字母
func removeDuplicateLetters(s string) string {
	if len(s) <= 1 {
		return s
	}

    // 记录字符总出现次数
	tmp := make(map[byte]int, len(s))
	for i := 0; i < len(s); i++ {
		tmp[s[i]]++
	}
    // 记录是否出现在栈中
	inStack := make(map[byte]bool, len(s))
    // 栈,用于记录最终结果
	stack := make([]byte, 0, len(s))
    
	for i := 0; i < len(s); i++ {
        // 无论是否入栈,该值出现后就应减去1次出现次数
		tmp[s[i]]--
        // 在栈中就不用参与比较了,也不能继续入栈了
		if inStack[s[i]] {
			continue
		}

        // 栈中元素必须大于0,该值必须小于栈末尾元素,栈末尾必须以后还能出现,此时循环让栈顶元素出栈
		for len(stack) > 0 && s[i] < stack[len(stack)-1] && tmp[stack[len(stack)-1]] > 0 {
            // 要出栈,说明栈中已经不存在该元素了
			inStack[stack[len(stack)-1]] = false
            // 出栈动作
			stack = stack[:len(stack) - 1]
		}
        // 让其他人出栈了,自己进栈
		stack = append(stack, s[i])
        // 证明自己进栈了
		inStack[s[i]] = true

	}
	return string(stack)
}
239. 滑动窗口最大值

解法:单调递减栈

func maxSlidingWindow(nums []int, k int) []int {
	stack := make([]int, 0 , len(nums))
	res := make([]int, 0 , len(nums) - k + 1)

	for i := 0; i < len(nums); i++ {
	// 栈里面存储的是(当前最大数的)索引值
		for len(stack) > 0 && nums[i] >= nums[stack[len(stack) - 1]] {
			stack = stack[:len(stack) - 1]
		}
		
		stack = append(stack, i)
        // i-k 是左边界下标
		if i-k+1 > stack[0] {
            // 窗口已越过栈首元素(下标值),出栈
			stack = stack[1:]
		}
        // 要开始存储结果了
		if i+1 >= k {
			res = append(res, nums[stack[0]])
		}
	}
	return res
}
901. 股票价格跨度

解法:单调递减栈

type StockSpanner struct {
    // 单调递减栈
    stack []int
    // 栈对应的小于等于自己的天数
    days []int
}


func Constructor() StockSpanner {
    return StockSpanner{
        stack:make([]int, 0, 10000),
        days:make([]int, 0, 10000),
    }
}

func (this *StockSpanner) Next(price int) int {
    // 鉴于等于,本来就算一天
    day := 1
    for len(this.stack) > 0 && price >= this.stack[len(this.stack) - 1] {
        // 先加上即将出栈的数值对应的days
        day += this.days[len(this.days) - 1]
        // 栈及对应的天数一起出去
        this.stack = this.stack[:len(this.stack) - 1]
        this.days = this.days[:len(this.days) - 1]
    }

    this.stack = append(this.stack, price)
    this.days = append(this.days, day)
    return day
}
962. 最大宽度坡

解法:

  1. 制作一个以nums[0]开头的单调递减栈,因为nums[0]一定要在,所以使用逐个判断append的方式
  2. nums从后往前逐个与栈中末尾元素对比,直到找到比自己大的元素(每一个都记录距离)
func maxWidthRamp(nums []int) int {
    stack := make([]int, 0, len(nums))
    // 完成以nums[0]开头的单调递减栈
    for i := 0; i < len(nums); i++ {
        if len(stack) == 0 || nums[i] < nums[stack[len(stack) - 1]] {
            stack = append(stack, i)
        }
    }

    res := 0
    // 从后往前逐一与栈首元素逐一比较
    for i := len(nums) - 1 ; i >= 0; i-- {
        for len(stack) > 0 && nums[i] >= nums[stack[len(stack) - 1]] {
            res = max(res, i - stack[len(stack) - 1])
            stack = stack[: len(stack) - 1]
        }
    }
    return res
}

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

并查集

1202. 交换字符串中的元素
func smallestStringWithSwaps(s string, pairs [][]int) string {
	strLength := len(s)
	parent, rank := make([]int, strLength), make([]int, strLength)
	for i := range parent{
		parent[i] = i
		rank[i] = 1
	}

	var find func(int) int
	find = func(x int) int {
		if parent[x] != x {
			parent[x] = find(parent[x])
		}
		return parent[x]
	}

	union := func(from,to int) {
		rootX := find(from)
		rootY := find(to)

		if rootX == rootY {
			return
		}

		if rank[rootX] < rank[rootY] {
			rootX, rootY = rootY, rootX
 		}
		rank[rootX] += rank[rootY]
		parent[rootY] = rootX
	}

	for _, row := range pairs {
		union(row[0], row[1])
	}

	groups := make(map[int][]byte, strLength)
	for i := range s{
		index := find(i)
		groups[index] = append(groups[index], s[i])
	}

	for _, bytes := range groups {
		sort.Slice(bytes, func(i, j int) bool { return bytes[i] < bytes[j] })
	}

	ans := make([]byte, strLength)
	for i := range ans {
		f := find(i)
		ans[i] = groups[f][0]
		groups[f] = groups[f][1:]
	}
	return string(ans)
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: PBFT(Practical Byzantine Fault Tolerance)共识算法是一种分布式系统中的共识算法,它用于在存在恶意节点的情况下达成一致。在Go语言中,可以使用Go的标准库以及第三方库来实现PBFT共识算法。 具体实现步骤如下: 1. 定义网络中的节点数量以及角色(例如:主节点,从节点)。 2. 实现请求阶段,即请求节点向其他节点发送请求消息。 3. 实现预备阶段,即其他节点对请求消息进行验证,如果足够多的节点同意,则进入下一个阶段。 4. 实现提交阶段,即同意请求的节点向其他节点发送提交消息。 5. 实现确认阶段,即其他节点对提交消息进行验证,如果足够多的节点同意,则完成共识。 实现PBFT共识算法需要对分布式系统和网络通信等方面有较深的了解,如果不熟悉可以先学习相关知识。 ### 回答2: Golang语言是一种由Google开发的开源编程语言,具有高效的并发性能和简单的语法。实现PBFT共识算法使用Golang语言可以提供良好的可读性和易于维护的代码。 PBFT(Practical Byzantine Fault Tolerance)共识算法是一种经典的拜占庭容错共识算法,用于在分布式系统中达成一致的决策。它可以容忍最多 f 个节点(其中 f 是拜占庭错误数),并确保所有正确节点在有限时间内达成一致。 在使用Golang实现PBFT共识算法时,我们可以使用Golang的并发机制,如goroutine和channel,来实现节点之间的消息传递和状态同步。 首先,我们需要定义一个PBFT节点的结构体,包含节点的标识符、状态、消息队列等信息。然后,我们可以使用goroutine创建多个节点,并通过channel进行通信。 在PBFT算法中,节点需要按照一定规则进行投票,并等待足够多的节点达成一致后才能继续执行下一步操作。我们可以使用Golang的select语句和channel来实现这一过程,通过select语句监听不同的channel,根据接收到的消息进行相应的操作。 在实现PBFT算法的消息传递过程中,我们可以使用Golang的网络编程库,如net包,来模拟节点之间的网络通信。节点之间可以通过TCP或UDP协议进行消息的传递和接收。 最后,我们需要编写一些测试用例来验证PBFT共识算法的正确性和性能。使用Golang的测试框架,例如testing包,可以方便地编写单元测试和性能测试。 总之,使用Golang语言实现PBFT共识算法可以充分发挥Golang的并发性能和简洁的语法特性。通过合理地利用Golang的并发机制和网络编程库,我们可以实现一个高效可靠的PBFT共识算法,并进行相关的测试和验证。 ### 回答3: Golang语言可以用于实现PBFT(Practical Byzantine Fault Tolerance)共识算法。PBFT是一种拜占庭容错的共识算法,适用于分布式系统中的节点之间存在可能故障和恶意行为的情况。 Golang语言有以下特点适合实现PBFT共识算法: 1. 并发性能强:Golang通过Goroutine和信道(Channel)实现了高效的并发,能够并行处理多个请求和节点之间的通信。这使得Golang非常适合处理PBFT算法中节点之间的消息传递和状态更新。 2. 跨平台支持广泛:Golang语言具有很好的跨平台支持,可以轻松部署在各种操作系统上。这对于实现一个可靠的共识算法来说非常重要,因为共识算法涉及到多个节点的交互和协同工作。 3. 内置网络库强大:Golang提供了内置的网络库,例如net包和http包,可以方便地实现节点之间的网络通信和消息传递。 4. 代码可读性好:Golang语言具有简洁的语法和良好的代码结构,使得代码易于阅读和理解。这对于共识算法这样复杂且关键的系统来说是至关重要的。 因此,使用Golang语言实现PBFT共识算法是非常可行的。开发者可以利用Golang的并发特性、跨平台支持和丰富的网络库进行快速而可靠的开发,以实现一个高性能和鲁棒的PBFT共识算法

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值