排序

184 阅读3分钟

冒泡排序

冒泡思想:每轮进行比较时,该轮最大的数下沉到最后面。下一轮比较的时候,上轮里面最大的数已经排好序了,就不会再参与比较。所以每次下一轮比较的时候,比较的数据就会比上一轮少一个。

 》 每冒泡一轮(外层for循环控制),选出这一轮中最大的数(内层for循环依次两两比较逐步移到最后...)
 》 一共进行arr.length-1轮 (2个比一轮、3个比两轮、、、)
 》 每轮比较arr.length-1次?(因为每一轮冒泡得到的最大值已无需再比 所以每轮比较arr.length-1-i次)
 
//先定义一个交换函数swap
function swap(arr, x, j) {
	[arr[x], arr[j]] = [arr[j], arr[x]]
}
//传值和传地址是不一样的,下面的swapError函数就是错误的,如果使用第二个swapError函数,那么数组里面的数是没办法成功交换的
function swapError(x,y){
	[x,y] = [y,x]
}

//定义冒泡函数
function maopao(arr) {
let len = arr.length - 1;
for (let i = 0; i < len; i++) {
  // j为比较次数 第i轮仅需比较length-1-i次
  for (let j = 0; j < len - i; j++) {
    // 通过判断相邻两项的大小 决定是否交换位置
    if (arr[j] < arr[j + 1]) {
      swap(arr, j, j + 1);
      }
    }
  }
}

冒泡排序:时间复杂度n方,空间复杂度1,稳定排序

有时候,可能只进行了 n 次冒泡,数组就已经是有序的了,甚至数组本来就是有序的。这时候我们希望:当发现一次冒泡后,数组有序,就停止下一次的冒泡,返回当前的数组。 这时候我们可以在每一趟的冒泡前,声明一个变量 exchangeFlag,将其设置为 false。冒泡过程中,如果发生了数据交换,就将 exchangeFlag 设置为 true。结束一趟冒泡后,我们就可以通过 exchangeFlag 知道 数据是否发生过交换。如果没有发生交换,就说明数组有序,直接返回该数组即可;否则说明还没有排好序,继续下一趟冒泡。

选择排序

排序思想:找到第一个最小的,放在第一位,再找到第二个最小的,放在第二位。

function xuanze(arr) {
  let len = arr.length;
  for (let i = 0; i < len - 1; i++) {
    let index = i;
    let min = arr[i];

  for (let j = i + 1; j < len; j++) {
    if (min > arr[j]) {
      min = arr[j];
      index = j;
    }
  }
  swap(arr, i, index);
  }
}

选择排序和冒泡排序比较的次数都是一样的,但是交换次数不同。选择排序在每轮比较完成后,才会有一次交换。而冒泡排序是依次比较相邻的两个数,符合条件就要交换。

选择排序的时间复杂度也是n方,空间复杂度为1,是不稳定的算法

快速排序

思路:以某某为基准,小的往前排,大的往后排 递归实现:

let quickSort = (numbers)=>{
    if(numbers.length<1){
        return [];
    }
    if(numbers.length==1){
        return numbers[0];
    }
    let midd = Math.floor(numbers.length/2);
    let midn = numbers[midd];
    let left = [];
    let right = [];
    numbers.splice(midd,1);
    for(let i=0;i<numbers.length;i++){
        if(numbers[i]<midn){
           left.push(numbers[i]);
        }
        else{
            right.push(numbers[i]);
        }
    }
    return quickSort(left).concat([midn],quickSort(right));
}

在写函数的时候,有几个需要注意的地方:midn,和数组numbers.splice可以结合,let midn = numbers.splice(midd,1)[0];这一句话。另外concat的时候,最好将midd化作数组[midd]。注意终结条件就是当数组numbers的长度小于等于1的时候,返回这个数组!!!

归并排序

哈哈,说归并排序是一个哲学问题。一个一个分开,每一个都是一个有序的长度为1的数组,然后合并为两个,两个再合并为四个...感觉像是没有排序一样,但其实不是的。

let mergeSort = (numbers){
    if(numbers.length<=1]){
        return numbers;
    }
    let midd = Math.floor(numbers.length/2);
    let left = numbers.slice(0,midd);
    let right = numbers.slice(midd);
    return merge(mergeSort(left),mergeSort(right));
}

let merge = (a,b)=>{
    if(a.length===0){
       return b;
    }
    if(b.length===0){
       return a;
    }
    if(a[0]>b[0]){
        return [b[0]].concat(merge(a,b.slice(1)));
    }else{
        return [a[0]].concat(merge(a.slice(1),b));
    }
}

计数排序

遍历一次数组,将数组中的数作为属性,出现的次数作为值,然后构成对象存储在hashTable中,然后遍历hashTable,按照属性从小到大输出。举例数组a=[6,5,3,7,6,9,1,2,3,7,4];a数组中共有10个数,其中3和6出现了2次,那么hashTable这个对象,存储的就应该是这样hashTable={"1":1;"2":1;"3":2;"4":1;"5":1;"6":2;"7":1;"9":1;}。

let countSort = (numbers)=>{
    //let mind=0;
    let minn=numbers[0];
    let maxn=numbers[0];
    let hashTable = {};
    let res = [];
    for(let i =1;i<numbers.length;i++){
        if(numbers[i]<minn){
            minn = numbers[i];
        }
        if(numbers[i]>maxn){
            maxn = numbers[i];
        }

    if(!(numbers[i] in hashTable)){
        hashTable[numbers[i]] = 1;
    }else{
        hashTable[numbers[i]] += 1;
    }
}

for(let j = minn;j<=maxn;j++){
    if(j in hashTable){
        for(let k=0;k<hashTable[j];k++){
           res.push(j);
        }
      
    }
}
return res;
}

插入排序

在要排序的数组里,假设前n-1个数已经是有序的,现将第n个数放到前面的有序数列中,使得这n个数也是排好顺序的,如此反复循环。

    function insertSort(arr){
    	for(let i=0;i<arr.length;i++){
        	let temp = arr[i]
        	let j=i 
        	for(;j>0;j--){
            	if(temp>arr[j-1]){
                	break;
                }
                arr[j]=arr[j-1]
            }
            arr[j] = temp
        }
        return arr
    }

数组去重

》利用es6的set集合

》利用map,创建一个空的map数据结构,循环数组,把数组里的元素作为key存到map中,如果该元素不在map里面,那就push进新数组。map里不会出现相同的key值。

》循环数组,看是否在新数组里面,在的话跳过,不在的话就push进新数组,用到了indexOf的方法;还有优化的循环去重方法<>

	function select(arr){
    	let newarr = []
        for(let i=0;i<arr.length;i++){
        	if(newarr.indexOf(arr[i])<0){
            	newarr.push(arr[i])
            }
        }
        return newarr
    }
    
    function select(arr){
      let newarr = []
          for(let i=0;i<arr.length;i++){
             for(let j=i+1;j<arr.length;j++){
             	if(arr[i]===arr[j]){
                	i++
                    j=i
                }
             }
             newarr.push(arr[i])
          }
          return newarr
    }
    
    
    

》排序后去重:计数排序,利用对象键值对,值大于1的只取一次就看可达到去重;也可以借助原生数组的sort方法排序后,相邻去重。遍历排序后的数组,将其与新数组的最后一项进行比较,相同进行下个遍历,不同push进新数组里面。

function select(arr){         
       arr.sort()
       let newarr = [arr[0]]
       for(let i=1;i<arr.length;i++){
       		if(arr[i]!==newarr[newarr.length-1]){
            	newarr.push(arr[i])
            }
       }
       return newarr
    }

参考