算法编程题-排序
算法编程题-排序
- 比较型排序算法
- 冒泡排序
- 选择排序
- 插入排序
- 希尔排序
- 堆排序
- 快速排序
- 归并排序
- 非比较型排序算法
- 计数排序
- 基数排序
本文将对七中经典比较型排序算法进行介绍,并且给出golang语言的实现,还包括基数排序、计数排序等非比较型的算法的介绍和实现。
比较型排序算法
所谓的比较型排序算法就是算法中会使用数据之间的比较,只能数组保存的是能相关比较大小的数据即可使用该类算法,相比于非比较型排序算法适用面更广。
在实现上进行了一定的封装,支持泛型和自定义排序规则,默认传入一个比较函数less,按照less指定的小于关系进行“从小到大”排序。抽象接口代码如下:
type Sorter[T any] struct {less func(item1, item2 T) bool
}func NewSorter[T any](less func(item1, item2 T) bool) *Sorter[T] {return &Sorter[T]{less: less}
}
冒泡排序
冒泡排序如其名,就是排序的过程和冒泡有点像。一个气泡从水底浮向水面,其体积是越来越大的。那么同理,在排序的过程中,也可以让一个个大的元素浮上去,从而完成整个的排序。代码实现如下:
// BubbleSort 冒泡排序,时间复杂度:O(n^2) 空间复杂度:O(1), 稳定
func (s *Sorter[T]) BubbleSort(arr []T) {n := len(arr)for i := 0; i < n; i++ {var bubbleFlag bool // 是否有冒泡for j := i; j+1 < n; j++ {if s.less(arr[j+1], arr[j]) {arr[j], arr[j+1] = arr[j+1], arr[j]bubbleFlag = true}}if !bubbleFlag { // 无冒泡,说明已经排序完成,提前退出break}}
}
冒泡排序时间复杂度是 O ( n 2 ) O(n^2) O(n2),空间复杂度为 O ( 1 ) O(1) O(1)。其是一种稳定的排序算法。在实现上可以记录一个标志,表明此一轮有没有发生冒泡,如果没有,说明数组已经完全有序,可以以前退出。
选择排序
选择排序同样也是人如其名,在每一轮中,选择剩余数组中的最小值,放入一个位置,经过n轮,完成排序。实现代码如下:
// SelectSort 选择排序,时间复杂度:(n ^ 2), 空间复杂度:O(1),不稳定
func (s *Sorter[T]) SelectSort(arr []T) {n := len(arr)for i := 0; i < n; i++ {minPtr := ifor j := i + 1; j < n; j++ {if s.less(arr[j], arr[minPtr]) {minPtr = j}}arr[minPtr], arr[i] = arr[i], arr[minPtr]}
}
选择排序时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度为 O ( 1 ) O(1) O(1),是一种不稳定的排序算法。
插入排序
插入排序的排序过程为:维护一个有序序列,然后将待排序的数字找到一个合适的位置,然后插入进去,这样还是一个有序序列,依次将所有元素完成这一个操作,即可完成数组的排序。实现代码如下:
// InsertSort 插入排序,时间复杂度:O(n^2) 空间复杂度:O(1), 稳定
func (s *Sorter[T]) InsertSort(arr []T) {n := len(arr)for i := 1; i < n; i++ {for j := i; j > 0; j-- {if s.less(arr[j], arr[j-1]) {arr[j], arr[j-1] = arr[j-1], arr[j]} else {break}}}
}
插入排序的时间复杂度平均为 O ( n 2 ) O(n^2) O(n2),但是对于一个基本有序的数组进行排序,时间复杂度可以达到 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1),是一种稳定的排序算法。
优化插入排序,可以从两个方面入手,减少找位置的时间或者移动元素的时间,每一轮这两个的时间都是 O ( n ) O(n) O(n)级别的,前者可以通过二分查找来优化,后者可以通过链表来优化,但是如何同时优化呢?笔者认为可以使用跳表,这样可以将插入排序的平均时间复杂度优化到 O ( n l o g n ) O(nlogn) O(nlogn),但是缺点在于实现复杂,且空间开销较大,相比于快速排序堆排序等,有点得不偿失。
希尔排序
希尔排序其实就是对于插入排序的优化,前文提到,插入排序在基本有序的数组排序效率比较高,那么可以考虑对于数组的子序列进行排序,具体的思路就是将子数组分为多个子序列,在每一轮中只对子序列内部进行插入排序。子序列分组的规则是每隔gap个的元素作为一个子序列,gap值在轮数下不断减小,直到为一后相当于是对全部数据进行插入排序,由于此时数组已经基本有序,所以最后一轮插入排序的效率很高。
// 希尔排序,时间复杂度: O(n^(3 / 2)) 空间复杂度:O(1), 不稳定
func (s *Sorter[T]) ShellSort(arr []T) {n := len(arr)gap := n >> 1for gap > 0 {for i := 0; i < gap; i++ {for j := i; j < n; j += gap {for k := i + gap; k < n; k += gap {if s.less(arr[k], arr[k-gap]) {arr[k], arr[k-gap] = arr[k-gap], arr[k]}}}}gap >>= 1}
}
希尔排序的时间复杂度为 O ( n 3 / 2 ) O(n ^ {3 / 2}) O(n3/2),但是最坏情况下也可能恶化成 O ( n 2 ) O(n^2) O(n2),空间复杂度为 O ( 1 ) O(1) O(1),是一种不稳定的排序算法,适用于中等规模的数据集排序。
堆排序
堆排序就是基于堆这种数据结构的性质,往往使用一个最小堆,将堆顶的元素取出,然后将最后一个元素放到堆顶后,从上到下进行调整,重复这一个过程,就能完成数组的排序。
type Heap[T any] struct {less func(item1, item2 T) boolarray []Tlength int // 标识实际长度
}// NewHeap 构建一个空的堆
func NewHeap[T any](less func(item1, item2 T) bool) *Heap[T] {return &Heap[T]{less: less, array: make([]T, 0), length: 0}
}// NewHeapWithArr 根据数组构建一个堆
func NewHeapWithArr[T any](less func(item1, item2 T) bool, arr []T) *Heap[T] {h := &Heap[T]{less: less, array: arr, length: len(arr)}for i := 1; i < len(arr); i++ {h.siftUp(i)}return h
}// Push 往堆h中插入一个元素
func (h *Heap[T]) Push(v T) {h.pushBack(v)h.siftUp(h.length - 1)
}// Top 返回堆顶的元素
func (h *Heap[T]) Top() T {return h.array[0]
}// Pop 移除堆顶的元素并返回
func (h *Heap[T]) Pop() T {ret := h.array[0]h.array[0] = h.array[h.length-1]h.length--h.siftDown(0)return ret
}func (h *Heap[T]) ToArr() []T {return h.array
}func (h *Heap[T]) IsEmpty() bool {return h.length == 0
}// pushBack 在尾部插入一个元素
func (h *Heap[T]) pushBack(v T) {if len(h.array) == h.length {h.array = append(h.array, v)} else {h.array[h.length] = v}h.length++
}// siftUp 从pos位置处开始往上调整
func (h *Heap[T]) siftUp(pos int) {if pos < 0 || pos >= h.length {return}i := posj := (pos - 1) / 2for j >= 0 && h.less(h.array[i], h.array[j]) {h.array[i], h.array[j] = h.array[j], h.array[i]i = jj = (i - 1) / 2}
}// siftDown 从pos位置处开始往下调整
func (h *Heap[T]) siftDown(pos int) {if pos < 0 || pos >= len(h.array) {return}i := 0j1 := 2*i + 1j2 := 2*i + 2for j1 < h.length {if h.less(h.array[j1], h.array[i]) && ((j2 >= h.length) || h.less(h.array[j1], h.array[j2])) {h.array[i], h.array[j1] = h.array[j1], h.array[i]i = j1} else if h.less(h.array[j2], h.array[i]) {h.array[i], h.array[j2] = h.array[j2], h.array[i]i = j2} else {break}j1 = 2*i + 1j2 = 2*i + 2}
}// HeapSort 堆排序,时间复杂度:O(nlogn), 空间复杂度:O(1), 不稳定
func (s *Sorter[T]) HeapSort(arr []T) {h := NewHeapWithArr(s.less, arr)k := len(arr) - 1for !h.IsEmpty() {arr[k] = h.Pop()k--}for i := 0; i < len(arr) / 2; i++ {arr[i], arr[len(arr) - 1 - i] = arr[len(arr) - 1 - i], arr[i]}
}
堆排序的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),空间复杂度为 O ( n ) O(n) O(n),是一种不稳定的排序算法,适用于大数据量下且内存资源相对宝贵的条件下。
快速排序
快速排序是一种基于分制的思路,对于一个数组,从中选择一个基准点,以这个基准点,小于的数交换到左边取,大于的数交换到右边去,然后递归地对左边和右边的子数组进行同样的处理,从而完成整个排序过程。
// 快速排序实现,时间复杂度:O(nlogn), 空间复杂度:O(1), 不稳定
func (s *Sorter[T]) QuickSort(arr []T) {s.quickSortV1(arr, 0, len(arr)-1)
}// 递归形式的排序
func (s *Sorter[T]) quickSortV1(arr []T, left, right int) {if left >= right { // 递归终点return}base := arr[left]basePtr := lefti := left + 1for i <= right {if s.less(arr[i], base) {basePtr++if basePtr != i {arr[basePtr], arr[i] = arr[i], arr[basePtr]}}i++}arr[left] = arr[basePtr]arr[basePtr] = bases.quickSortV1(arr, left, basePtr-1)s.quickSortV1(arr, basePtr+1, right)
}
以上实现版本的快速排序时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn),空间复杂度为 O ( 1 ) O(1) O(1),是一种不稳定的排序算法,但是最坏情况下时间复杂度也会恶化到 O ( n 2 ) O(n^2) O(n2)。比如上面的实现在数组基本有序的情况下,会化身为时间刺客,如下图。

一种常见的优化手段是从数组中取三个数字,然后以这三个数字的中位数作为基准点,如下为相关代码实现:
// quickSortV3优化实现,基准取头尾中三数中的平均数
func (s *Sorter[T]) quickSortV3(arr []T, left, right int) {if left >= right { // 递归终点return}s.adjustBase(arr, left, right)base := arr[left]basePtr := lefti := left + 1for i <= right {if s.less(arr[i], base) {basePtr++if basePtr != i {arr[basePtr], arr[i] = arr[i], arr[basePtr]}}i++}arr[left] = arr[basePtr]arr[basePtr] = bases.quickSortV3(arr, left, basePtr-1)s.quickSortV3(arr, basePtr+1, right)
}// adjustBase 调整基准值,将头尾中三数中的平均数放在头部作为基准
func (s *Sorter[T]) adjustBase(arr []T, left, right int) {basePtrs := []int{left, right, (left + right) / 2}s1 := NewSorter(func(i, j int) bool {return s.less(arr[i], arr[j])})s1.InsertSort(basePtrs)arr[left], arr[basePtrs[1]] = arr[basePtrs[1]], arr[left]
}
但是以上方法对于一个全是相同数字的数组还是会恶化成 O ( n 2 ) O(n^2) O(n2)。
在某些面试中,可能有些面试官要求实现一些递归算法的非递归版本,比如实现非递归版本的快速排序。递归在编程语言中的实质就是借助栈来实现的,所以这种题目本质上就是在模拟递归,代码实现如下:
// 非递归实现,借助栈来模拟递归
func (s *Sorter[T]) quickSortV2(arr []T, left, right int) {stack := NewStack[[2]int]()stack.Push([2]int{left, right})for !stack.IsEmpty() {state := stack.Pop()left, right := state[0], state[1]if left >= right {continue}base := arr[left]basePtr := lefti := left + 1for i <= right {if s.less(arr[i], base) {basePtr++if basePtr != i {arr[basePtr], arr[i] = arr[i], arr[basePtr]}}i++}arr[left] = arr[basePtr]arr[basePtr] = basestack.Push([2]int{left, basePtr - 1})stack.Push([2]int{basePtr + 1, right})}
}
实际上,也可以不使用栈,使用队列或者其他的数据结构都是可以的。
归并排序
归并排序的重点在于归并上,对于两个已经有序的数组而言,可以合并到一个有序的数组中,时间复杂度为 O ( n ) O(n) O(n),这样,最小的段即一个数的数组,必然是有序的,小段合成大段,最后合并完成整个数组的排序。
// MergeSort 归并排序,时间复杂度:O(nlogn), 空间复杂度:O(n), 稳定
func (s *Sorter[T]) MergeSort(arr []T) []T {return s.mergeSortV1(arr)
}// 递归形式的归并排序
func (s *Sorter[T]) mergeSortV1(arr []T) []T {if len(arr) <= 1 { // 递归终点return arr}// 递归过程leftArr := s.mergeSortV1(append([]T(nil), arr[:len(arr)/2]...))rightArr := s.mergeSortV1(append([]T(nil), arr[len(arr)/2:]...))// 合并i := 0j := 0k := 0for i < len(leftArr) || j < len(rightArr) {if i >= len(leftArr) || (j < len(rightArr) && s.less(rightArr[j], leftArr[i])) {arr[k] = rightArr[j]j++k++} else {arr[k] = leftArr[i]i++k++}}return arr
}
归并排序也可以实现非递归的形式,如下:
// 非递归形式的归并排序
func (s *Sorter[T]) mergeSortV2(arr []T) []T {segLen := 1 // 比较的段长for segLen < len(arr) {for p := 0; p < len(arr); p += 2 * segLen {leftArr := append([]T(nil), arr[p:min(p+segLen, len(arr))]...)rightArr := append([]T(nil), arr[min(p+segLen, len(arr)):min(p+2*segLen, len(arr))]...)// 合并i := 0j := 0k := 0for i < len(leftArr) || j < len(rightArr) {if i >= len(leftArr) || (j < len(rightArr) && s.less(rightArr[j], leftArr[i])) {arr[p+k] = rightArr[j]j++k++} else {arr[p+k] = leftArr[i]i++k++}}}segLen *= 2}return arr
}
归并排序时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),空间复杂度为 O ( n ) O(n) O(n),是一种稳定的排序算法。归并排序适用于大数据量且要求稳定的场景,毕竟同时间复杂度的排序算法快速排序和堆排序都是不稳定的。
非比较型排序算法
可以注意到以上的排序算法都需要通过比较元素的大小来交换位置或者其他的操作,这类算法统称为比较型排序算法。另外一类非比较型排序算法,不需要比较大小,但是其适用面比较小。
计数排序
所谓的计数排序就是维护一个数组,用来表示每一个元素出现的次数,然后遍历数组,将各个数字取出即可。
type IntSorter struct {
}func NewIntSorter() *IntSorter {return &IntSorter{}
}// 计数排序,时间复杂度:O(n+k) k为数据范围 空间复杂度:O(k) 稳定
// 适用于数据范围不大的情况下
func (s *IntSorter) CountSort(arr []int64) {minv := arr[0]maxv := arr[0]for _, item := range arr {minv = min(minv, item)maxv = max(maxv, item)}counts := make([]int64, maxv-minv+1)for _, item := range arr {counts[item-minv]++}k := 0for i := int64(0); i < maxv-minv+1; i++ {for j := int64(0); j < counts[i]; j++ {arr[k] = i + minvk++}}
}
计数排序的时间复杂度为 O ( n ) O(n) O(n)级别,空间复杂度为 O ( m ) O(m) O(m),其中m为数组中最大数减去最小数,可以看到,计数排序只适用于整数,且最好数据比较集中。
基数排序
以整数的基数排序来说明这一个过程。由于整数的每一位只有十种可能,从0到9,那么可以建立十个桶,遍历数组,按照最低位对应的数字放到对应的桶中,然后再将所有数组按照0号桶收集起来,然后从数字的低第二位做重复的操作,直到最大数字的最高位。代码实现如下:
func (s *IntSorter) RadixSort(arr []int) {var newBuckets func() []*List[int] = func() []*List[int] {buckets := make([]*List[int], 10)for i := 0; i < 10; i++ {buckets[i] = NewList[int]()}return buckets}buckets := newBuckets()oldBuckets := newBuckets()for _, num := range arr {oldBuckets[num%10].AppendNodeTail(NewListNode[int](num/10, num))}flag := true // 停止标志for flag {flag = falsefor i := 0; i < 10; i++ {for node := oldBuckets[i].Begin(); node != oldBuckets[i].End(); node = node.Next {if node.Key != 0 {flag = true}buckets[node.Key%10].AppendNodeTail(NewListNode[int](node.Key/10, node.Val))}}oldBuckets = bucketsbuckets = newBuckets()}// 此时所有数据都在零号桶里k := 0for node := oldBuckets[0].Begin(); node != oldBuckets[0].End(); node = node.Next {arr[k] = node.Valk++}
}
基数排序的时间复杂度也大致在 O ( n m ) O(nm) O(nm)左右,对于整数可以认为是 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n),适用于整数或者字符串等。
相关文章:
算法编程题-排序
算法编程题-排序 比较型排序算法冒泡排序选择排序插入排序希尔排序堆排序快速排序归并排序 非比较型排序算法计数排序基数排序 本文将对七中经典比较型排序算法进行介绍,并且给出golang语言的实现,还包括基数排序、计数排序等非比较型的算法的介绍和实现…...
【AIGC】如何准确引导ChatGPT,实现精细化GPTs指令生成
博客主页: [小ᶻZ࿆] 本文专栏: AIGC | 提示词Prompt应用实例 文章目录 💯前言💯准确引导ChatGPT创建爆款小红书文案GPTs指令案例💯 高效开发GPTs应用的核心原则明确应用场景和目标受众构建多样化风格模板提问与引导技巧持续优…...
【Axure高保真原型】或和且条件
今天和大家分享或和且条件案例的原型模板,效果包括: 可以选择指标、等式和填写对应值构成条件等式; 点击添加条件按钮,可以增加一行新的条件; 点击所在行的号按钮,可以在该行下方添加一行新的条件&#x…...
KubeVirt下gpu operator实践(GPU直通)
KubeVirt下gpu operator实践(GPU直通) 参考《在 KubeVirt 中使用 GPU Operator》,记录gpu operator在KubeVirt下实践的过程,包括虚拟机配置GPU直通,容器挂载GPU设备等。 KubeVirt 提供了一种将主机设备分配给虚拟机的机制。该机制具有通用性…...
Vue通过file控件上传文件到Node服务器
功能: 1.多文件同时上传、2.拖动上传、3.实时上传进度条、4.中断上传和删除文件、5.原生file控件的美化 搁置的功能: 上传文件夹、大文件切片上传、以及其他限制条件未处理 Node服务器的前置准备: 新建文件夹: file_upload_serve初始化npm: npm …...
如何在 SQL Server 中新增账户并指定数据库权限
在日常的数据库维护与开发中,管理用户的权限是必不可少的一环。本文将指导你如何在 SQL Server 中为新用户创建账户,并为其指定相应的数据库权限,使其只能查看有权访问的数据。我们将以步骤和代码示例的方式展示整个流程。用户权限分配包括:读取权限、写入权限、执行权限。…...
c#编码技巧(十九):各种集合特点汇总
.NET 常用集合对比: .NET 常见的线程安全集合 .NET 只读集合...
汽车软件DevOps解决方案
汽车软件DevOps解决方案是专为现代汽车行业设计的一套集成化需求、开发、测试、部署、OTA与监控,旨在加速软件开发流程,提高软件质量和安全性,同时确保整个生命周期的高效性和灵活性。以下是经纬恒润汽车软件DevOps解决方案的关键组成部分和优…...
同步的意义以及机制
一、同步的意义 同步(Synchronization)的意义在于确保在多线程环境中,多个线程对共享资源的访问是安全的,避免竞争条件(race conditions)和数据不一致的情况。 具体来说,同步的核心目标是&…...
leetcode 面试150之 156.LUR 缓存
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -…...
启发式搜索算法复现
🏡作者主页:点击! 🤖编程探索专栏:点击! ⏰️创作时间:2024年11月21日19点05分 神秘男子影, 秘而不宣藏。 泣意深不见, 男子自持重, 子夜独自沉。 论文链接 点击开启你的论文编程之旅…...
【IDE】使用指南
定期更新实用技能,建议关注收藏点赞。 友情链接: 点击跳转常见代码编辑器的报错解决方案 目录 常用快捷键pycharm右下角边栏脚本头安装IDE的插件git配置TODO 代码编辑器里有许多小技巧,便于办公。本篇主要以pycharm,vscode等主流常用IDE为…...
设计编程网站集:简述可扩展性系统设计(笔记)
视频连接:简述可扩展性系统设计 三个关键原则 无状态 松散耦合 异步处理 扩展 负载均衡 缓存 分片...
「Mac玩转仓颉内测版25」基础篇5 - 布尔类型详解
本篇将介绍 Cangjie 中的布尔类型,包括布尔值的定义、运算操作符、逻辑运算、布尔类型的常见应用场景及其在条件判断中的应用,帮助开发者理解和使用布尔类型。 关键词 布尔类型定义布尔运算逻辑运算符条件判断常见应用场景 一、布尔类型概述 布尔类型&…...
Fashion-VDM:引领视频虚拟试穿技术的新篇章
引言 随着虚拟现实和增强现实技术的飞速发展,视频虚拟试穿(VVT)已成为时尚产业的一大创新领域。然而,现有的VVT方法在服装细节和时间一致性方面仍存在诸多不足。为了解决这些问题,Johanna Karras等人提出了Fashion-VDM,一种基于视频扩散模型(VDM)的新型视频虚拟试穿技…...
Scala中的集合复习(1)
Map、Set、Array、List 一、集合的三大类 1.序列Seq表示有先后顺序的集合。(Array、List) 2.集Set:表示无序且不重复的集合。 3.映射Map:表示键值对。 Stack:栈,特点是:后进先出。 packag…...
Java依赖包漏洞检测命令
1、漏洞扫描工具 maven插件方式:Dependency-Check 2、命令 检查单个 Maven 工程的安全漏洞 mvn dependency-check:check 这个命令会在 target 目录下生成一个 dependency-check-report.html 文件,其中包含了依赖项的安全漏洞分析报告。 检查多个 M…...
【Java】强制类型转换
int a23; short b(short) a; 小的接受大的接受不了,强制类型转换. 带有Buffer的,带有流的,都是数组。 网络流,文件流都是数组. 这种就是流。 操作系统底层就是C. 没有直系关系的,不让转换 语法不报错,运行…...
RabbitMQ消息可靠性保证机制4--消费端限流
7.7 消费端限流 在类似如秒杀活动中,一开始会有大量并发写请求到达服务端,城机对消息进行削峰处理,如何做? 当消息投递的速度远快于消费的速度时,随着时间积累就会出现“消息积压”。消息中间件本身是具备一定的缓冲…...
查找萤石云IOS Sdk中的编解码接口
2021/1/20 以前的时候,碰到的问题,想把萤石云视频介入到TRTC,但是... 萤石云的IOS接口中没有相应的解码播放库,也就是找不到PlayerSDK对应部分,怎么做呢? 一个是坐等萤石云开放这部分接口,可能…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
