前端常用的一些算法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端常用的一些算法相关的知识,希望对你有一定的参考价值。

参考技术A /*去重*/

function delRepeat(arr)

var newArray=new Array();

var len=arr.length;

for(var i=0;i

for(var j=i+1;j



if(arr[i]==arr[j])



++i;





newArray.push(arr[i]);



return newArray;



var arr=new Array("red","red","1","5","2");

alert(delRepeat(arr));

/*二分法*/

又称为折半查找算法,但是有缺陷就是要求数字是预先排序好的

function binary(items,value)

var startIndex=0,

stopIndex=items.length-1,

midlleIndex=(startIndex+stopIndex)>>>1;

while(items[middleIndex]!=value && startIndex

if(items[middleIndex]>value)

stopIndex=middleIndex-1;

else

startIndex=middleIndex+1;



middleIndex=(startIndex+stopIndex)>>>1;



return items[middleIndex]!=value ? false:true;



/*十六进制颜色值的随机生成*/

function randomColor()

var arrHex=["0","2","3","4","5","6","7","8","9","a","b","c","d"],

strHex="#",

index;

for(var i=0;i<6;i++)

index=Math.round(Math.random()*15);

strHex+=arrHex[index];



return strHex;



/*一个求字符串长度的方法*/

function GetBytes(str)

var len=str.length,

bytes=len;

for(var i=0;i

if(str.CharCodeAt>255)

bytes++;





return bytes;



/*插入排序*/

所谓的插入排序,就是将序列中的第一个元素看成一个有序的子序列,然后不段向后比较交换比较交换。

function insertSort(arr)

var key;

for(var j = 1; j < arr.length ; j++)

//排好序的

var i = j - 1;

key = arr[j];

while(i >= 0 && arr[i] > key)

arr[i + 1] = arr[i];

i --;



arr[i + 1] = key;



return arr;



/*希尔排序*/

希尔排序 ,也称 递减增量排序算法

其实说到底也是插入排序的变种

function shellSort(array)

var stepArr = [1750, 701, 301, 132, 57, 23, 10, 4, 1]; // reverse()在维基上看到这个最优的步长较小数组

var i = 0;

var stepArrLength = stepArr.length;

var len = array.length;

var len2 =  parseInt(len/2);

for(;i < stepArrLength; i++)

if(stepArr[i] > len2)

continue;



stepSort(stepArr[i]);



//排序一个步长

function stepSort(step)

//console.log(step)使用的步长统计

var i = 0, j = 0, f, tem, key;

var stepLen = len%step > 0 ?  parseInt(len/step) + 1 : len/step;

for(;i < step; i++)//依次循环列

for(j=1;/*j < stepLen && */step * j + i < len; j++)//依次循环每列的每行

tem = f = step * j + i;

key = array[f];

while((tem-=step) >= 0)//依次向上查找

if(array[tem] > key)

array[tem+step] = array[tem];

else

break;





array[tem + step ] = key;







return array;



/*快速排序*/

快速排序算法就系对冒泡排序的一种改进,采用的就是算法理论中的分治递归的思想。

具体做法:通过一趟排序将待排序的纪录分割成两部分,其中一部分的纪录值比另外一部分的纪录值要小,就可以继续分别对这两部分纪录进行排序;不段的递归实施上面两个操作,从而实现纪录值的排序。

function sort(arr)

return quickSort(arr,0,arr.length-1);

function quickSort(arr,l,r)

if(l

var mid=arr[parseInt((l+r)/2)],i=l-1,j=r+1;

while(true)

//大的放到右边,小的放到左边, i与j均为游标

while(arr[++i]

while(arr[--j]>mid);

if(i>=j)break;//判断条件

var temp = arr[i];

arr[i]=arr[j];

arr[j]=temp;



quickSort(arr,l,i-1);

quickSort(arr,j+1,r);



return arr;





function main()

var list=new Array(49,38,65,97,76,13,27);

document.write(sort(list).valueOf());



main();

/*冒泡法*/

function bullSort(array)

var temp;

for(var i=0;i

for(var j=array.length-1;j>i;j--)

if(array[j]

temp = array[j];

array[j]=array[j-1];

array[j-1]=temp;







return array;



/*js递归实现方案*/

递归函数是在一个函数通过调用自身的情况下去解决的:

方式如下:

function factorial(num)

if(num<=1)

return 1;

else

return num*factorial(num-1);





但是这在js里面可能会出现错误:

var anotherFactorial = factorial;

factorial=null;

alert(anoterFactorial(4));

因为在调用anoterFactorial时内部的factorial已经不存在了。

解决方法是通过arguments.callee来解决。

如下:

function factorial(num)

if(num<=1)

return 1;

else

return num*arguments.callee(num-1);



var anotherFactorial = factorial;

factorial = null;

alert(anotherFactorial(4));

成功!!!!



/**js模拟多线程**/

if (Array.prototype.shift==null)

Array.prototype.shift = function ()

var rs = this[0];

for (var i=1;i

this.length=this.length-1

return rs;



if (Array.prototype.push==null)

Array.prototype.push = function ()

for (var i=0;i

return this.length;



var commandList = [];

var nAction = 0;//控制每次运行多少个动作

var functionConstructor = function().constructor;

function executeCommands()

for (var i=0;i

if (commandList.length>0)

var command = commandList.shift();

if (command.constructor == functionConstructor)

if (command.scheduleTime == null || new Date()-command.scheduleTime>0)

command();

else

commandList.push(command);





function startNewTask()

var resultTemp = document.getElementById("sampleResult").cloneNode(true);

with (resultTemp)

id="";style.display="block";style.color=(Math.floor(Math.random()* (1<<23)).toString(16)+"00000").substring(0,6);



document.body.insertBefore(resultTemp,document.body.lastChild);

commandList.push(function()simThread(resultTemp,1););

nAction++;



function  simThread(temp,n)

if (temp.stop) n--;

else temp.innerhtml = temp.innerHTML - (-n);

if (n<1000)

commandList.push(function()simThread(temp,++n));

else

var command = function()document.body.removeChild(temp);;nAction--;;

command.scheduleTime = new Date()-(-2000);

commandList.push(command);





window.onload = function()setInterval("executeCommands()",1);

/

/*选择法排序*/

选择法主要有三种:

《1》简单的选择排序:简单的前后交互。

/*简单选择法排序*/

其实基本的思想就是从待排序的数组中选择最小或者最大的,放在起始位置,然后从剩下的数组中选择最小或者最大的排在这公司数的后面。

function selectionSort(data)



var i, j, min, temp , count=data.length;

for(i = 0; i < count - 1; i++)

/* find the minimum */

min = i;

for (j = i+1; j < count; j++)

   if (data[j] < data[min])

min = j;



/* swap data[i] and data[min] */

temp = data[i];

data[i] = data[min];

data[min] = temp;



return data;



《2》树型排序:又称锦标赛排序,首先对n个元素进行两两比较,然后在其中[n/2]个较小者再进行两两比较如此重复直至选出最小的关键字的纪录为止。(可用完全二差树表示)。缺点:辅助空间需求过大,和“最大值”进行多余比较

《3》堆排序:(不适用于纪录数较少的文件)

堆排序算法的过程如下:

1)得到当前序列的最小(大)的元素

2)把这个元素和最后一个元素进行交换,这样当前的最小(大)的元素就放在了序列的最后,而原先的最后一个元素放到了序列的最前面

3)的交换可能会破坏堆序列的性质(注意此时的序列是除去已经放在最后面的元素),因此需要对序列进行调整,使之满足于上面堆的性质.

重复上面的过程,直到序列调整完毕为止.

js实现:

/**

*堆排序

* @param items数组

* @return排序后的数组

*/

function heapSort(items)



items = array2heap(items); //将数组转化为堆

for(var i = items.length - 1; i >= 0; i--)



items = swap(items, 0, i); //将根和位置i的数据交换(用于将最大值放在最后面)

items = moveDown(items, 0, i - 1); //数据交换后恢复堆的属性



return items;



/**

*将数组转换为堆

* @param items数组

* @return堆

*/

function array2heap(items)



for(var i = Math.ceil(items.length / 2) - 1; i >= 0; i--)



items = moveDown(items, i, items.length - 1); //转换为堆属性



return items;



/**

*转换为堆

* @param items数组

* @param first第一个元素

* @param last最后一个元素

* @return堆

*/

function moveDown(items, first, last)



var largest = 2 * first + 1;

while(largest <= last)



if(largest < last && items[largest] < items[largest + 1])



largest++;



if(items[first] < items[largest])



items = swap(items, first, largest); //交换数据

first = largest;   //往下移

largest = 2 * first + 1;



else



largest = last + 1; //跳出循环





return items;



/**

*交换数据

* @param items数组

* @param index1索引1

* @param index2索引2

* @return数据交换后的数组

*/

function swap(items, index1, index2)



var tmp = items[index1];

items[index1] = items[index2];

items[index2] = tmp;

return items;



var a = [345,44,6,454,10,154,3,12,11,4,78,9,0,47,88,9453,4,65,1,5];

document.write(heapSort(a));

所谓归并就是将两个或者两个以上的有序表合成一个新的有序表。

递归形式的算法在形式上较为简洁但实用性较差,与快速排序和堆排序相比,归并排序的最大特点是,它是一种稳定的排序方法。

js实现归并:

function MemeryArray(Arr,n, Brr, m)

     var i, j, k;

var Crr=new Array();

i = j = k = 0;

while (i < n && j < m)



if (Arr[i] < Brr[j])

Crr[k++] = Arr[i++];

else

Crr[k++] = Brr[j++];



while (i < n)

Crr[k++] = Arr[i++];

while (j < m)

Crr[k++] = Brr[j++];

return Crr;



var Arr=new Array(45,36,89,75,65);

var Brr=new Array(48,76,59,49,25);

alert(MemeryArray(Arr , Arr.length , Brr , Brr.length));

归并排序待续,先睡了:

归并排序:

//将有二个有序数列a[first...mid]和a[mid...last]合并。

function mergearray(Arr,first,mid,last,tempArr)



var i = first, j = mid + 1;

var m = mid,   n = last;

var k = 0;

while (i <= m && j <= n)



if (Arr[i] < Arr[j])

tempArr[k++] = Arr[i++];

else

tempArr[k++] = Arr[j++];



while (i <= m)

tempArr[k++] = Arr[i++];

while (j <= n)

tempArr[k++] = Arr[j++];

for (i = 0; i < k; i++)

Arr[first + i] = tempArr[i];



function mergesort(Arr,first,last)



var tempArr=new Array();

if (first < last)



var mid = (first + last)>>>1;

mergesort(Arr, first, mid, tempArr);    //左边有序

mergesort(Arr, mid + 1, last, tempArr);  //右边有序

mergearray(Arr, first, mid, last, tempArr);  //再将二个有序数列合并



return  Arr;



var Arr=new Array(1,65,45,98,56,78);

alert(mergesort(Arr,0,Arr.length-1));

/*比较两个字符串的相似性-Levenshtein算法简介*/

问题与描述:

近似字符串匹配问题

说明:设给定样本,对于任意文本串,样本P在文本T中的K-近似匹配(K-approximate match)是指P在T中包含最多K个差异的匹配,这里的差别指:

(1)修改:P与T中对应的字符不同

(2)删除:T中含有一个未出现在P中的字符

(3)插入:T中不包含出现在P中的一个字符

(也就是编辑距离问题)

例如:

T: a p r o x i o m a l l y

P: a p p r o x i m a t l y

经过1:插入2:删除3:修改

那么就是一个3-近似问题

事实上,两个字符串可能有不得出不同的差别数量,所以K-近似匹配要求:

(1)差别数最多为K个

(2)差别数为所有匹配方式下最少的称为编辑距离

(字符串T到P最少的差别数称为T和P的编辑距离)

试验要求:

(1)利用动态规划方法给出两个字符串编辑距离的算法

(2)分析复杂度

(3)考虑其它方法

Levenshtein Distance来文史特距离

goodzzp

LD也叫edit distance,它用来表示2个字符串的相似度,不同于Hamming Distance,它可以用来比较2个长度不同的字符串。LD定义为需要最少多少步基本操作才能让2个字符串相等,基本操作包含3个:

1,插入;

2,删除;

3,替换;

比如,kiteen和sitting之间的距离可以这么计算:

1,kitten – > sitten,替换k为s;

2,sitten – > sittin,替换e为i;

3,sittin – > sitting,增加g;

所以,其LD为3;

计算LD的算法表示为:

int LevenshteinDistance(char str1[1..lenStr1], char str2[1..lenStr2])

// d is a table with lenStr1+1 rows and lenStr2+1 columns

declare int d[0..lenStr1, 0..lenStr2]

// i and j are used to iterate over str1 and str2

declare int i, j, cost

for i from 0 to lenStr1

d[i, 0] := i

for j from 0 to lenStr2

d[0, j] := j

for i from 1 to lenStr1

for j from 1 to lenStr2

if str1[i] = str2[j] then cost := 0

else cost := 1

d[i, j] := minimum(

d[i-1, j ] + 1,// deletion

d[i , j-1] + 1,// insertion

d[i-1, j-1] + cost// substitution

)

return d[lenStr1, lenStr2];

这个算法其实就是一个矩阵的计算:

k i t t e n

0 1 2 3 4 5 6

s 1 1 2 3 4 5 6

i 2 2 1 2 3 4 5

t 3 3 2 1 2 3 4

t 4 4 3 2 1 2 3

i 5 5 4 3 2 2 3

n 6 6 5 4 3 3 2

g 7 7 6 5 4 4 3

首先给定第一行和第一列,然后,每个值d[i,j]这样计算:d[i,j] = min(d[i-1,j]+ 1,d[i,j-1] +1,d[i-1,j-1]+(str1[i] == str2[j]?0:1));

最后一行,最后一列的那个值就是LD的结果。

LD(str1,str2) <= max(str1.len,str2.len);

有人提出了Levenshtein automaton(Levenshtein自动机)来计算和某个字符串距离小于某个值的集合。这样能够加快近似字符串的计算过程。见文献:Klaus U. Schulz, Stoyan Mihov, Fast String Correction with Levenshtein-Automata. International Journal of Document Analysis and Recognition, 5(1):67--85, 2002.

A Guided Tour to Approximate String Matching GONZALONAVARRO

这篇文章里面对这个方面(字符串相似)进行了很多描述。其中,包含了动态规划法计算Edit distance的方法。

js实现:

//求两个字符串的相似度,返回差别字符数,Levenshtein Distance算法实现

function Levenshtein_Distance(s,t)

var n=s.length;// length of s

var m=t.length;// length of t

var d=[];// matrix

var i;// iterates through s

var j;// iterates through t

var s_i;// ith character of s

var t_j;// jth character of t

var cost;// cost

// Step 1

if (n == 0) return m;

if (m == 0) return n;

// Step 2

for (i = 0; i <= n; i++)

d[i]=[];

d[i][0] = i;



for (j = 0; j <= m; j++)

d[0][j] = j;



// Step 3

for (i = 1; i <= n; i++)

s_i = s.charAt (i - 1);

// Step 4

for (j = 1; j <= m; j++)

t_j = t.charAt (j - 1);

// Step 5

if (s_i == t_j)

cost = 0;

else

cost = 1;



// Step 6

d[i][j] = Minimum (d[i-1][j]+1, d[i][j-1]+1, d[i-1][j-1] + cost);





// Step 7

return d[n][m];



//求两个字符串的相似度,返回相似度百分比

function Levenshtein_Distance_Percent(s,t)

var l=s.length>t.length?s.length:t.length;

var d=Levenshtein_Distance(s,t);

return (1-d/l).toFixed(4);



//求三个数字中的最小值

function Minimum(a,b,c)

return a



var str1="ddsddf",str2="xdsfsx";

alert(Levenshtein_Distance_Percent(str1,str2));

前端算法入门:刷算法题常用的 JS 基础扫盲

参考技术A

此篇属于前端算法入门系列的第一篇,主要介绍常用的 数组方法 、 字符串方法 、 遍历方法 、 高阶函数 、 正则表达式 以及相关 数学知识 。

在尾部追加,类似于压栈,原数组会变。

在尾部弹出,类似于出栈,原数组会变。数组的 push & pop 可以模拟常见数据结构之一:栈。

在头部压入数据,类似于入队,原数组会变。

在头部弹出数据,原数组会变。数组的 push (入队) & shift (出队) 可以模拟常见数据结构之一:队列。

concat 会在当前数组尾部拼接传入的数组,然后返回一个新数组,原数组不变。

在数组中寻找该值,找到则返回其下标,找不到则返回 -1 。

在数组中寻找该值,找到则返回 true ,找不到则返回 false 。

将数组转化成字符串,并返回该字符串,不传值则默认逗号隔开,原数组不变。

翻转原数组,并返回已完成翻转的数组,原数组改变。

从 start 开始截取到 end ,但是不包括 end

可参考 MDN:Sort [5]

将数组转化成字符串,并返回该字符串,逗号隔开,原数组不变。

返回指定索引位置处的字符。类似于数组用中括号获取相应下标位置的数据。

类似数组的concat(),用来返回一个合并拼接两个或两个以上字符串。原字符串不变。

indexOf ,返回一个字符在字符串中首次出现的位置, lastIndexOf 返回一个字符在字符串中最后一次出现的位置。

提取字符串的片断,并把提取的字符串作为新的字符串返回出来。原字符串不变。

使用指定的分隔符将一个字符串拆分为多个子字符串数组并返回,原字符串不变。

match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配,并返回一个包含该搜索结果的数组。

注意事项 :如果 match 方法没有找到匹配,将返回 null 。如果找到匹配,则 match 方法会把匹配到以数组形式返回,如果正则规则未设置全局修饰符 g ,则 match 方法返回的数组有两个特性: input 和 index 。 input 属性包含整个被搜索的字符串。 index 属性包含了在整个被搜索字符串中匹配的子字符串的位置。

replace 接收两个参数,参数一是需要替换掉的字符或者一个正则的匹配规则,参数二,需要替换进去的字符,仔实际的原理当中,参数二,你可以换成一个回调函数。

在目标字符串中搜索与正则规则相匹配的字符,搜索到,则返回第一个匹配项在目标字符串当中的位置,没有搜索到则返回一个 -1 。

toLowerCase 把字母转换成小写, toUpperCase() 则是把字母转换成大写。

includes 、 startsWith 、 endsWith , es6 的新增方法, includes 用来检测目标字符串对象是否包含某个字符,返回一个布尔值, startsWith 用来检测当前字符是否是目标字符串的起始部分,相对的 endwith 是用来检测是否是目标字符串的结尾部分。

返回一个新的字符串对象,新字符串等于重复了指定次数的原始字符串。接收一个参数,就是指定重复的次数。原字符串不变。

最常用的 for 循环,经常用的数组遍历,也可以遍历字符串。

while 、 do while 主要的功能是,当满足 while 后边所跟的条件时,来执行相关业务。这两个的区别是, while 会先判断是否满足条件,然后再去执行花括号里面的任务,而 do while 则是先执行一次花括号中的任务,再去执行 while 条件,判断下次还是否再去执行 do 里面的操作。也就是说 do while 至少会执行一次操作 .

拷贝一份遍历原数组。

for…of 是 ES6 新增的方法,但是 for…of 不能去遍历普通的对象,** for…of 的好处是可以使用 break 跳出循环。**

面试官:说一下 for...in 和 for...of 区别?

返回一个布尔值 。当我们需要判定数组中的元素是否满足某些条件时,可以使用 every / some 。这两个的区别是, every 会去判断判断数组中的每一项,而 some 则是当某一项满足条件时返回。

reduce 从左到右将数组元素做“叠加”处理,返回一个值。 reduceRight 从右到左。

Object.keys 方法的参数是一个对象,返回一个数组。该数组的成员都是该对象自身的(而不是继承的)所有属性名,且只返回可枚举的属性。

Object.getOwnPropertyNames 方法与 Object.keys 类似,也是接受一个对象作为参数,返回一个数组,包含了该对象自身的所有属性名。但它能返回不可枚举的属性。

这里罗列一些我在刷算法题中遇到的正则表达式,如果有时间可认真学一下 正则表达式不要背 [7]

持续更新,敬请期待……

若一个正整数无法被除了 1 和它自身之外的任何自然数整除,则称该数为质数(或素数),否则称该正整数为合数。

以上是关于前端常用的一些算法的主要内容,如果未能解决你的问题,请参考以下文章

字节跳动前端团队工作中会使用到的数据结构和算法

一些常用的机器学习算法实现

我接触过的前端数据结构与算法

一些常用/经典算法的文章的收集整理

熬夜肝了这一份来自牛客,LeetCode,剑指 Offer大佬整理的前端常用算法面试题.pdf,你也能进大厂

熬夜肝了这一份来自牛客,LeetCode,剑指 Offer大佬整理的前端常用算法面试题.pdf,你也能进大厂