排序算法的比较
参考:
https://blog.csdn.net/lyhkmm/article/details/78920769
https://blog.csdn.net/u012681635/article/details/80453664
https://www.zhihu.com/people/dong-hua-xue-bian-cheng
排序算法:
1、简单排序:冒泡、选择、插入
1.1:冒泡排序:两两对比,前大于后,则交换位置,直到把最大的冒泡到顶部。交换O(N^2),比较O(N^2)
1.2:选择排序:取出最小值/最大值位置,与第一个/最后一个位置交换。交换O(N),比较O(N^2)
1.3:插入排序:核心思想是局部有序,将后一个要比较的取出,然后比较判断是否移动(不是交换)。由于局部有序,不需要移动则后面的不需要再比较,且取出的元素赋值到停止比较的位置。效率是简单排序里最高的。交换O(N),比较O(N^2),看起来和选择排序一样,但其实是比冒泡和选择少了一半的,且移动比交换的效率高
2、高级排序:希尔、快速、归并、技术、基数、堆、桶
2.1:希尔排序:插入排序的改进,效率更高。
主要思路:设置间隔,重新排序,让数字离自己正确的位置更近。
增量的问题:如何选择合适的间隔?
希尔的原稿建议初始间隔为 N(数字个数)/ 2,不需要在开始排序前为找合适的增量而进行计算;还有Hibbard、Sedgewick等。
希尔的效率和增量的选择关系很大,但是不好推断,因为一些增量无法证明。但是最坏情况下,使用原始增量也能达到O(N^2),所以通常会比他小
第一次gap:
第二次gap:
第三次gap:
2.2:快速排序:一般来说,是排序里效率最高的,可以看成高级的冒泡
一次循环(递归)中,找到元素的正确位置,该元素之后不需要任何移动
主要思路:分而治之(divide and conquer,D&C):将原问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些子问题,然后在合并这些子问题的解来建立原问题的解。
取一元素,将大于他的放置于右边,小于的放置于左边,递归该函数
主要操作:选取枢纽:pivot主元。一般取头、中、尾的中位数
效率:主要看枢纽的选择,最坏的情况就是枢纽是最右或者最左,其效率等同于冒泡。平均效率是O(N*logN)
图示的pivot取的是最后一个值,思路基本和下面附属代码类似。
function ArrayList() { //属性 this.array = [] //方法 //数据插入 ArrayList.prototype.insert = function (item) { this.array.push(item) } ArrayList.prototype.toString = function () { return this.array.join(' ') } //定义一个交换变量的函数 ArrayList.prototype.swap = function (m, n) { let temp = this.array[m] this.array[m] = this.array[n] this.array[n] = temp } //冒泡 ArrayList.prototype.bubbleSort = function () { for (let n = 0; n < this.array.length; n++) { for (let i = 0; i < this.array.length - n; i++) { if (this.array[i] > this.array[i + 1]) { this.swap(i, i + 1) } } } } //选择 ArrayList.prototype.selectionSort = function () { let length = this.array.length let index = null for (let n = 0; n < length; n++) { index = 0 for (let i = 0; i < length - n - 1; i++) { if (this.array[index] < this.array[i + 1]) { index = i + 1 } } //有比他大的值,再和末位交换,否则不操作 if (index != 0) { this.swap(index, length - 1 - n) } } } //插入 ArrayList.prototype.insertionSort = function () { if (this.array.length > 1) { for (let i = 1; i < this.array.length; i++) { let temp = this.array[i] //这里大于-1是为了当插入的元素是最后一个时,最后一个else的复制 for (let j = i - 1; j >= -1; j--) { if (temp < this.array[j]) { this.array[j + 1] = this.array[j] } else { this.array[j + 1] = temp break } } } } } //希尔 ArrayList.prototype.shellSort = function () { let gap = Math.floor(this.array.length / 2) while (gap >= 1) { //下面其实就是插入排序,前值的间隔从1变成了gap for (let i = gap; i < this.array.length; i++) { let temp = this.array[i] for (let j = i - gap; j >= -gap; j = j - gap) { if (temp < this.array[j]) { this.array[j + gap] = this.array[j] } else { this.array[j + gap] = temp break } } } gap = Math.floor(gap / 2) } } //快速 //枢纽选择:头中尾中位数,这个是要递归的,所以要传左右值进来 ArrayList.prototype.medium = function (left, right) { let center = Math.floor((left + right) / 2) //先进行简单排序交换 //顺序执行 if (this.array[right] < this.array[left]) { this.swap(right, left) } if (this.array[center] < this.array[left]) { this.swap(center, left) } if (this.array[center] > this.array[right]) { this.swap(center, right) } // 把中间值放在第二个位置,或者是倒数第二个位置 //目的是为了把未判别的值全部放置于右边或者左边,为了接下来的操作 this.swap(left + 1, center) return this.array[left + 1] } ArrayList.prototype.quickSort = function () { this.quick(0, this.array.length - 1) } ArrayList.prototype.quick = function (left, right) { //越界时,退出递归 if (left >= right) { return } let pivot = this.medium(left, right) //记录当前左边值和右边值的位置 let low = left + 1 let high = right //交换 while (low < high) { while (this.array[low] <= pivot) { low++ } while (this.array[high] > pivot) { high-- } //这里要先判断low的值是不是还小于high,不然就越界了 if (low < high) { this.swap(low, high) } } //循环结束就是数据都正确放到的枢纽的左右,枢纽换位 this.swap(left + 1, low - 1) //递归,分而治之再处理左右值 this.quick(left, low - 1) this.quick(low, right) } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具