冒泡排序
冒泡思想:每轮进行比较时,该轮最大的数下沉到最后面。下一轮比较的时候,上轮里面最大的数已经排好序了,就不会再参与比较。所以每次下一轮比较的时候,比较的数据就会比上一轮少一个。
》 每冒泡一轮(外层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
}