在我们的程序中,排序是非常常见的一种需求,提供一些数据元素,把这些数据元素按照一定的规则进 行排序。比如查询一些订单,按照订单的日期进行排序;再比如查询一些商品,按照商品的价格进行排 序等等。所以,接下来我们要学习一些常见的排序算法。
冒泡排序
冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。
实现原理
- 比较相邻的元素。如果前一个元素比后一个元素大,就交换这两个元素的位置。
- 对每一对相邻元素做同样的工作,从开始第一对元素到结尾的最后一对元素。最终最后位置的元素 就是最大值。
代码实现
// 冒泡排序
// 1. 比对相邻的两个元素,如果前面一个元素比后面一个元素大
// 2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对元素,最终位置的元素就是最大值
function Bubble(arr){
for(let i=arr.length;i>0;i--){
for(let j=0;j<i;j++){
if(greater(arr[j],arr[j+1])){
[arr[j],arr[j+1]]=[arr[j+1],arr[j]]
}
}
}
function greater(v,w){
return v>w
}
return arr
}
console.log(Bubble([9,8,7,6]))
选择排序
选择排序是一种更加简单直观的排序方法。
实现原理
- 每一次遍历的过程中,都假定第一个索引处的元素是最小值,和其他索引处的值依次进行比较,如 果当前索引处的值大于其他某个索引处的值,则假定其他某个索引出的值为最小值,最后可以找到 最小值所在的索引
- 交换第一个索引处和最小值所在的索引处的值
代码实现
function Selection(arr){
for(let i=0;i<arr.length;i++){
for(let j=0;j<arr.length;j++){
if(arr[j]>arr[i]){
[arr[j],arr[i]]=[arr[i],arr[j]]
}
}
}
return arr
}
console.log(Selection([9,8,7,6,5,4]))
插入排序
插入排序(Insertion sort)是一种简单直观且稳定的排序算法。
插入排序的工作方式非常像人们排序一手扑克牌一样。开始时,我们的左手为空并且桌子上的牌面朝 下。然后,我们每次从桌子上拿走一张牌并将它插入左手中正确的位置。为了找到一张牌的正确位置, 我们从右到左将它与已在手中的每张牌进行比较
实现原理
- 把所有的元素分为两组,已经排序的和未排序的;
- 找到未排序的组中的第一个元素,向已经排序的组中进行插入;
- 倒叙遍历已经排序的元素,依次和待插入的元素进行比较,直到找到一个元素小于等于待插入元素, 那么就把待插入元素放到这个位置,其他的元素向后移动一位
代码实现
function Insertion(arr){
for(let i=1;i<arr.length;i++){
for(let j=i;j>0;j--){
if(arr[j]<arr[j-1]){
[arr[j],arr[j-1]]=[arr[j-1],arr[j]]
}
}
}
return arr
}
console.log(Insertion([9,8,7,6,5,4]))
希尔排序
希尔排序是插入排序的一种,又称“缩小增量排序”,是插入排序算法的一种更高效的改进版本。
实现原理
- 选定一个增长量h,按照增长量h作为数据分组的依据,对数据进行分组;
- 对分好组的每一组数据完成插入排序;
- 减小增长量,最小减为1,重复第二步操作。
代码实现
function shellSort(arr) {
let len = arr.length;
// gap 即为增量
for (let gap = Math.floor(len / 2); gap > 0; gap = Math.floor(gap / 2)) {
for (let i = gap; i < len; i++) {
let current = arr[i];
while(i-gap>=0&¤t < arr[i - gap]) {
arr[i] = arr[i - gap];
i = i - gap;
}
arr[i] = current;
}
}
return arr
}
console.log(shellSort([9,8,7,6,1]))
归并排序
归并排序是建立在递归操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将 已有序的子 序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个 有序表合并成一个有序 表,称为二路归并。
排序原理
- 尽可能的一组数据拆分成两个元素相等的子组,并对每一个子组继续拆分,直到拆分后的每个子组 的元素个数是1为止。
- 将相邻的两个子组进行合并成一个有序的大组
- 不断的重复步骤2,直到最终只有一个组为止
代码实现
function Merge(arr){
let assets=new Array(arr.length)
let lo=0
let hi=arr.length-1
function sort(a,lo,hi){
if(hi<=lo){
return
}
let mid=Math.floor((hi+lo)/2)
sort(a,lo,mid)
sort(a,mid+1,hi)
merge(a,lo,mid,hi)
}
function merge(a,lo,mid,hi){
let i=lo
let p1=lo
let p2=mid+1
while (p1<=mid&&p2<=hi){
if(a[p1]<a[p2]){
assets[i++]=a[p1++]
}else {
assets[i++]=a[p2++]
}
}
while(p1<=mid){
assets[i++]=a[p1++]
}
while (p2<=hi){
assets[i++]=a[p2++]
}
for(let j=lo;j<=hi;j++){
a[j]=assets[j]
}
}
sort(arr,lo,hi)
return arr
}
console.log(Merge([9,8,7,6,5]))
快速排序
快速排序是对冒泡排序的一种改进。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部 分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进 行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
排序原理
- 首先设定一个分界值,通过该分界值将数组分成左右两部分;
- 将大于或等于分界值的数据放到到数组右边,小于分界值的数据放到数组的左边。此时左边部分中 各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值;
- 然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数 据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处 理。
- 重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部 分的顺序。当左侧和右侧两个部分的数据排完序后,整个数组的排序也就完成了。
切分原理
把一个数组切分成两个子数组的基本思想:
- 找一个基准值,用两个指针分别指向数组的头部和尾部;
- 先从尾部向头部开始搜索一个比基准值小的元素,搜索到即停止,并记录指针的位置;
- 再从头部向尾部开始搜索一个比基准值大的元素,搜索到即停止,并记录指针的位置;
- 交换当前左边指针位置和右边指针位置的元素
- 重复2,3,4步骤,直到左边指针的值大于右边指针的值停止。
代码实现
function Quick(arr){
let lo=0
let hi=arr.length-1
function sort(arr,lo,hi){
if(hi<lo){
return
}
let p=partition(arr,lo,hi)
sort(arr,lo,p-1)
sort(arr,p+1,hi)
}
function partition(arr,lo,hi){
let key=arr[lo]
let left=lo
let right=hi+1
while(true){
while (key<arr[--right]){
if(right==lo){
break
}
}
while (arr[++left]<key){
if(left==hi){
break
}
}
if(left>=right){
break
}else{
[arr[left],arr[right]]=[arr[right],arr[left]]
}
}
[arr[lo],arr[right]]=[arr[right],arr[lo]]
return right
}
sort(arr,lo,hi)
return arr
}
console.log(Quick([9,8,7,6,5]))
以上,感谢观看,如果有帮助的话可以点赞收藏关注下专栏哦 持续更新更多数据结构和算法代码片段。希望能够帮助到更多人