经典排序算法
冒泡排序
原理:
1.比较相邻的元素,如果第一个比第二个大,就交换位置。
2.重复以上步骤,依次得出最大值,次大值。。。。
3.重复以上步骤,直到没有任何一对数字需要比较
算法分析:
1.若文件的初始状态是正序,一趟扫描完成排序。所需要的关键字的比较次数C和记录移动的次数M达到最小值:Cmin=n-1,Mmin=0,故冒泡排序的时间复杂度O(n);
2.若初始文件是反序的,需要进行n-1趟排序。每趟排序要进行n-i(1<=i<=n-1)次关键字的比较,且每次比较都必须移动记录3次(比如交换a[i-1]和a[i];tmp=a[i-1];a[i-1]=a[i];a[i]=tmp)来达到交换记录的位置。此时,比较和移动次数都达到最大值:Cmax=n*(n-1)/2=O(n2);Mmax=3n*(n-1)/2=O(n2), (根据等差数列求得) 故冒泡排序的最坏的时间复杂度是O(n2)。
综上两点,冒泡排序总的平均时间复杂度为O(n2);
总结:平均时间复杂度:O(n2), 稳定度:稳定, 空间复杂度:O(1)
代码实现:
var arr=[1,5,3,2];
function bubbleSort(arr){
for(let i=0,l=arr.length;i<l;i++){
for(let j=i+1;j<l;j++){
if(arr[i]>arr[j]){
let temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
}
return arr;
}
bubbleSort(arr);
选择排序
原理:每趟从待排序的记录中选出关键字最小的记录,顺序放到末尾,直到全部排序结束
1.从待排序中,找到最小的元素
2.如果最小元素不在排序序列的第一个元素,和第一个元素进行交换
3.从剩下的n-1个元素中,找出最小的元素,重复1,2步骤
算法分析:
1.时间复杂度:O(n2)
2.空间复杂度:O(1)
代码实现:
var arr1=[5,3,2,1];
function selectSort(arr){
let len=arr.length,minIndex;//minIndex记录最小的索引
for(var i=0;i<len-1;i++){
minIndex=i; //记录最小的索引
for(var j=i+1;j<len;j++){
if(arr[j]<arr[minIndex]){
minIndex=j;
}
}
if(i!=minIndex){
var temp =arr[i];
arr[i]=arr[minIndex];arr[minIndex]=temp;}
}
return arr;
}
selectSort(arr1);
插入排序/折半排序(insertion sort)
原理:插入排序的思想有点像打扑克牌时候,我们插入扑克牌的做法。一般打牌的时候,我们都是把抓到的牌按顺序放到已经按顺序排好的牌中。
1.将第二个位置的数字,和左面的数字比较,放入合适的位置(相当于手中有牌,又抓了一张牌)
2.将i个位置的数字,和其所在位置的左面的数字依次比较,放入合适的位置
3.重复以上步骤,直到排序完成。
算法分析:
1.时间复杂度:O(n2)
2.空间复杂度:O(1)
代码实现:
var arr1=[5,4,3,2];
function insertionSort(arr){
var cur,preIndex;
for(var i=1;i<arr.length;i++){
cur = arr[i];preIndex=i-1;
while(preIndex>=0 && arr[preIndex]>cur){
arr[preIndex+1]=arr[preIndex];
preIndex--;
}
arr[preIndex+1]=cur;
}
return arr;
}insertionSort(arr1);
归并排序(merge sort)
原理:是将两个顺序序列合并成一个顺序序列的方法。
算法分析:
1.算法稳定性:稳定
2.时间复杂度:O(n*log2n),归并排序的形式就是一棵二叉树,它需要遍历的次数就是二叉树的深度,而根据完全二叉树可以得出
3.空间复杂度:n
代码实现:
function mergeSort(arr){
if(arr.length==1){
return arr;
}
//首先将无序数组划分为两个数组
var mid = Math.floor(arr.length/2);
var left = arr.slice(0,mid);
var right = arr.slice(mid);
return merge(mergeSort(left),mergeSort(right));
}
function merge(left,right){
var re=[];
while(left.length>0 && right.length>0){
if(left[0]<right[0]){
re.push(left.shift());
}else{
re.push(right.shift());
}
}
return re.concat(left,right);
}
mergeSort(arr1);
这段合并排序的代码相当简单直观,但是mergeSort()函数会导致很频繁的自调用。一个长度为n的数组最终会调用mergeSort() 2*n-1次,这意味着如果需要排序的数组长度很大会在某些栈小的浏览器上发生栈溢出错误。
补充:浏览器栈的大小限制,可以用如下的代码
var cnt = 0;
try {
(function() {
cnt++; arguments.callee(); })();
} catch(e) {
console.log(e.message, cnt);
}
为了防止遇到栈溢出的代码,将递归改为了迭代:
function merge(left, right) {
var result = [];
while (left.length && right.length) {
if (left[0] < right[0])
result.push(left.shift());
else
result.push(right.shift());
}
return result.concat(left, right);
}
function mergeSort(a) {
if (a.length === 1)
return a;
var work = [];
for (var i = 0, len = a.length; i < len; i++)
work.push([a[i]]);
work.push([]); // 如果数组长度为奇数
for (var lim = len; lim > 1; lim = ~~((lim + 1) / 2)) {
for (var j = 0, k = 0; k < lim; j++, k += 2)
work[j] = merge(work[k], work[k + 1]);
work[j] = []; // 如果数组长度为奇数
}
return work[0];
}
快速排序(quick sort)
原理:是冒泡排序基础上的递归分治法
算法分析:
1.最坏时间复杂度O(n2)
2.平均时间复杂度:O(nlogn)
function quickSort(arr) {
if(arr.length<=1) {
return arr;
}
let leftArr = [];
let rightArr = [];
let q = arr[0];
for(let i = 1,l=arr.length; i<l; i++) {
if(arr[i]>q) {
rightArr.push(arr[i]);
}else{
leftArr.push(arr[i]);
}
}
return [].concat(quickSort(leftArr),[q],quickSort(rightArr));
分享几个网址:
1.h5模拟算法地址,超好,推荐,大学时候知道它就好了
2.各种算法地址,超有趣,推荐