1.冒泡排序
两两相邻记录的关键字进行比较,如果反序则交换,直到没有反序的记录为止。
var arr=[2,4,3,7,5,8]
var temp=0;
for(var i=arr.length;i>0;i--){
for(var j=0;j<i-1;j++){
if(arr[j]>arr[j+1]){
temp=arr[j]
arr[j]=arr[j+1]
arr[j+1]=temp
}
}
}
console.log(arr)
2.直接插入排序
每一步将一个待排序的元素,按其排序码的大小,插入到前面已经排好序的一组元素的适当位置上去,直到元素全部插入为止。当插入第i(i >= 1)时,前面的V[0],V[1],……,V[i-1]已经排好序。这时,用V[I]的排序码与V[i-1],V[i-2],…的排序码顺序进行比较,找到插入位置即将V[i]插入,原来位置上的元素向后顺移。
var arr=[2,4,3,7,5,8]
var len=arr.length;
for(var i=1;i<len;i++){
var key=arr[i];
var j=i-1;
while(j>=0&&arr[j]>key){
arr[j+1]=arr[j];
j=j-1;
}
arr[j+1]=key;
}
console.log(arr)
3.选择排序
从无序当中进行两两比较选择出最小的,然后放在最前面。
var arr=[2,4,3,7,5,8]
var len=arr.length;
var temp=0;
for(var i=0;i<len;i++){
for(var j=i+1;j<len;j++){
if(arr[i]>arr[j]){
temp=arr[i]
arr[i]=arr[j]
arr[j]=temp
}
}
}
console.log(arr)
4.快速排序
从待排序中选取一个记录的关键字为key,通过一趟排序分割成两部分,其中一部分记录的关键字不大于key,另一部分记录的关键字不小于key,之后分别对这两部分记录进行排序,直到整个序列都有序了为止。
var arr=[2,4,3,7,5,8]
function qsort(arr){
var len=arr.length;
if(len==0){
return []
}
var key=arr[0];
var left=[];
var right=[];
for(var i=1;i<len;i++){
if(key>arr[i]){
left.push(arr[i])
}
else{
right.push(arr[i])
}
}
return qsort(left).concat(key,qsort(right))
}
console.log(qsort(arr))
function quickSort(num) {
return _quickSort(num, 0, num.length - 1); // 将整个num数组快速排序,left和right分别指向数组左右两端。
}
function _quickSort(num, left, right) {
var list = [[left, right]]; // 将[left,right]存入数组中,类似于递归入栈
while (list.length > 0) { // 若list不为空,循环弹出list最后一个数组进行快排
var now = list.pop(); // 弹出list末尾。(也可用list.shift()取出list第一个数组,但在数据量较大时,这种方式效率较低)
if (now[0] >= now[1]) { // 若左右指针相遇,待排序数组长度小宇1,则无需进行快排(注意不能写成now[0]==now[1],这里now[0]是有可能大于now[1]的
continue;
}
var i = now[0], j = now[1], flag = now[0]; // 以下与递归方法相同,请参考上面的递归详解
while (i < j) {
while (num[j] >= num[flag] && j > flag) j--;
if (i >= j) {
break;
}
while (num[i] <= num[flag] && i < j) i++;
let temp = num[flag];
num[flag] = num[j];
num[j] = num[i];
num[i] = temp;
flag = i;
}
list.push([now[0], flag - 1]); // 将flag左边数组作为待排序数组,只需将左右指针放入list即可。
list.push([flag + 1, now[1]]); // 将flag右边数组作为待排序数组,只需将左右指针放入list即可。
}
return num
}
var num=[2,5,4,6,8,7,9,1]
console.log(quickSort(num))//[1,2,4,5,6,7,8,9]
整个步骤如下:
1.num:{9,17,0,6,10,5},其中基数为9,即flag=0,left=0,right=5
定义i = left,j = right,先使 j 不断左移直到num[j]<num[flag],交换这两个数,并使flag = j;
此时 num:{ 5,17,0,6,10,9 }
2.再使 i 不断右移直到num[i]>num[flag],交换这两个数 ,并使flag = i
此时 num:{ 5,9,0,6,10,17 }
不断重复以上步骤,直到i==j时
此时 num:{ 5,6,0,9,10,17 }
将基数左右划分为两部分,分别作为待排序数组进行此步骤
即待排序数组num:{ 5,6,0 },9,10,17,
执行步骤①后num:{ 0,5,6 },9,10,17
接下来排序另一部分num:0,5,6,{ 9,10,17 } ,不需要移动
接下来排num:0,5,6,9,{ 10,17 },也不需要移动( ̄▽ ̄)"
快速排序的优化
基准的选择
1.固定基准
缺点就是如果数组元素基本有序时,此时的划分就容易产生最坏的情况,时间复杂度O(n^2)
2.随机基准
在待排数组有序或基本有序的情况下,选择使用固定基准影响快排的效率。为了解决数组基本有序的问题,可以采用随机基准的方式来化解这一问题。
3.由于随机基准选取的随机性,使得它并不能很好的适用于所有情况。目前,比较好的方法就是三数取中选取基准。它的思想是:选取数组开头,中间和结尾的元素,通过比较,选择中间的值作为快排的基准。
4.当快排达到一定深度后,划分的区间很小时,再使用快排的效率不高。当待排序列的长度达到一定数值后(5-20),可以使用插入排序。在划分到很小的区间时,里面的元素已经基本有序了,再使用快排,效率就不高了。所以,在结合插入排序后,程序的执行效率有所提高。
5.归并排序
归并排序是建立在归并操作上的一种有效的排序算法,该算法采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列。即先使每个子序列有序,再使子序列段间有序,若将两个有序表合并成一个有序表,最后就形成了有序列表。
function mergeSort(arr) { //采用自上而下的递归方法
var len = arr.length;
if(len < 2) {
return arr;
}
var middle = Math.floor(len / 2);
var left = arr.slice(0, middle);
var right = arr.slice(middle);
return merge(mergeSort(left), mergeSort(right));
}
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());
}
}
while (left.length)
result.push(left.shift());
while (right.length)
result.push(right.shift());
return result;
}
var arr=[2,4,8,5,7]
console.log(mergeSort(arr))
6.基数排序
即从最低位开始排序,然后由最低位到最高位依次排序。
7.排序算法的时间复杂度和空间复杂度及稳定性
8.排序算法的使用场景
1.若n较小n<=50,可采用直接插入或直接选择排序。
2.若初始状态基本有序的时候,可以选择直接插入排序和冒泡排序。
3.若n较大的时候,则应采用时间复杂度为O(nlgn),快速排序和归并排序
如果还要是稳定的,那么就选择归并排序
9.排序算法的稳定性
稳定的:如果存在多个具有相同排序码的记录,经过排序后,这些记录的相对次序仍然保持不变,则这种排序算法称为稳定的。
10.时间复杂度和空间复杂度
时间复杂度:评估执行程序所需的时间。可以估算出程序对处理器的使用程度。
空间复杂度:评估执行程序所需的存储空间。可以估算出程序对计算机内存的使用程度。
10.排序大类
1.插入排序:直接插入排序和希尔排序
2.交换排序:快速排序和冒泡排序
3.选择排序:简单选择排序和堆排序
4.归并排序:二路归并排序和多路归并排序