javaScript数据结构:排序算法的实现
1.冒泡排序
bubbleSort : O(n^2)
原理:
从左到右,相邻元素进行比较,如果前一个元素值大于后一个元素值(正序),则交换,这样一轮下
来,将最大的数在最右边冒泡出来。这样一轮一轮下来,最后实现从小到大排序。
Array.prototype.bubbleSort = function() {
//console.log(this);//这里的this就是自己的数组,比如这里输出的是[ 5, 4, 3, 2, 1 ]
for(let i = 0; i<this.length - 1; i+=1) {
for(let j=0; j<this.length-1-i;j+=1){
if(this[j] > this[j+1]) {
const temp = this[j];
this[j] = this[j+1];
this[j+1] = temp;
}
}
}
}
const arr = [5,4,8,2,10]
arr.bubbleSort();
console.log(arr);//[ 2, 4, 5, 8, 10 ]
//bubbleSort :O(n^2)
2.选择排序
selectionSort: O(n^2)
把第一个没有排序过的元素设置为最小值,
从前往后遍历每个没有排序过的元素,
如果元素 < 现在的最小值
将此元素设置成为新的最小值,
将最小值和第一个没有排序过的位置交换。
这样下来,每一趟排序都会找到一个最小值,将它放在队列的前面。
Array.prototype.selectionSort = function() {
//console.log(this);//这里的this就是自己的数组,比如这里输出的是[ 5, 4, 3, 2, 1 ]
for(let i = 0; i< this.length - 1;i += 1) {
//在每一轮循环里,将第一个未排序的数字当做最小值
let indexMin = i;
for(let j = i; j < this.length; j += 1) {
if(this[j] < this[indexMin]) {
indexMin = j;
}
}
//上面的for循环结束后,此时已经得到了真正最小值的下标为indexMin
//如果最小值不是每次排序的第一个数,那么就交换
if(indexMin !== i) {
const temp = this[i];
this[i] = this[indexMin];
this[indexMin] = temp;
}
}
}
const arr = [5,4,3,2,1]
arr.selectionSort();
console.log(arr);//[ 1, 2, 3, 4, 5 ]
// selectionSort: O(n^2)
3.插入排序
insertionSort: O(n^2)
将第一个元素标记为已排序,遍历每个没有排序的元素,“提取”该元素,将它与前面已排序的元素进行比较,找到它应该放置的位置,并将它插入进去。
Array.prototype.insertionSort = function() {
//console.log(this);//这里的this就是自己的数组,比如这里输出的是[ 5, 4, 3, 2, 1 ]
for(let i = 1; i < this.length; i +=1) {
const temp = this[i];
let j = i;
while(j>0) {
//因为要插入j,所以要让前面的值都向后挪一位
if(this[j - 1] > temp) {
this[j] = this[j-1];
}else {
break;
}
j -= 1;
}
//此时下标为j的地方就是应该插入的地方
this[j] = temp;
}
}
const arr = [5,7,3,9,1]
arr.insertionSort();
console.log(arr);//[ 1, 3, 5, 7, 9 ]
// insertionSort: O(n^2)
4.归并排序
它采用了分治策略,将数组分成2个较小的数组,然后每个数组再分成两个更小的数组,直至每个数组里只包含一个元素,然后将小数组不断的合并成较大的数组,直至只剩下一个数组,就是排序完成后的数组序列。
实现步骤:
- 将原始序列平分成两个小数组
- 判断小数组⻓度是否为1,不为1则继续分裂
- 原始数组被分称了⻓度为1的多个小数组,然后合并相邻小数组(有序合并)
- 不断合并小数组,直到合并称一个数组,则为排序后的数组序列
时间复杂度:O(n*log2n)
function mergeSort(arr) {
let array = mergeSortRec(arr);
return array;
}
function mergeSortRec(arr) {
let length = arr.length
if(length === 1) {
return arr
}
let mid = Math.floor(length/2),
left = arr.slice(0, mid),
right = arr.slice(mid, length)
return merge(mergeSortRec(left), mergeSortRec(right))
}
//顺序合并两个小数组left、right 到 result
function merge(left, right) {
let result = [],
ileft = 0,
iright = 0
while(ileft < left.length && iright < right.length) {
if(left[ileft] < right[iright]) {
result.push(left[ileft++])
}else {
result.push(right[iright++])
}
}
while(ileft < left.length) {
result.push(left[ileft++])
}
while(iright < right.length) {
result.push(right[iright++])
}
return result
}
let arr = [1,2,8,0,6]
console.log(mergeSort(arr));//[ 0, 1, 2, 6, 8 ]
代码二
Array.prototype.insertionSort = function() {
//console.log(this);//这里的this就是自己的数组,比如这里输出的是[ 5, 4, 3, 2, 1 ]
//定义一个递归函数
const rec = (arr) => {
if(arr.length === 1) {return arr;}
const mid = Math.floor(arr.length/2)
const left = arr.slice(0,mid)
const right = arr.slice(mid, arr.length)
const orderLeft = rec(left)
const orderRight = rec(right)
const res = []
while(orderLeft.length || orderRight.length) {
if(orderLeft.length && orderRight.length) {
res.push(orderLeft[0] < orderRight[0] ? orderLeft.shift() : orderRight.shift())
} else if(orderLeft.length) {
res.push(orderLeft.shift())
} else if(orderRight.length) {
res.push(orderRight.shift())
}
}
return res
}
//执行递归函数
const res = rec(this)
//改变原数组,this指向的是传入的arr,n是对应的值,i是下标
//res数组中存放的是已经排好序的数组,将res的每个值传给this数组
res.forEach((n, i) => {this[i] = n})
}
const arr = [5,7,3,9,1]
arr.insertionSort();
console.log(arr);//[ 1, 3, 5, 7, 9 ]
5.快速排序
原理
和归并排序一致,它也使用了分治策略的思想,它也将数组分成一个个小数组,但与归并不同的是,
它实际上并没有将它们分隔开。
快排使用了分治策略的思想,所谓分治,顾名思义,就是分而治之,将一个复杂的问题,分成两个或
多个相似的子问题,在把子问题分成更小的子问题,直到更小的子问题可以简单求解,求解子问题,
则原问题的解则为子问题解的合并。
快排的过程简单的说只有三步:
首先从序列中选取一个数作为基准数
将比这个数大的数全部放到它的右边,把小于或者等于它的数全部放到它的左边 (一次快排
partition )
然后分别对基准的左右两边重复以上的操作,直到数组完全排序
具体按以下步骤实现:
- 创建两个指针分别指向数组的最左端以及最右端
- 在数组中任意取出一个元素作为基准
- 左指针开始向右移动,遇到比基准大的停止
- 右指针开始向左移动,遇到比基准小的元素停止,交换左右指针所指向的元素
- 重复3,4,直到左指针超过右指针,此时,比基准小的值就都会放在基准的左边,比基准大
的值会出现在基准的右边 - 然后分别对基准的左右两边重复以上的操作,直到数组完全排序
时间复杂度:O(n*log2n)
function quick(arr){
if(arr.length <= 1){
return arr;
}
var left = [],
right = [],
mid = arr[0];
for(let i = 1;i < arr.length; i++){
if(arr[i] > mid){
right.push(arr[i]);
}else{
left.push(arr[i]);
}
}
// return quick(left).concat([mid],quick(right));
return [...quick(left),mid,...quick(right)]
}
const arr = [5,3,7,0,6]
console.log(quick(arr));//[ 0, 3, 5, 6, 7 ]