2025/3/19
This commit is contained in:
373
技术/Go/算法相关.md
Normal file
373
技术/Go/算法相关.md
Normal file
@@ -0,0 +1,373 @@
|
||||
## 性能优化运算
|
||||
~~~ go
|
||||
2^x = 1 << x
|
||||
x/2 x >> 1
|
||||
~~~
|
||||
## 加油站
|
||||
在一条环路上有 `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
|
||||
}
|
||||
~~~
|
||||
|
||||
|
||||
|
||||
## 快速排序
|
||||
|
||||
```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 max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func addStrings(num1 string, num2 string) string {
|
||||
buf := make([]byte, max(len(num1), len(num2)) + 1)
|
||||
length := len(buf) - 1
|
||||
add := 0
|
||||
for i, j := len(num1)-1, len(num2)-1; j >= 0 || i >= 0; {
|
||||
num := add
|
||||
if i >= 0 {
|
||||
num += int(num1[i] - '0')
|
||||
i--
|
||||
}
|
||||
if j >= 0 {
|
||||
num += int(num2[j] - '0')
|
||||
j--
|
||||
}
|
||||
if num >= 10 {
|
||||
num %= 10
|
||||
add = 1
|
||||
} else {
|
||||
add = 0
|
||||
}
|
||||
buf[length] = byte(num+'0')
|
||||
length--
|
||||
}
|
||||
if add != 0 {
|
||||
buf[length] = '1'
|
||||
}
|
||||
if buf[0]==0{
|
||||
buf=buf[1:]
|
||||
}
|
||||
return string(buf)
|
||||
}
|
||||
```
|
||||
|
||||
## 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。
|
Reference in New Issue
Block a user