简单的排序算法整理(一)
本文将介绍几个比较简单的排序算法,并基于JavaScript实现,全文分为一、二部分。
1.插入类排序
插入类排序的思想是:在一个已排好序的序列区内,对待排序的无序序列中的记录逐个进行处理,每一步都讲待排序的记录和已排好的序列中的记录进行比较,然后有序的插入到该序列中,直到所有待排序的记录全部插入为止。
(1)直接插入排序(基于顺序查找)
思想:每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。
第一趟比较前两个数,然后把第二个数按大小插入到有序表中;
第二趟把第三个数据与前两个数从后向前扫描,把第三个数按大小插入到有序表中;
依次进行下去,进行了(n-1)趟扫描以后就完成了整个排序过程。
效率:
时间复杂度:平均O(n^2),不适合对于数据量比较大的排序应用。
空间复杂度:O(1)
稳定性:稳定
js代码:
function sort(elements){
for(var i = 1; i < elements.length; i++){
if(elements[i] < elements[i-1]){
var guard = elements[i];
var j = i - 1;
elements[i] = elements[j];
while(j >= 0 && guard < elements[j]){
elements[j+1] = elements[j];
j--;
}
elements[j+1] = guard;
}
}
}
(2)折半插入排序(基于折半查找)
思想:折半插入排序是对直接插入排序的简单改进,其过程是在将一个新元素插入已排好序的数组的过程中,寻找插入点时,将待插入区域的首元素设置为a[low],末元素设置为a[high],则轮比较时将待插入元素与a[m],其中m=(low+high)/2相比较,如果比参考元素小,则选择a[low]到a[m-1]为新的插入区域(即high=m-1),否则选择a[m+1]到a[high]为新的插入区域(即low=m+1),如此直至low<=high不成立,即将此位置之后所有元素后移一位,并将新元素插入a[high+1]。
效率:
时间复杂度:平均O(n^2),折半查找只是减少了比较次数,但是元素的移动次数不变。
空间复杂度:附加空间O(1)
稳定性:稳定
js代码:
function pushArrayWithIndex(arr, index, value) { // 将元素添加到数组的指定位置
var temArr = arr.slice(0, index);
temArr.push(value);
return temArr.concat(arr.slice(index));
}
function BiInsertSort(arr) { // 折半插入排序
var temArr = []; // 临时数组,存储已排序项
function getSortTmpIndex(subArr, num) {
var len = subArr.length;
if(0 == len)
return 0; // 当数组为空时,返回最开始位置
var cpmIndex = Math.ceil(len / 2); // 计算中间元素所在位置
if(cpmIndex > len - 1)
cpmIndex = len - 1;
if(num == subArr[cpmIndex]) { // 相等时直接返回
return cpmIndex;
}
if(num > subArr[cpmIndex]) { // 向后折半查找
cpmIndex++;
return cpmIndex + getSortTmpIndex(subArr.slice(cpmIndex), num);
}
if(num < subArr[cpmIndex]) { // 向前折半查找
return getSortTmpIndex(subArr.slice(0, cpmIndex), num);
}
}
for (var i in arr) {
var index = getSortTmpIndex(temArr, arr[i]); //查找arr[i]在temArr中的位置
temArr = pushArrayWithIndex(temArr, index, arr[i]); //将元素插入到查找位置
}
return temArr;
}
var arr = [3, 7, 6, 5, 9, 1, 2, 3, 1, 7, 4];
console.log("before:"+arr);
arr = BiInsertSort(arr);
console.log(" after:"+arr);
(3)希尔排序(基于逐趟缩小增量)
思想:先将整个待排元素分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序时(增量足够小),再对全体元素进行一次直接插入排序。
过程:
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。
所有距离为d1的倍数的记录放在同一个组中。
先在各组内进行直接插入排序;
然后,取第二个增量d2<d1
重复上述的分组和排序,直至所取的增量
dt=1(dt<dt-1…<d2<d1),
即所有记录放在同一组中进行直接插入排序为止。
效率:
时间复杂度:平均 O(n^2)
空间复杂度:需要辅助空间 O(1)
稳定性:不稳定
js代码:
function ShellInsert(arr){
var len=arr.length;
for(var i=Math.floor(len/2);i>0;i=Math.floor(i/2)){
for(var j=i;j<len;j++){
for(var k=j-i;k>=0 && arr[k]>arr[i+k];k-=i){
var temp=arr[k];
arr[k]=arr[i+k];
arr[i+k]=temp;
}
}
}
return arr;
}
var arr=[49,38,65,97,76,13,27,49,55,04];
console.log("before:"+arr);
arr =ShellInsert(arr);
console.log(" after:"+arr);
2.交换类排序
选择类排序的基本思想是:对待排序记录的关键字进行两两比较,只有发现两个记录为逆序就进行交换,直到没有逆序为止。
(1)冒泡排序
思想:
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
效率:
时间复杂度:平均 O(n)
空间复杂度:需要辅助空间 O(1)
稳定性:稳定
js代码:
function bubbleSort(arr) {
var i = arr.length, j;
var tempExchangVal;
while (i > 0) {
for (j = 0; j < i - 1; j++) {
if (arr[j] > arr[j + 1]) {
tempExchangVal = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tempExchangVal;
}
}
i--;
}
return arr;
}
var arr = [3, 2, 4, 9, 1, 5, 7, 6, 8];
console.log("before: "+arr);
var arrSorted = bubbleSort(arr);
console.log(" after: "+arrSorted);
(2)快速排序
思想:从待排序列中任意选择一个记录,以该记录的关键字作为“枢轴”,凡其关键字小于枢轴的记录均移至该记录之前,反之,凡是关键字大于枢轴的记录均移至该记录之后。
快速排序采用了分治策略,而分治法的基本思想是:将原问题分解为若干规模更小但是结构与原问题相似的子问题,递归的解决这些问题,然后将这些子问题的解组合为原问题的解。
效率:
时间复杂度:平均 O(Nlog*2*N)
空间复杂度:需要辅助空间 O(log*2*N)
稳定性:不稳定
js代码:
function quickSort (arr){
if (arr.length <= 1) { return arr; }
var pivotIndex = Math.floor(arr.length / 2);
var pivot = arr.splice(pivotIndex, 1)[0];
var left = [];
var right = [];
for (var i = 0; i < arr.length; i++){
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quickSort(left).concat([pivot], quickSort(right));
}
var arr=[46,68,12,2,5,33,80,19,33];
console.log("before: "+arr);
var arr=quickSort (arr);
console.log(" after: "+arr);