面试题算法总结--go实现

1、反转字符串

leetcode:https://leetcode.cn/problems/reverse-string/
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出

func reverseString(s []byte)  {
    //双指针
    left, right := 0, len(s)-1
    for left < right{
        s[left],s[right] = s[right], s[left]
        left++
        right--
    }

}

2、最大的子数组和

leetcode: https://leetcode.cn/problems/maximum-subarray/
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。

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

func maxSubArray(nums []int) int {
    //典型的动态规划题目
    dp := make([]int, len(nums))
    dp[0] = nums[0]
    result := dp[0]
    for i := 1;i < len(nums);i++{
        dp[i] = max(dp[i-1]+nums[i], nums[i])
        result = max(result, dp[i])
    }
    return result
}
func max(x, y int) int{
    if x > y{
        return x
    }
    return y
}

3、求无重复字符的最长子串

leetcode:https://leetcode.cn/problems/longest-substring-without-repeating-characters/
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = “abcabcbb”
输出: 3

func lengthOfLongestSubstring(s string) int {
    len := len(s)
    if len < 1{
        return 0
    }
    maxLen := 1
    left, right, windows := 0, 0, make(map[byte]bool)
    for right < len {
        rightChan := s[right]
        for windows[rightChan]{
            delete(windows, s[left])
            left ++
        }
        if right - left +1 > maxLen{
            maxLen = right - left +1
        }
        windows[rightChan] = true
        right ++
    }

    return maxLen

}

4、回文数

leetcode:https://leetcode.cn/problems/palindrome-number/
给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。

回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

例如,121 是回文,而 123 不是。

func isPalindrome(x int) bool {
    str := strconv.Itoa(x)
    left := 0
    right := len(str)-1
    for left <= right{
        if str[left] != str[right]{
            return false
        }
        left++
        right--
    }
    return true

}

5、最长回文子串

leetcode:https://leetcode.cn/problems/longest-palindromic-substring/
给你一个字符串 s,找到 s 中最长的回文子串。

func longestPalindrome(s string) string {
    if len(s) == 0{
        return ""
    }
    strLen := len(s)
    left := 0
    right := 0
    tmpLen := 1
    maxLen := 0
    maxStart := 0
    for i := 0; i < strLen; i++{
        left = i - 1
        right = i + 1

        for left >= 0 && s[left] == s[i]{
            left-- 
            tmpLen++
        }
        for right < strLen && s[right] == s[i]{
            right++
            tmpLen++
        }
        for left >= 0 && right < strLen && s[right] == s[left]{
            tmpLen = tmpLen + 2
            left--
            right++
        }
        if tmpLen > maxLen{
            maxLen = tmpLen
            maxStart = left
        }
        tmpLen = 1
    } 
    return s[maxStart+1:maxStart+maxLen+1]


}

6、查找数组中次数过半的数

leetcode: https://leetcode.cn/problems/majority-element/?favorite=2cktkvj
给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

func majorityElement(nums []int) int {
    sort.Ints(nums)
    return nums[len(nums)/2]

}

7、反转链表

leetcode:https://leetcode.cn/problems/reverse-linked-list/?favorite=2cktkvj
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func reverseList(head *ListNode) *ListNode {
    var prev *ListNode
    curr := head
    for curr!=nil{
        next:=curr.Next
        curr.Next = prev
        prev = curr
        curr = next
    }
    return prev

}

8、回文链表

leetcode:https://leetcode.cn/problems/palindrome-linked-list/
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func isPalindrome(head *ListNode) bool {
    if head == nil{
        return true
    }
    test := head
    for test != nil{
        fmt.Println("test:", test)
        test=test.Next
    }

    //找到中间节点
    firstHalfEnd := searchTail(head)
    fmt.Println("firstHalfEnd:",firstHalfEnd.Next)
    //反转链表 注意反转链表的起始位置
    secondHalfStart := revertList(firstHalfEnd.Next)
    // tmp := slow
    //双指针判断是否回文
    p1 := head
    p2 := secondHalfStart
    result := true

    for result && p2 != nil {
        if p1.Val != p2.Val {
            result = false
        }
        p1 = p1.Next
        p2 = p2.Next
    }
    //还原链表
    firstHalfEnd.Next = revertList(secondHalfStart)
    return result

}
//找到尾结点
func searchTail(head *ListNode) *ListNode{
    fast := head
    slow := head
    for fast.Next != nil && fast.Next.Next != nil{
        slow = slow.Next
        fast = fast.Next.Next
    }
    return slow
}
//反转链表
func revertList(head *ListNode) *ListNode{
        var prev, cur *ListNode = nil, head
    for cur != nil {
        nextTmp := cur.Next
        cur.Next = prev
        prev = cur
        cur = nextTmp
    }
    return prev
}

9、合并两个有序数组

leetcode:https://leetcode.cn/problems/merge-sorted-array/
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

func merge(nums1 []int, m int, nums2 []int, n int)  {
    i,j,k := m-1,n-1,m+n-1
    // 结束条件,nums2完全插入nums1,即j<0时退出循环
    for j>=0{
        if i>=0&&nums1[i]>=nums2[j]{
            nums1[k] = nums1[i]
            k--
            i--
        }else{
            nums1[k] = nums2[j]
            k--
            j--
        }
        // fmt.Printf("i:%d,j:%d,k:%d\n",i,j,k)
        // fmt.Println(nums1)
    }

}

快手面试出了一道类似题,数组 奇数位正序,偶数位倒序,要求整体排成正序,不能用简单排序算法,空间复杂度没要求,时间复杂度越小越好

package main

import "fmt"

func main() {
	nums := []int{10, 70, 30, 50, 60, 20, 80, 5}
	fmt.Println(sortNum(nums))
}

// 数组 奇数位正序,偶数位倒序,要求整体排成正序,不能用简单排序算法,
// 空间复杂度没要求,时间复杂度越小越好
func sortNum(nums []int) []int {
	n := len(nums)
	arr := make([]int, n)
	k := 0
	i := 0        //奇数位开始元素
	j := n - 1    //偶数位开始元素
	tail := n - 2 //奇数位末尾元素
	if n%2 != 0 {
		j = n - 2
		tail = n - 1
	}
	for i <= tail || j >= 0 {
		if i <= tail && j >= 0 && nums[i] < nums[j] {
			arr[k] = nums[i]
			k++
			i = i + 2
		} else if i <= tail && j >= 0 && nums[i] >= nums[j] {
			arr[k] = nums[j]
			k++
			j = j - 2
		} else if i > tail && j >= 0 {
			for j >= 0 {
				arr[k] = nums[j]
				j = j - 2
				k++
			}
		} else if j < 0 && i <= tail {
			for i <= tail {
				arr[k] = nums[i]
				i = i + 2
				k++
			}
		}
		// fmt.Println(arr)
		// fmt.Printf("i=%d,j=%d\n", i, j)
	}
	return arr
}

10、寻找重复数

leetcode: https://leetcode.cn/problems/find-the-duplicate-number/?favorite=2cktkvj
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。

假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。

你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。

输入:nums = [1,3,4,2,2]
输出:2

使用hashmap实现,但是不符合空间复杂度要求

func findDuplicate(nums []int) int {
    var numMap map[int]bool
    numMap = make(map[int]bool,len(nums))
    for _,k := range nums{
        if numMap[k] == true{
            return k
        }
        numMap[k] = true
    }
    return -1

}

11、爬楼梯

leetcode: https://leetcode.cn/problems/climbing-stairs/?favorite=2cktkvj
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

func climbStairs(n int) int {
    // 使用动态规划求解
    if n <= 2{
        return n
    }
    var dp []int
    dp = make([]int, n+1)
    dp[1] = 1
    dp[2] = 2
    for i := 3; i <= n; i++{
        dp[i] = dp[i-1] + dp[i-2]
    }
    return dp[n]

}

https://leetcode.cn/problems/fibonacci-number/
斐波那契数列代码类似

func fib(n int) int {
    if n < 2{
        return n
    }
    dp := make([]int,n+1)
    dp[0] = 0
    dp[1] = 1
    for i:= 2;i<=n;i++ {
        dp[i] = dp[i-1] + dp[i-2]
    }
    return dp[n]
}

12、有效的括号

LeetCode:https://leetcode.cn/problems/valid-parentheses/?favorite=2cktkvj
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号

栈先入后出特点恰好与本题括号排序特点一致,即若遇到左括号入栈,遇到右括号时将对应栈顶左括号出栈,则遍历完所有括号后 stack 仍然为空
使用哈希表,key为右括号,value 为左括号

func isValid(s string) bool {
    var pairs map[byte]byte = map[byte]byte{')':'(','}':'{',']':'['}
    stack := []byte{}
    for i:=0; i < len(s); i++{
        if len(stack) > 0 && stack[len(stack)-1] == pairs[s[i]]{
            stack = stack[:len(stack)-1]
        }else{
            stack = append(stack,s[i])
        }
    } 
    if len(stack) == 0{
        return true
    }
    return false
}

13、判断字符串是否为合法ip

思路
1、用"."分割ip
2、判断分割后的切片长度是否为4
3、判断切片中每个字符串是否都可以转换为数字,且在0~255之内

func main() {
	//判断字符串是否为合法ip
	ip := "11.150.131.255"
	fmt.Println(checkIp(ip))

}
func checkIp(ip string) bool {
	//将字符串进行分割
	ip_list := strings.Split(ip, ".")
	if len(ip_list) != 4 {
		return false
	}
	for _, v := range ip_list {
		num, ok := strconv.Atoi(v)
		if ok != nil || num < 0 || num > 255 {
			return false
		}
	}
	return true
}

14、判断手机号是否合法

判断用户输入的是不是一个手机号,要求判断手机号长度11,开头数字为1,全部为数字组成

package main

import (
	"fmt"
	"regexp"
)

//判断用户输入的是不是一个手机号,要求判断手机号长度11,开头数字为1,全部为数字组成
func main() {
	fmt.Println(isPhone("13345679898"))
	fmt.Println(isPhone("133a5679898"))

}
func isPhone(str string) bool {
	if len(str) != 11 && str[1] != '1' {
		return false
	}
	//判断是否由数字组成
	for _, v := range str {
		if !(v >= '0' && v <= '9') {
			return false
		}
	}

	return true
}

使用正则匹配判断

func isPhone2(str string) bool {
	pattern := "^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|147)\\d{8}$"
	re := regexp.MustCompile(pattern)
	if !re.MatchString(str) {
		return false
	}
	return true
}

15、 用栈实现队列

https://leetcode.cn/problems/implement-queue-using-stacks/description/
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
实现 MyQueue 类:
void push(int x) 将元素 x 推到队列的末尾
int pop() 从队列的开头移除并返回元素
int peek() 返回队列开头的元素
boolean empty() 如果队列为空,返回 true ;否则,返回 false
思路:
将一个栈当作输入栈,用于压入 push\texttt{push}push 传入的数据;另一个栈当作输出栈,用于 pop\texttt{pop}pop 和 peek\texttt{peek}peek 操作。
每次 pop\texttt{pop}pop 或 peek\texttt{peek}peek 时,若输出栈为空则将输入栈的全部数据依次弹出并压入输出栈,这样输出栈从栈顶往栈底的顺序就是队列从队首往队尾的顺序。

type MyQueue struct {
    in []int
    out []int
}


func Constructor() MyQueue {
    return MyQueue{
        in:[]int{},
        out:[]int{},
    }
}


func (this *MyQueue) Push(x int)  {
    this.in = append(this.in,x)
}


func (this *MyQueue) Pop() int {
    if len(this.out) == 0{
        for len(this.in) != 0{
            this.out = append(this.out,this.in[len(this.in)-1])
            this.in = this.in[:len(this.in)-1]
        }
        
    } 
    x := this.out[len(this.out)-1]
    this.out = this.out[:len(this.out)-1]
    return x
}


func (this *MyQueue) Peek() int {
    x := this.Pop()
    this.out = append(this.out,x)
    return x

}


func (this *MyQueue) Empty() bool {
    return len(this.in)==0&&len(this.out)==0

}

15、用队列实现栈

https://leetcode.cn/problems/implement-stack-using-queues/
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。

实现 MyStack 类:
void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

思路:

type MyStack struct {
    queue1,queue2 []int

}


func Constructor() MyStack {
    return MyStack{
        queue1:[]int{},
        queue2:[]int{},
    }
}


func (this *MyStack) Push(x int)  {
    this.queue2 = append(this.queue2,x)
    for len(this.queue1)>0{
        this.queue2 = append(this.queue2,this.queue1[0])
        this.queue1 = this.queue1[1:]
    }
    this.queue1,this.queue2 = this.queue2,this.queue1
}


func (this *MyStack) Pop() int {
    x := this.queue1[0]
    this.queue1 = this.queue1[1:]
    return x
}


func (this *MyStack) Top() int {
    return this.queue1[0]
}


func (this *MyStack) Empty() bool {
    return len(this.queue1)==0
}


/**
 * Your MyStack object will be instantiated and called as such:
 * obj := Constructor();
 * obj.Push(x);
 * param_2 := obj.Pop();
 * param_3 := obj.Top();
 * param_4 := obj.Empty();
 */ 

16、topk问题

找出数组中第k大的数,数组中存在重复数字
采用冒泡排序法的思路

package main

import (
	"fmt"
)

func main() {
	arr := []int{1, 2, 3, 2, 5, 6, 5}
	k := 3
	fmt.Println(test(arr, k))
}
func test(arr []int, k int) int {
	n := len(arr)
	for i := 0; i < n; i++ {
		for j := i; j < n-i-1; j++ {
			if arr[j] > arr[j+1] {
				arr[j], arr[j+1] = arr[j+1], arr[j]
			}
		}
		fmt.Println("arr:", arr)
		if i-1 > 0 && arr[i] == arr[i-1] {
			k = k + 1
		}
		if i == k-1 {
			return arr[i]
		}

	}
	return -1
}

17、买卖股票的最佳时机

力扣:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
输入:[7,1,5,3,6,4]
输出:5

func maxProfit(prices []int) int {
    //解题方法 暴力求解,贪心算法,动态规划
    minP := prices[0]
    var profit int
    for _,price := range prices{
        if profit < price - minP{
            profit = price - minP
        }
        if minP > price{
            minP = price
        }

    }
    return profit
}

18、跳跃游戏

力扣:https://leetcode.cn/problems/jump-game/
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。

func canJump(nums []int) bool {
//解题思路 如果 最远可以到达的位置 大于等于数组中的最后一个位置,那就说明最后一个位置可达 所以实时维护最远可以到达的位置
    n := len(nums)
    most := 0
    for i:=0;i<n;i++{
        if most>=i{
            most = max(most,i+nums[i])
            if most>= n-1{
                return true
            }
        }
    }
    return false

}
func max(x,y int)int{
    if x>y{
        return x
    }
    return y
}

19、最长公共前缀

力扣:https://leetcode.cn/problems/longest-common-prefix/
最优解法应该是KMP算法,奈何太渣了,没学会,先用遍历解题~

func longestCommonPrefix(strs []string) string {
    ch:=make([]byte,0)
    for i:=0;i<len(strs[0]);i++{
        tmp:=strs[0][i]
        for _,str := range strs{
            if i>=len(str)||str[i] !=tmp{
                return string(ch)
            }
        }
        ch = append(ch,tmp)
    }
    return string(ch)


}
func min(x,y int)int{
    if x<y{
        return x
    }
    return y
}

20、二叉树的前序遍历,中序遍历,后序遍历 递归解法+迭代解法

(1)前序遍历
力扣:https://leetcode.cn/problems/binary-tree-preorder-traversal/description/

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
var ans []int
func preorderTraversal(root *TreeNode) []int {
    //迭代解法
    ans = make([]int,0)
    stack := make([]*TreeNode,0)
    stack = append(stack,root)
    for len(stack)>0{
        tmp := stack[len(stack)-1]
        stack = stack[:len(stack)-1]
        if tmp==nil{
            continue
        }
        ans = append(ans,tmp.Val)
        stack = append(stack,tmp.Right)
        stack = append(stack,tmp.Left)
    }
    return ans
}
//递归解法
func preorder(root *TreeNode){
    if root == nil{
        return 
    }
    ans = append(ans,root.Val)
    preorder(root.Left)
    preorder(root.Right)
}

(2)中序遍历
力扣:https://leetcode.cn/problems/binary-tree-inorder-traversal/description/

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func inorderTraversal(root *TreeNode) []int {
    ans := make([]int,0)
    //迭代解法
    stack := make([]*TreeNode,0)
    for root!=nil||len(stack)>0{
        for root != nil{
            stack = append(stack,root)
            root = root.Left
        }
        root = stack[len(stack)-1]
        stack = stack[:len(stack)-1]
        ans = append(ans,root.Val)
        root = root.Right
    }
    
    return ans
}

// 递归解法
func order(root *TreeNode, ans []int)[]int{
    if root == nil{
        return ans
    }
    ans = order(root.Left,ans)
    ans = append(ans,root.Val)
    ans = order(root.Right,ans)
    return ans
}

(3)后序遍历
力扣:https://leetcode.cn/problems/binary-tree-postorder-traversal/submissions/
迭代法思路
在这里插入图片描述

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
var ans []int
func postorderTraversal(root *TreeNode) []int {
    
    ans = make([]int,0)
    //迭代法
    stack:=make([]*TreeNode,0)
    stack = append(stack,root)
    for len(stack)>0{
        tmp := stack[len(stack)-1]
        stack = stack[:len(stack)-1]
        if tmp == nil{
            continue
        }
        ans = append(ans,tmp.Val)
        stack = append(stack,tmp.Left)
        stack = append(stack,tmp.Right)  
        fmt.Println(ans)
    }
    ans = reverse(ans)
    //递归法
    // postorder(root)
    return ans
}

func reverse(ans []int)[]int{
    i,j:=0,len(ans)-1
    for i<=j{
        ans[i],ans[j] = ans[j],ans[i]
        i++
        j--
    }
    return ans
}

func postorder(root *TreeNode){
    if root == nil{
        return
    }
    postorder(root.Left)
    postorder(root.Right)
    ans = append(ans,root.Val)

}

迭代解法二: 由于后序遍历的顺序 :左子树 =》 右子树 =》根节点, 所以元素添加时机,在于 当前元素的左右子数都被遍历完后,才能够将该元素添加到结果数组中。

func postorderTraversal(root *TreeNode) []int {
    stack := make([]*TreeNode,0)
    result := make([]int,0)
    var prev *TreeNode
    for root!=nil || len(stack)>0{
        for root !=nil{
            stack = append(stack,root)
            root = root.Left
        }
        root = stack[len(stack)-1]
        stack = stack[:len(stack)-1]
        // 当前根节点的右子树不存在;当前根节点的右子树被完全遍历;
        if root.Right ==nil || root.Right == prev{
            // 后序遍历插入时机,在于根元素的左右子树都被遍历完;
            result = append(result,root.Val)
            // 变量prev,用于后续遍历节点去判断其右子树是否已经被遍历完;
            prev = root
            root = nil
        }else{
            stack = append(stack,root)
            root= root.Right
        }

    }
    return result
}

21、搜索二维矩阵

LeetCode:https://leetcode.cn/problems/search-a-2d-matrix-ii/description/
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
在这里插入图片描述
解法一、最容易想到思路,对每行进行二分查找

//对每一行进行二分查找
func searchMatrix(matrix [][]int, target int) bool {
    n := len(matrix)
    m:=len(matrix[0])
    for i:=0;i<n;i++{
        //目标值小于这一行的最小值,不需要再向下搜索
        if target<matrix[i][0]{
            break
        }
        //目标值大于改行的最大值,说明本行不存在目标值,继续向下搜索
        if target>matrix[i][m-1]{
            continue
        }
        flag := binarySearch(matrix[i],target)
        if flag == true{
            return true
        }
    }
    return false

}
func binarySearch(nums []int,target int) bool{
    left := 0
    right := len(nums)-1
    for left<=right{
        mid := (left+right)/2
        if target == nums[mid]{
            return true
        }else if target < nums[mid]{
            right = mid-1
        }else {
            left = mid+1
        }
    }
    return false

}

解法二、Z字形查找,从矩阵的右上角开始向下搜索

//z字形
func searchMatrix(matrix [][]int, target int) bool {
    if len(matrix)==0{
        return false
    }
    x := 0
    y:=len(matrix[0])-1
    for x<len(matrix)&&y>=0{
        if matrix[x][y]==target{
            return true
        }
        if matrix[x][y]<target{
            x++
        }else if matrix[x][y]>target{
            y--
        }
    }
    return false
}

22、用两个协程打印交替打印字母和数字,例如A1B2C3D4E5…

注意:WaitGroup对象不是一个引用类型,通过函数传值的时候需要使用地址,因为Go语言只有值传递,传递WaitGroup是值的话,就会导致会发生panic!!!

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

//goroutine 交替打印数字和字母
//打印数字
func NumberPrint(numCh chan int, letterCh chan int) {
	i := 1
	for {
		<-numCh
		fmt.Printf("%d", i)
		i = i + 1
		letterCh <- 1
	}
}
func LetterPrint(numCh chan int, letterCh chan int) {
	str := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	i := 0
	for {
		<-letterCh
		if i >= len(str) {
			wg.Done()
			return
		}
		fmt.Print(string(str[i]))
		i = i + 1
		numCh <- 1
	}
}
func main() {

	numCh, letterCh := make(chan int), make(chan int)
	wg.Add(1)
	go NumberPrint(numCh, letterCh)
	go LetterPrint(numCh, letterCh)
	letterCh <- 1 //重点!!!!,缺这句会导致死锁
	wg.Wait()

}

一种更加优雅的写法

package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	wg.Add(1)
	chNumber := make(chan struct{})
	chLetter := make(chan struct{})
	i := 0
	index := 0
	a := "abcdefghijklmnopqrskuvwxyz"
	go func() {
		for {
			select {
			case <-chNumber:
				fmt.Print(i)
				i++
				chLetter <- struct{}{}
				break
			default:
				break
			}
		}
	}()

	go func(wg *sync.WaitGroup) {
		for {
			select {
			case <-chLetter:
				if index > len(a)-1 {
					wg.Done()
					return
				}
				fmt.Print(string(a[index]))
				index++
				chNumber <- struct{}{}
				break
			default:
				break
			}
		}
	}(&wg)
	chNumber <- struct{}{}
	wg.Wait()
}

23、多个协程交替打印数字

参考:http://beangogo.cn/2021/03/03/golang-%E4%BA%A4%E6%9B%BF%E6%89%93%E5%8D%B0/

package main

import "fmt"

// 10个协程交替打印数字1~100
func main() {
	num := 100
	numG := 10
	chanArr := make([]chan int, numG)
	for i := 0; i < numG; i++ {
		chanArr[i] = make(chan int)
	}
	exit := make(chan bool)
	for i := 0; i < numG; i++ {
		go PrintNum(chanArr[i], chanArr[(i+1)%numG], num, i, exit)
	}

	chanArr[0] <- 0
	<-exit
}
func PrintNum(nowChan, nextChan chan int, num, i int, exit chan bool) {
	for {
		currNum := <-nowChan
		if currNum >= num {
			exit <- true
			break
		}
		currNum++
		fmt.Println("协程:", i, "currNum:", currNum)
		nextChan <- currNum
	}
}

24、实现一个单例模式

详见刘丹冰大佬博客:https://www.yuque.com/aceld/lfhu8y/gsrg40
(1)懒汉式
注意:a.外界不能通过这个类直接创建一个对象 ,那么这个类就应该变得非公有访问,类名称首字母要小写
b.如果全部为私有化,那么外部模块将永远无法访问到这个类和对象, 所以需要对外提供一个方法来获取这个唯一实例对象,这个方法一定是一个全局普通函数,这个方法必须首字母大写

//懒汉式
type singleton struct{}

var instance *singleton= new(singleton)

func GetSingleton() *singleton{
	return instance
}

(2)饿汉式
A.线程不安全的饿汉式

//饿汉式
type singleton struct{}

var instance *singleton

func GetSingleton() *singleton{
	if instance == nil{
		instance = new(singleton)
	}
	return instance
}

B 线程安全的饿汉式
线程安全但是效率低

//饿汉式
type singleton struct{}
var lock sync.Mutex
var instance *singleton

func GetSingleton() *singleton{
	lock.Lock()
	defer lock.Unlock()
	if instance == nil{
		instance = new(singleton)
	}
	return instance
}

利用atomic包来做互斥

//饿汉式
type singleton struct{}

var lock sync.Mutex
var instance *singleton
var a uint32

func GetSingleton() *singleton {
	//如果标记为被设置,直接返回,不加锁
	if atomic.LoadUint32(&a) == 1 {
		return instance
	}
	//如果没有,则加锁申请
	lock.Lock()
	defer lock.Unlock()
	if a == 0 {
		instance = new(singleton)
		//设置标记位
		atomic.StoreUint32(&a, 1)
	}
	return instance
}

上述操作Once模块已经帮助开发者实现了

//饿汉式
type singleton struct{}

var instance *singleton
var once sync.Once

func GetSingleton() *singleton {
	once.Do(func() {
		instance = new(singleton)
	})
	return instance
}

25、使用channel实现广播通知(一对多)

package main

import (
	"fmt"
	"time"
)

type Client struct {
	name   string
	source chan interface{}
	quit   chan bool
}

func main() {
	n := 3 //client的数量
	clientCh := make([]*Client, n)
	for i := 0; i < n; i++ {
		clientCh[i] = &Client{
			name:   fmt.Sprintf("G-%d", i),
			source: make(chan interface{}),
			quit:   make(chan bool),
		}
	}
	for _, v := range clientCh {
		go clientListen(v)
	}

	go hostSend(clientCh)
	time.Sleep(time.Second * 5)
}
func clientListen(host *Client) {
	//客户端接收通知
	for {
		select {
		case msg := <-host.source:
			fmt.Printf("client:%s 收听到消息:%s\n", host.name, msg)
		case <-host.quit:
			fmt.Printf("client:%s退出\n", host.name)
			return
		}
	}
}

func hostSend(clientCh []*Client) {
	i := 1
	for {
		msg := fmt.Sprintf("msg%d", i)
		i++
		fmt.Println("host发送消息:", msg)
		for _, v := range clientCh {
			v.source <- msg
		}
		time.Sleep(time.Second)
	}
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值