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)
}
}