JS排序算法-笔记

1、算法分类:(1)比较类排序[非线性](2)非比较类排序[线性]

2、(1.1)稳定:a=b,a在b前,排序后仍保持a在b前

(1.2)不稳定:排序后a可能会出现在b后

(2.1)时间复杂度:排序数据总操作次数

(2.2)空间复杂度:存储空间度量

排序算法

一、冒泡排序

二、选择排序

三、插入排序

四、希尔排序

五、归并排序

六、快速排序

七、堆排序

八、计数排序

九、桶排序

十、基数排序

其他算法

一、LRU实现_最少使用置换算法

二、二叉树构建,广度/深度遍历

(1)深度优先遍历

1.1递归实现(前中后序)

1.2栈实现

(2)广度优先遍历-队列实现(先进先出)

三、KMP算法


排序算法

一、冒泡排序

平均时间复杂度:O(n^{2}) 稳定:相同大小元素,不进行交换位置

用代码表示则如下:

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

优化:标志性变量exchange,如果某一趟都未进行交换数据,结束排序

function  bubbleSort1(arr){
var i=arr.length-1;
while(i>0){
let pos=0;
for(let j=0;j<i;j++)
{
    if(arr[j]>arr[j+1]){
            let temp=arr[j+1];
            arr[j+1]=arr[j];
            arr[j]= temp;
            pos=j;    //记录最后交换的位置
            }
        }
    i=pos;    //判断是否需要下一趟
    }
return arr;
}
二、选择排序

平均时间复杂度::O(n^{2})不稳定:[原理]查找最小(大)元素,存放到数组起始位置,交换后元素不再变换位置       

适用:数据规模越小越好

function selectionSort(arr){
var len=arr.length;
var minIndex,temp;
for(let i=0;i<len-1;i++){
    minIndex=i;
    for(let j=i+1;j<len;j++){
    if(arr[j]<arr[minIndex]){
    minIndex=j;
            }
        }
    temp=arr[i];
    arr[i]=arr[minIndex];
    arr[minIndex]=temp;
    }
return arr;
}
三、插入排序

平均时间复杂度:O(n^{2})稳定:[原理]从左到右比较插入,建立有序数组,类似扑克牌排序;如果相同元素,则插入到该元素后面

适用:数据量不大,算法稳定性强,数据局部或整体有序排列

function insertionSort(arr){
var len=arr.length;
var preIndex,current;
for(var i=1;i<len;i++){
preIndex=i-1;
current=arr[i];
        while(preIndex>=0&&arr[preIndex]>current){
            arr[preIndex+1]=arr[preIndex];
            arrIndex--;
        }
    arr[preIndex+1]=current;
    }
return arr;
}
四、希尔排序

平均时间复杂度:O(n^{1.3})不稳定:[原理]间隔序列,简单插入排序改进版,优先比较远距离元素,又叫缩小增量排序。相同元素在各自插入排序中移动,可能会打乱稳定性。

function shellSort(arr){
var len=arr.length;
for(let gap=Math.floor(len/2);gap>0;gap=Math.floor(gap/2)){    //分组交替执行
    for(var i=gap;gap<len;i++){
        var j=i;
        var current=arr[i];
        while(j-gap>=0&&current<arr[j-gap]){
            arr[j]=arr[j-gap];
            j=j-gap;
            }
        arr[j]=current;
        }
    }
return arr;
}
五、归并排序

平均时间复杂度:O(n\log_{2}n)稳定:[原理]分治法,分-合两个步骤.,在1个或2个元素时,1个元素不会交换,2个元素如果大小相等也不会交换 

适用:算法稳定,与选择算法类似性能不受输入数据影响,需要占据额外内存空间

function mergeSort(arr){
    const len=arr.length;
if(len<2){
    return arr;
    }
    var middle=Math.floor(arr/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>0&&right.length>0){
    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;
}
六、快速排序

平均时间复杂度:O(n\log_{2}n)不稳定:[原理]选取基准数字,头部(或尾部),如果元素的相等可能被分到不同的子数组中,改变它们的相对位置

适用:适宜内部排序,数据量过大且杂乱

function quickSort(arr){
const rec=(arr)=>{
if(arr.length<=1){return arr;}
const left=[];
const right=[];
const mid=arr[0];
for(let i=1;i<arr.length;i++){
if(arr[i]<mid){
left.push(arr[i]);
}else{
right.push(arr[i]);
            }
        }
return [...rec(left),...rec(right)];
    }
return arr;
}
七、堆排序

平均时间复杂度:O(n\log_{2}n)不稳定:堆积类似二叉树,将根节点和叶子节点交换后调整得到数组,可能会改变它们的相对位置

var len;
function buildMaxHeap(arr){    //建立大顶堆
    len=arr.length;
    for(var i=Math.floor(len/2);i>=0;i--){
    heapify(arr,i);
    }
}
function heapify(arr,i){    //调整
var left=2*i+1,
    right=2*i+2,
    largest=i;
    if(left<len&&arr[left]>arr[largest]){
    largest=left;
    }
    if(right<len&&arr[right]>arr[largest]){
    largest=right;
    }
    if(largest!=i){
    swap(arr,i,largest);    //交换
    heapify(arr,largest);    //调整
    }
}
function swap(arr,i,j){    //交换
var temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
function heapSort(arr){
buildMaxHeap(arr);    //先建立一次大顶堆
for(var i=arr.length-1;i>0;i--){  //交换根节点和叶子节点元素
swap(arr,0,i);
len--;
heapify(arr,0);    //调整
}
return arr;
}
八、计数排序

平均时间复杂度:O(n+k)稳定:[原理]不是比较排序算法,核心将输入数据值转化为键存储在开辟的额外数组空间。计数排序输入数据必须为有确定范围的整数   输入元素n个0到k的整数

适用:当k不是很大并且序列比较集中,排序速度快于任何比较排序算法

function countingSort(arr,maxValue){    //确定数组范围中最大值
var bucket=new Array(maxValue+1),   //开辟额外数组空间
    sortedIndex=0,                //存放新数组元素个数
    arrLen=arr.length,
    buckLen=maxValue+1;

    for(var i=0;i<arrLen;i++){    //把旧数组以键格式存放到新内存空间中
    if(!buckLen[arr[i]]){
    bucket[arr[i]]=0;
    }
    bucket[arr[i]]++;
} 
   for(var j=0;j<buckLen;j++){    //取出
    while(buckLen[j]>0){
    arr[sortedIndex++]=j;
    buckLen[j]--;
    }
}
return arr;
}
九、桶排序

平均时间复杂度:O(n+k)稳定:计数排序升级版,利用函数映射,将输入数据均匀分布到有限桶内

function bucketSort(arr,bucketSize){
if(arr.length===0){return arr;}
var i,
minValue=arr[0],
maxValue=arr[0];
for(i=1;i<arr.length;i++){
if(arr[i]<minValue){
minValue=arr[i];
}else if(arr[i]>maxValue){
maxValue=arr[i];
    }
}
//桶的容量初始化
var DEFAULT_BUCKET_SIZE=10;
bucketSize=bucketSize||DEFAULT_BUCKET_EISE;
var bucketCount=Math.floor((maxValue-minValue)/bucketSize)+1;
var buckets=new Array(bucketCount);
for(i=0;i<buckets.length;i++){
buckets[i]=[];//初始化
}
//利用映射将数组元素存放入桶中
for(let i=0;i<arr.length;i++){
buckets[Math.floor((arr[i]-minValue)/bucketSize)].push(arr[i]);
}
//桶内排序并输出
arr.length=0;
for(i=0;i<buckets.length;i++){
    insertionSort(buckets[i]);    //使用插入排序对每个桶内的数据进行排序
    }
    for(var j=0;j<buckets[i].length;j++){
    arr.push(buckets[i][j]);
    }
}
return arr;
}
十、基数排序

平均时间复杂度:O(n*k)稳定:分别排序,分别收集,但性能比桶排序略差

var counter=[];
function radixSort(arr,maxDigit){
var mod=10;    //求尾数,10内,100内
var dev=1;    //整数,个位,百位
for(var i=0;i<maxDigit;i++,mod*=10,dev*=10){
    for(var j=0;j<arr.length;j++){    //存入存储空间
        var bucket=parseInt((arr[j]%mod)/dev);
        if(counter[bucket]==null){
        counter[bucket]=[];
            }
        counter[bucket].push(arr[j]);
        }
    var pos=0;//取出
    for(var j=0;j<counter.length;j++){
    var value=null;
    if(counter[j]!=null){
    while((value=counter[j].shift())!=null){    //取出判断是否有元素
    arr[pos++]=value;    //存入新数组中
                }
            }
        }
    }
return arr;
}

其他算法

一、LRU实现_最少使用置换算法
//使用构造函数
function LRU(length){
this.length=length;
this.arr=[];
}
//根据键值取数
LRU.prototype.get=function(key){
let index=this.arr.findIndex(item=>item.key=key);
if(index!==-1){    //如果存在
const result=this.arr.splice(index,1);
}
this.arr.push(result);
return result.val;
}
LRU.prototype.set=function(key,val){
let index=this.arr.findIndex(item=>item.key=key);
if(index!==-1){
this.arr.splice(index,1);
}
this.arr.push({key,value});
//如果超出限表删除最前列元素
if(this.length<this.arr.length){
this.arr.shift();
    }
}
二、二叉树构建,广度/深度遍历

按照前序遍历出二叉树:

function createTree(nodeList){
if(Array.isArray(nodeList)){
const len=nodeList.length;
if(!len) return;
else{
//获取最前的节点数
const data=nodeList.shift();
let node=null;    //构建二叉树
if(data){
node=new TreeNode(data);
node.left=createTree(nodeList);
node.right=createTree(nodeList);
        }
return node;
    }
}else{
throw Error("请输入一个节点序列");
}
}
const tree=createTree([1,3,null,null,5,7,9,null,11]);
console.log(tree);
(1)深度优先遍历
1.1递归实现(前中后序)
//二叉树前序遍历-DLR
function preOrderTraveral(nodeTree){
if(!nodeTree) return;
else{
console.log(nodeTree.data);
preOrderTraveral(nodeTree.left);
preOrderTraveral(nodeTree.right);
    }
}

console.log("前序遍历:");
console.log(preOrderTraveral(tree));

//二叉树中序遍历-LDR
function middleOrderTraveral(nodeTree){
if(!nodeTree) return;
else{
middleOrderTraveral(nodeTree.left);
console.log(nodeTree.data);
middleOrderTraveral(nodeTree.right);
    }
}

console.log("中序遍历:");
console.log(middleOrderTraveral(tree));

//二叉树后序遍历-LRD
function postOrderTraveral(nodeTree){
if(!nodeTree) return;
else{
postOrderTraveral(nodeTree.left);
postOrderTraveral(nodeTree.right);
console.log(nodeTree.data);
    }
}

console.log("后序遍历:");
console.log(postOrderTraveral(tree));
1.2栈实现
/*栈实现深度优先遍历*/

//前序遍历
function stackPreTravel(nodeTree){
if(!nodeTree)return;
let stack=[];
let node=nodeTree;
//如果节点不为空,且栈不为空,则继续循环
while(node||stack.length){
//如果节点不为空,则继续向左边遍历
while(node){
    console.log(node.data);
    stack.push(node);
    node=node.left;
        }
//当左节点为空时,栈不为空,遍历右节点子树
while(stack.length){
    node=stack.pop();
    node=node.right;
        }
    }
}

//中序遍历
function stackMiddleTravel(nodeTree){
if(!nodeTree) return;
let stack=[];
let node=nodeTree;
while(node||stack.length){
while(node){
    stack.push(node);
    node=node.left;
}
while(stack.length){
    node=stack.pop();
    console.log(node.data);
    node=node.right;
        }
    }
}

//后序遍历
function stackPostTravel(nodeTree){
if(!nodeTree) return;
let stack=[];
let node=nodeTree;
while(node||stack.length){
while(node){
    stack.push(node);
    node=node.left;
    }
}
if(stack.length){
    node=stack.pop();
    if(node.right){
    stack.push(node);
    node=node.right;
    }
    console.log(stack.data);
}
}
(2)广度优先遍历-队列实现(先进先出)
/*使用队列实现广度优先遍历*/

function levelOrderTravel(nodeTree){
if(!nodeTree) return;
//初始化一个队列
let queue=[];
//将根节点入队
queue.push(nodeTree);
let node=null;
while(queue.length){
//先进先出
node=queue.shift();
console.log(node.data);
//如果出队节点存在左节点,则入队
if(node.left){ queue.push(node.left);}
//右边同上
if(node.right){ queue.push(node.right);}
}
}
console.log(levelOrderTravel(tree));
三、KMP算法

实现步骤:

1、构建最大长度表    

2、构建next数组,计算移动位数(可与1合并)

3、KMP搜索算法实现

//1、构建最大长度表
function generatePrefixTable(pattern){
var prefix_table=[];
var len=0;    //    最少公共前后缀长度初始化为0
prefix_table[0]=len;    //初始化i=0,len=0
var i=1;
var n=pattern.length;
while(i<n){
if(pattern[len]===pattern[i]){
len++;
prefix_table[i]=len;
i++;
}else{
if(len>0){    //侧移
len=prefix_table[len-1];
}else{
prefix_table[i]=0;
i++;
            }
        }
    }
return prefix_table;
}
//2、构建next数组
function generateNextArr(prefix_table){
for(var i=prefix_table.length-1;i>0;i--){
prefix_table[i]=prefix_table[i-1];
}
prefix_table[0]= -1;
}
//3、KMP搜索算法的实现
function kmp(str,pattern){
var prefix_table=generatePrefixTable(pattern);
generateNextArr(prefix_table);
var i=0;    //str 指针
var j=0;    pattern指针
while(i<str.length&&j<pattern.length){
if(str[i]==pattern[j]){
i++;
j++;
}else{
j=prefix_table[j];
if(j===-1){ 
i++;
j++;
        }
    }
}
if(j===pattern.length){
return i-j;
}else{
return -1;
    }
}
kmp("bbc abcdab abcdabcdabde", "cdabd")  // 结果输出为17,正确

KMP算法优化

直接构建next数组

function generateNextArr(pattern){
var i=0;
var j=-1;
var next=[];
next[0]=-1;
while(i<pattern.length){
if(j===-1||pattern[i]===pattern[j]){
        i++;
        j++;
        next[i]=j;
        }else{
        j=next[j];
        }
    }
return next;
}
function kmp(str,pattern){
var next=generateNextArr(pattern);
var i=0;
var j=0;
while(i>str.length&&j<pattern.length){
if(str[i]===pattern[j]||j===-1){
i++;
j++;
}else{
j=next[j];
    }
}
if(j===pattern.length){
return i-j;
}else{
return -1;
}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值