冒泡排序
冒泡排序的原理如下,从第一个元素开始,把当前元素和下一个索引元素进行比较。如果当前元素大,那么就交换位置,重复操作直到比较到最后一个元素,那么此时最后一个元素就是该数组中最大的数。下一轮重复以上操作,但是此时最后一个元素已经是最大数了,所以不需要再比较最后一个元素,只需要比较到
length - 1
的位置。以此类推,每一次最外层循环都会将后面的数排好序,因此里面的循环可以减少i次循环.冒泡排序算法时间复杂度为O(n*n)
//算法实现
function bubble(array){
let len = array.length;
if(len <= 0){return false;}
for(let i = 0;i < len;i++){
//由于后面的数已经排好序了,因此第二次没必要再循环len次,而是len-i次
for(let j = 0; j < len - 1 - i;j++){
if(array[j] > array[j+1]){
[array[j], array[j+1]] = [array[j+1], array[j]];
}
}
}
}
选择排序
选择排序的原理如下:先将一个值的索引设置为最小索引minIndex,通常设置是第一个值,然后将最小索引的值与其他值进行比较,若最小索引值比其他的值要大,则将最小索引设置为该值的下标,然后最小索引的值与该值交换。
选择排序的时间复杂度是O(n*n)
function selection(array){
let len = array.length;
if(len <= 0){return false;}
for(let i = 0; i < len; i++){
let minIndex = i;
for(let j = i + 1;j < len;j++){
//索引的互换,minIndex可能会多次变化
minIndex = array[j] < array[minIndex] ? j : minIndex;
}
//值的互换
[array[i], array[minIndex]] = [array[minIndex], array[i]];
}
}
插入排序
插入排序的原理:默认第一个元素已经排好序,外层从第二个元素开始循环,里层循环从第一个元素开始,若里层循环满足条件,则交换值。里层循环将会进行多次比较
插入排序的时间复杂度为O(n*n)
function insert(array){
let len = array.length;
if(len<=0){return false;}
for(let i = 1; i < len; i++){
for(let j = i - 1; j >= 0 && array[j] > array[j+1]; j--){
[array[j], array[j+1]] = [array[j+1], array[j]];
}
}
}
归并排序
归并排序的原理如下。递归的将数组两两分开直到最多包含两个元素,然后将数组排序合并,最终合并为排序好的数组。假设我有一组数组
[2,7,5,6,1,4,3]
,中间数索引是 3,先排序数组[2,7,5,6]
。在这个左边数组上,继续拆分直到变成数组包含两个元素(如果数组长度是奇数的话,会有一个拆分数组只包含一个元素)。然后排序数组[2,7]
和[5,6]
,然后再排序数组[2,7,5,6]
,这样左边数组就排序完成,然后按照以上思路排序右边数组,最后将数组[2, 7, 5, 6]
和[1, 3, 4]
排序。归并排序的时间复杂度为O(N*logN)
function sort(array){
let len = array.length;
if(len <=0){return;}
mergeSort(array, 0, len - 1);
}
function mergeSort(array, left, right){
if(left === right) {return;}
//位运算更安全,不会溢出,相当于left+(right-left)/2
let mid = parseInt(left + ((right - left) >> 1));
mergeSort(array, left, mid);
mergeSort(array, mid+1, right);
let temp = [], i = 0, p1 = left, p2 = mid+1;
while(p1 <= mid && p2 <= right){
temp[i++] = array[p1] < array[p2] ? array[p1++] : array[p2++];
}
while(p1 <= mid){
temp[i++] = array[p1++];
}
while(p2 <= right){
temp[i++] = array[p2++];
}
for(let i = 0; i < temp.length; i++){
array[left + i] = temp[i];
}
return array;
}
快速排序
快排的原理如下。随机选取一个数组中的值作为基准值,如最右侧的值,从左至右取值与基准值对比大小。比基准值小的放数组左边,大的放右边,对比完成后将基准值和第一个比基准值大的值交换位置。然后将数组以基准值的位置分为两部分,继续递归以上操作
快速排序的时间复杂度为O(N*logN)
function quickSort(array){
let len = array.length;
if(len < 0){return;}
quick(array, 0, array.length-1);
}
function quick(array, left, right){
let index;
if(array.length > 1){
index = partition(array, left, right);
if(left < index - 1){
quick(array, left, index - 1);
}
if(index < right){
quick(array, index, right);
}
}
}
function partition(array, left, right){
let pivot = array[Math.floor((right + left) / 2)]; //选取中间的元素作为主元
let i = left, j = right;
while(i <= j){
while(array[i] < pivot){
i++;
}
while(array[j] > pivot){
j--;
}
if(i <= j){
[array[i], array[j]] = [array[j], array[i]];
i++;
j--;
}
}
return i;
}
堆排序
堆也可以作为一种 数据结构,通常情况下,堆被嵌入到储存数列的排序中,并且只通过交换数字来完成排序
堆排序利用了二叉堆的特性来做,二叉堆通常用数组表示,并且二叉堆是一颗完全二叉树(所有叶节点(最底层的节点)都是从左往右顺序排序,并且其他层的节点都是满的)
function heapSort(array) {
function maxHeapify(array, index, heapSize) {
let iMax, iLeft, iRight;
while (true) {
iMax = index;
iLeft = 2 * index + 1;
iRight = 2 * (index + 1);
if (iLeft < heapSize && array[index] < array[iLeft]) {
iMax = iLeft;
}
if (iRight < heapSize && array[iMax] < array[iRight]) {
iMax = iRight;
}
if (iMax !== index) {
//swap(array, iMax, index);
[array[iMax], array[index]] = [array[index], array[iMax]];
index = iMax;
} else {
break;
}
}
}
function buildMaxHeap(array) {
let i, iParent = Math.floor(array.length / 2) - 1;
for (i = iParent; i >= 0; i--) {
maxHeapify(array, i, array.length);
}
}
function sort(array) {
buildMaxHeap(array);
for (let i = array.length - 1; i > 0; i--) {
//swap(array, 0, i);
[array[0], array[i]] = [array[i], array[0]];
maxHeapify(array, 0, i);
}
return array;
}
return sort(array);
}