## 性能优化运算 ~~~ go 2^x = 1 << x x/2 x >> 1 ~~~ ## 递增的三元子序列 给你一个整数数组 `nums` ,判断这个数组中是否存在长度为 `3` 的递增子序列。 如果存在这样的三元组下标 `(i, j, k)` 且满足 `i < j < k` ,使得 `nums[i] < nums[j] < nums[k]` ,返回 `true` ;否则,返回 `false` 。 ~~~ go func increasingTriplet(nums []int) bool { n := len(nums) if n < 3 { return false } first, sencond := nums[0], math.MaxInt32 for i := 1;i < n;i++ { num := nums[i] if num > sencond { return true } else if num > first { sencond = num } else { first = num } } return false } ~~~ 赋初始值的时候,已经满足second > first了,现在找第三个数third (1) 如果third比second大,那就是找到了,直接返回true (2) 如果third比second小,但是比first大,那就把second指向third,然后继续遍历找third (3) 如果third比first还小,那就把first指向third,然后继续遍历找third(这样的话first会跑到second的后边,但是不要紧,因为在second的前边,老first还是满足的) ## 字符串的最大公因子 ~~~ go func gcdOfStrings(str1 string, str2 string) string { if str1+str2 != str2+str1 { return "" } return str1[:gcd(len(str1), len(str2))] } // 欧几里得法求最大公因数 func gcd(a, b int) int { if b == 0 { return a } return gcd(b, a%b) } ~~~ ## 快乐数 ~~~ go func isHappy(n int) bool { slow, fast := n, step(n) for fast != 1 && slow != fast { slow = step(slow) fast = step(step(fast)) } return fast == 1 } func step(n int) int { sum := 0 for n > 0 { //通过循环逐位计算 sum += (n%10) * (n%10) n = n/10 } return sum } ~~~ ## 查找二维数组 ~~~ go func searchMatrix(matrix [][]int, target int) bool { row := sort.Search(len(matrix), func(i int) bool { return matrix[i][0] > target }) - 1 if row < 0 { return false } col := sort.SearchInts(matrix[row], target) return col < len(matrix[row]) && matrix[row][col] == target } ~~~ ## LRU缓存 ~~~ go // 定义LRU缓存结构体,包含当前大小、最大容量、键值对映射表以及双向链表的头尾节点 type LRUCache struct { size int // 当前缓存中元素的数量 capacity int // 缓存的最大容量 cache map[int]*DLinkedNode // 键到节点的映射表,用于快速查找节点 head, tail *DLinkedNode // 双向链表的虚拟头节点和虚拟尾节点,便于操作边界条件 } // 定义双向链表节点结构体,包含键、值以及前后指针 type DLinkedNode struct { key, value int // 节点存储的数据键和值 prev, next *DLinkedNode // 指向前一个和后一个节点的指针 } // 初始化一个双向链表节点,仅设置键和值,前后指针默认为nil func initDLinkedNode(key, value int) *DLinkedNode { return &DLinkedNode{ key: key, value: value, } } // 构造函数,创建一个新的LRU缓存实例,并初始化其内部数据结构 func Constructor(capacity int) LRUCache { l := LRUCache{ cache: make(map[int]*DLinkedNode), // 初始化键值对映射表 head: initDLinkedNode(0, 0), // 创建虚拟头节点 tail: initDLinkedNode(0, 0), // 创建虚拟尾节点 capacity: capacity, // 设置缓存最大容量 } l.head.next = l.tail // 将虚拟头节点指向虚拟尾节点 l.tail.prev = l.head // 将虚拟尾节点指向虚拟头节点 return l // 返回构造好的LRU缓存实例 } // 根据给定键从LRU缓存中获取对应的值,如果不存在则返回-1 func (this *LRUCache) Get(key int) int { if _, ok := this.cache[key]; !ok { // 判断键是否存在于缓存中 return -1 // 如果不存在,直接返回-1 } node := this.cache[key] // 获取到对应节点 this.moveToHead(node) // 将该节点移动到链表头部(表示最近访问) return node.value // 返回节点的值 } // 向LRU缓存中添加或更新键值对,如果缓存已满,则移除最久未使用的节点 func (this *LRUCache) Put(key int, value int) { if _, ok := this.cache[key]; !ok { // 判断键是否已经存在于缓存中 node := initDLinkedNode(key, value) // 创建新节点 this.cache[key] = node // 将新节点加入到映射表中 this.addToHead(node) // 将新节点添加到链表头部(表示最近访问) this.size++ // 增加缓存大小计数 if this.size > this.capacity { // 如果缓存大小超过最大容量 removed := this.removeTail() // 移除链表尾部节点(最久未使用) delete(this.cache, removed.key) // 删除映射表中对应的键值对 this.size-- // 减少缓存大小计数 } } else { // 如果键已经存在 node := this.cache[key] // 获取到对应节点 node.value = value // 更新节点的值 this.moveToHead(node) // 将该节点移动到链表头部(表示最近访问) } } // 将指定节点添加到双向链表头部 func (this *LRUCache) addToHead(node *DLinkedNode) { node.prev = this.head // 设置节点的前驱为虚拟头节点 node.next = this.head.next // 设置节点的后继为原头部节点 this.head.next.prev = node // 将原头部节点的前驱指向当前节点 this.head.next = node // 将虚拟头节点的后继指向当前节点 } // 从双向链表中移除指定节点 func (this *LRUCache) removeNode(node *DLinkedNode) { node.prev.next = node.next // 将前驱节点的后继指向后继节点 node.next.prev = node.prev // 将后继节点的前驱指向前驱节点 } // 将指定节点移动到双向链表头部 func (this *LRUCache) moveToHead(node *DLinkedNode) { this.removeNode(node) // 首先移除该节点 this.addToHead(node) // 然后将其添加到链表头部 } // 移除双向链表尾部节点并返回该节点 func (this *LRUCache) removeTail() *DLinkedNode { node := this.tail.prev // 获取尾部节点(不包括虚拟尾节点) this.removeNode(node) // 移除该节点 return node // 返回被移除的节点 } ~~~ ## 加油站 在一条环路上有 `n` 个加油站,其中第 `i` 个加油站有汽油 `gas[i]` 升。 你有一辆油箱容量无限的的汽车,从第 `i` 个加油站开往第 `i+1` 个加油站需要消耗汽油 `cost[i]` 升。你从其中的一个加油站出发,开始时油箱为空。 给定两个整数数组 `gas` 和 `cost` ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 `-1` 。如果存在解,则 **保证** 它是 **唯一** 的。 ~~~ go func canCompleteCircuit(gas []int, cost []int) int { n := len(gas) totalGas, currentGas,start := 0, 0, 0 for i := 0; i < n; i++ { totalGas += gas[i] - cost[i] currentGas += gas[i] - cost[i] // 如果当前油量小于0,说明从start到i之间不能作为一个有效的起始点 if currentGas < 0 { start = i + 1 currentGas = 0 } } if totalGas >= 0 { return start } return -1 } ~~~ ## 逆波兰表达式 栈 ~~~ go func evalRPN(tokens []string) int { stk := []int{} for _, token := range tokens { if token == "+" { stk = append(stk[:len(stk)-2], stk[len(stk)-2]+stk[len(stk)-1]) } else if token == "-" { stk = append(stk[:len(stk)-2], stk[len(stk)-2]-stk[len(stk)-1]) } else if token == "*" { stk = append(stk[:len(stk)-2], stk[len(stk)-2]*stk[len(stk)-1]) } else if token == "/" { stk = append(stk[:len(stk)-2], stk[len(stk)-2]/stk[len(stk)-1]) } else { i, err := strconv.Atoi(token) if err != nil { return 0 } stk = append(stk, i) } } return stk[0] } ~~~ ## 二叉树的层序遍历 DFS算法 ~~~ go package main import . "nc_tools" // 引入工具包,假设其中定义了树节点结构 `TreeNode` // BFS迭代法,通法 func levelOrder(root *TreeNode) [][]int { // 如果根节点为空,直接返回空的二维数组 if root == nil { return [][]int{} } queue := []*TreeNode{} // 定义一个队列,用于存储当前层的节点 queue = append(queue, root) // 将根节点加入队列 levels := [][]int{} // 定义一个二维数组,用于存储每一层的节点值 // 当队列不为空时,继续遍历 for len(queue) > 0 { n := len(queue) // 获取当前层的节点数量 level := []int{} // 定义一个一维数组,用于存储当前层的节点值 // 遍历当前层的所有节点 for i := 0; i < n; i++ { root = queue[0] // 取出队列的第一个节点 queue = queue[1:] // 将该节点从队列中移除 level = append(level, root.Val) // 将当前节点的值加入当前层的结果中 // 如果左子节点存在,将其加入队列 if root.Left != nil { queue = append(queue, root.Left) } // 如果右子节点存在,将其加入队列 if root.Right != nil { queue = append(queue, root.Right) } } // 将当前层的结果加入最终结果中 levels = append(levels, level) } // 返回所有层的结果 return levels } ~~~ ## 存在重复元素II 哈希表 ~~~ go func containsNearbyDuplicate(nums []int, k int) bool { m := make(map[int]int) for i, value := range nums{ if _, ok := m[value]; ok { if (i - m[value]) <= k { return true } else { m[value] = i } } else { m[value] = i } } return false } ~~~ ## 移动0 快慢指针 ~~~ go func moveZeroes(nums []int) {     left, right := 0, 0     for right < len(nums) {         if nums[right] != 0 {             nums[left], nums[right] = nums[right], nums[left]             left++         }         right++     } } ~~~ ## Pow(x, n) ~~~ go // myPow 计算x的n次幂,通过快速幂算法实现时间复杂度O(log n) // 当n为负数时,根据数学性质x^(-n) = 1/(x^n)转换为正指数计算 func myPow(x float64, n int) float64 { if n >= 0 { return quick(x, n) } return 1.0 / quick(x, -n) // 负指数转为倒数计算 } // quick 递归实现快速幂算法,采用分治策略降低计算次数 // 核心思想:每次将指数折半,底数平方,利用公式x^n = x^(n/2) * x^(n/2)(n为偶数时) func quick(x float64, n int) float64 { if n == 0 { return 1 // 递归终止条件:任何数的0次方均为1 } y := quick(x, n/2) // 将问题规模减半,分治策略核心步骤 if n%2 == 0 { return y * y // 偶数次幂:直接返回子问题平方 } return y * y * x // 奇数次幂:额外乘以一次底数x } ~~~ ## 重复的子字符串 给定一个非空的字符串 `s` ,检查是否可以通过由它的一个子串重复多次构成。 ~~~ go func repeatedSubstringPattern(s string) bool {     n := len(s)     for i := 1; i*2 <= n; i++ {         if n%i == 0 {             match := true             for j := i; j < n; j++ {                 if s[j] != s[j-i] {                     match = false                     break                 }             }             if match {                 return true             }         }     }     return false } ~~~ ## 判断IP地址 ~~~ go import "strings" /** * 验证IP地址 * @param IP string字符串 一个IP地址字符串 * @return string字符串 */ func solve( IP string ) string { if isIpv4(IP) { return "IPv4" } if isIpv6(IP) { return "IPv6" } return "Neither" } func isIpv4(ip string) bool { //首先判断字符串是不是大于7(1.1.1.1),首尾不能是 . if len(ip) < 7 || ip[0] == '.' || ip[len(ip) - 1] == '.' { return false } //分割之后长度不能少于4 strSlice := strings.Split(ip,".") if len(strSlice) != 4 { return false } for _, str := range strSlice { k := len(str) //开头不能是0 // str = []byte(str) if k == 0 || str[0] == '0' && k > 1 || k > 3 { return false } num := 0 for i:=0;i '9' { return false } num = num * 10 + int((c-'0')) //不能超过255 if num > 255 { return false } } } return true } func isIpv6(ip string) bool { //首先判断字符串是不是小于15(1.1.1.1),首尾不能是 . if len(ip) < 7 || ip[0] == '.' || ip[len(ip) - 1] == '.' { return false } strSlice := strings.Split(ip,":") if len(strSlice) != 8 { return false } for _, str := range strSlice { k := len(str) //开头不能是0 // str = []byte(str) if k == 0 || k > 4 { return false } for i:=0;i= "a") || (c <= "F" && c >= "A") || (c >= "0" && c <= "9")) { return false } } } return true } ~~~ ## 快速排序 ```go func QuickSort(arr []int) { if len(arr) <= 1 { return } // 选择最后一个元素作为基准 pivot := arr[len(arr)-1] i := 0 // 标记小于基准的边界 // 分区过程 for j := 0; j < len(arr)-1; j++ { if arr[j] <= pivot { arr[i], arr[j] = arr[j], arr[i] i++ } } // 将基准放到正确的位置 arr[i], arr[len(arr)-1] = arr[len(arr)-1], arr[i] // 递归排序左右子数组 QuickSort(arr[:i]) QuickSort(arr[i+1:]) } ``` 快速排序采用 **分治法(Divide and Conquer)** 策略: 1. **选基准**:从数组中选择一个元素作为基准(pivot)。 2. **分区**:将数组分为两部分: * 左半部分所有元素 ≤ 基准 * 右半部分所有元素 ≥ 基准 3. **递归**:对左右子数组重复上述步骤,直到子数组长度为 1 或 0(已有序)。 *** ## 反转链表 ```go /** * 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 } ``` *** ## 二分查找 ```go func search(nums []int, target int) int { left, right := 0, len(nums)-1 for left <= right { mid := left + (right-left)/2 if nums[mid] == target { return mid } else if nums[mid] > target{ right = mid - 1 } else { left = mid + 1 } } return -1 } ``` ## 字符串相加 ```go func solve(s string, t string) (ans string) { buf := make([]byte, 0, max(len(s), len(t))+1) add := 0 for i, j := len(s)-1, len(t)-1; i >= 0 || j >= 0; { if i >= 0 { add += int(s[i] - '0') i-- } if j >= 0 { add += int(t[j] - '0') j-- } buf = append(buf, byte(add%10)+'0') add /= 10 } if add > 0 { buf = append(buf, byte(add)+'0') } for _, v := range buf { ans = string(v) + ans } return ans } ``` ## Rand7 --> Rand10 拒绝采样 ```go func rand10() int { for { row := rand7() col := rand7() idx := (row-1)*7 + col if idx <= 40 { return 1 + (idx-1)%10 } } } ``` ## 最大子序和 ```go func maxSubArray(nums []int) int { max := nums[0] for i := 1; i < len(nums); i++ { if nums[i] + nums[i-1] > nums[i] { nums[i] += nums[i-1] } if nums[i] > max { max = nums[i] } } return max } ``` ## 阶乘后的零 ~~~ go func trailingZeroes(n int) (ans int) { for n > 0 { n /= 5 ans += n } return } ~~~ 阶乘末尾的零由因子2和5的乘积(即10)产生。由于2的个数远多于5的个数,因此只需统计因子5的个数即可。具体步骤如下: 1. **统计5的倍数** :每个能被5整除的数至少贡献一个5。 2. **统计25的倍数** :每个能被25整除的数额外贡献一个5(因为25 = 5×5)。 3. **统计更高次幂** :类似地,统计125、625等更高次幂的倍数,直到商为0。