自己整理的一些JS常用算法,有些是摘网上的,有些是自己整理的。
一、快速排序
思路
1.假设数组ary有n项, 取出其中的某一项p;
2. 定义两个空数组left
和right
,让p与数组其余项进行比对,小于p的push
在left
,其余的push
在right
,并且让left
数组位于p左侧,right
位于p右侧;
3. 对left
和right
的项重复上诉1和2步骤,知道当left
和right
为空,最终获得p*n个数,并对p进行concat
拼接,最终获得从小到大排序数组。
原理
根据《算法导论》快速排序中,当划分产生的两个子问题分别包括了n-1个元素和0个元素时,快速排序产生最坏情况发生,算法的复杂度是T(n)=O(n^2)
,在可能的最平衡的划分中,即其中一个子问题的规模为[n/2],而另外一个子问题的规模为[n/2]-1,这种情况下,快速排序的性能最好,算法的复杂度为T(n)=O(nlgn)
,事实上,任何一种常数比例的划分都会产生深度为O(lgn)的递归树,其中每一层的时间代价都是O(n),因此,只要划分是常数比例的,算法的运行时间总是O(nlgn);
- 最坏情况公式:
T(n)=T(n-1)+T(0)+O(n)=T(n-1)+O(n)
,每一层递归的代价可以被累加起来,从而算得T(n)=O(n^2); - 最好情况公式:
T(n)=2T(n/2)+O(n)
,在上式中,我们忽略了一些余项以及减1操作的影响。最终递归式的解为T(n)=O(nlgn)
- 平均的划分公式:
T(n)=T(9n/10)+T(n/10)+cn
这里,我们显式地写出了O(n)
项中所隐含的常数c
.由于递归树中,每层的代价都是cn
,知道在深度log10n=O(lgn)
达到递归的边界条件时为止,之后每层代价之多为cn
。递归在深度为log10/9n=O(lgn)
处为止,因此,快速排序的总代价为O(nlgn)
;
伯乐在线 有篇文章这样描述他们http://blog.jobbole.com/100349/
O()表示算法的时间复杂度,O(1)表示常数阶复杂度,O(n)表示线性阶复杂度,O(n^2)表示平方阶复杂度
例子:
数据量低时,O(1) 和 O(n^2)的区别可以忽略不计。比如,你有个算法要处理2000条元素。
- O(1) 算法会消耗 1 次运算
- O(log(n)) 算法会消耗 7 次运算
- O(n) 算法会消耗 2000 次运算
- O(n*log(n)) 算法会消耗 14,000 次运算
- O(n^2) 算法会消耗 4,000,000 次运算
看不懂,直接上代码
function qSort(ary) {
var left=[];//创建两个空数组
var right=[];
var p=ary[0]// 取出数组第一项
if(ary.length==0){//当数组长度为零时,返回空数组
return ary;
}
for(var i=1;i<ary.length;i++){//用数组剩余值与第一个值做比较
if(p>ary[i]){
left.push(ary[i])
}else{
right.push(ary[i])
}
}
return qSort(left).concat(p,qSort(right));
}
var ary=[1,2,3,2,22,11,32,54,3,232,4234,4,5]
console.log(qSort(ary));
二、冒泡排序
思路
1. 假设数组ary
有n项;
2. 取出ary
的第i项aryi
,与其余项进行一一比较,如果小于则位置不变,大于则替换位置;
3. 重复上述2
内容,同时,每遍历一次,与aryi
比较数组项应-1
;
4. 注意数组最后一项不需要进行比较。
原理
- 根据《数据结构》严蔚敏版说解释:“起泡排序的过程,首先将第一个记录的关键字和第二个记录的关键字进行比较,若为逆,则将两个记录交换之,然后比较第二个记录和第三个记录的关键字,以此类推,知道第n-1个记录和第n个记录的关键字进行过比较为止。”
- 也就说,假如有数组ary=[1,2,3,4,5],每比较一次,我们假设时间消耗是1.也就是ary的排序所消耗时间为:第一次比较4次,第二次比较3次,第三次比较2次,第四次比较1次,总计比较10次。
当数组为ary=[1,2,3,4····,n-2,n-1,n];
第几次比较 | 比较次数 |
---|---|
1 | n-1 |
2 | n-2 |
3 | n-3 |
···· | ···· |
n-3 | 3 |
n-2 | 2 |
n-1 | 1 |
最终比较结果为O(n)=(n-1)+(n-2)+(n-3)+3+2+1=n*(n-1)/2.(数学公式:(首项+尾项)*项数/2)
也由于当n足够大时,得出总的时间复杂度为O(n^2)[忽略其他影响值比较小的数]
不明所以?上代码
* @param ary 数组
* @param type type表示升序(0)还是降序(1)
function sortAry(ary,type) {
for(var i=ary.length-1;i>0;i--){
for(var j=0;j<i;j++){
if(ary[j]>ary[j+1]){
var temp=ary[j];
ary[j]=ary[j+1];
ary[j+1]=temp;
}
}
}
type===1?ary.reverse():void 0;
return ary;
}
ary=[1,2,3,4,4,2,2,11];
console.log(sortAry(ary,0));
数组去重
方法一
思路
1. 在JS的对象类型中,每个属性名都是唯一的;
2. 同理,数组中的每一项,也是唯一的;
3. 如果让数组的每一项与值相等,即ary[i]=i,是否数组ary
的所有值都会是唯一的?利用这个原理,就有了我们接下来的数组去重方法;
由于文字太绕,这里直接上代码
function mySlice(ary) {
var obj=[];
for(var i=0;i<ary.length;i++){
var cur=ary[i];
if(obj[cur]==ary[i]){
ary.splice(i,1);
i--;//由于i从0开始,这里需要i--防止数组坍塌
}else{
obj[cur]=cur;
}
}
}
ary=[1,2,3,3,2,1,3,4,5,5,23,,4,4,,3,3];
mySlice(ary);
方法二
思路
1. 与冒泡排序原理相同,假设数组ary
有n项;
2. 取出ary
的第i项aryi
,与其余项进行一一比较,如果相等,删除该项,同时记得如果i从0开始,需要–i防止数组坍塌;
function mySlice(ary) {
for(var i=0;i<ary.length-1;i++){
for(var j=i+1;j<ary.length;j++){
if(ary[i]==ary[j]){
ary.splice(j,1);
j--;
}
}
}
}
ary=[11,2,2,3,3,2,1,3,4,5,5,23,4,4,4,4,3,3];
mySlice(ary);
反转字符串
原网站:http://www.w3cplus.com/javascript/how-to-reverse-a-string-in-javascript-in-different-ways.html
- 需求:假设有
var str="abcdefg"
;将字符串str
延中间字母左右调换位置,即调换后为str="efgdabc"
;
思路
1. 先判断字符串是奇数行还是偶数行;(这边分别使用两种字符串方法来操作)
2. 如果是奇数行,用Math.round(str.length/2)
求出中间字符串的位置mid
,用字符串方法substr(0,mid)
和substr(mid-1)
分别将中间字符串左右两边的值分别取出,最后用concat
重新拼接;
3. 如果是偶数行,我们统一取出中间两个字符串中的mid-right
字符串,再用字符串方法split(mid-right)
为中间为分界,拆分成数组,在用reverse
进行数组倒序,最后用join
拼接回字符串,加上mid-right
即可
不知什么,直接看代码
function reversString(str) {
var mid=(str.length%2==0);
if(!mid){//奇数
mid=Math.round(str.length/2);
var arry1=str.substr(0,mid-1);
var arry2=str.substr(mid);
var arry3=str.charAt(mid-1);
var arry=arry2.concat(arry3,arry1);
return arry;
}else{//偶数
mid=str.charAt(str.length/2);
var ary= str.split(mid);
ary=ary.reverse();
ary=ary.join("");
ary=mid+ary;
return ary;
}
}
console.log(reversString("abcdefg"))
数组随机取整
案例原网站:http://www.w3cplus.com/javascript/rounding-recipes.html
/*思路:Math.random()函数经常返回0~1之间的浮点数。从技术上讲,Math.random()返回的数会出现0,但永远不会出现1
*
* */
//从min-max里随机抽取6个不重复的整数,按从小到大输出(不包含min) //Math.ceil:向上取整
function random(min,max) {
var a=[];
while (a.length<6){
var random=Math.ceil(Math.random()*(max-min))+min;
var cur=random;
if(a.indexOf(random)==-1){
a.push(random);
}
}
a.sort(function (a,b) {
return a-b;
})
return a;
}
console.log(random(0,33)) ;
Mutation
同样转载自w3cplus
//Mutations对一个包含两个元素的数组,检测数组中的第二个元素的所有字母是否都在第一个元素中出现过,如果出现true,否则false(忽略大小写)
//方法一:遍历
function mutation (arr) {
var baseStr = arr[0].toLowerCase().split('');
var testStr = arr[1].toLowerCase().split('');
for (var i = 0, len = testStr.length; i < len; i++){
var temp = baseStr.indexOf(testStr[i]);
if (temp === -1) {
return false;
}
}
return true;
}
//方法二:every方法
function mutation (arr) {
var baseStr = arr[0].toLowerCase().split(''),
testStr = arr[1].toLowerCase().split('');
return testStr.every(function (letter) {
return baseStr.indexOf(letter) != -1; });
}
Falsey Bouncer
同样转载自w3cplus
1. falsy和truthy的概念:
- falsy(既false值):false/null/undefined/NaN/0/”“(空字符串),其余都为truthy值
//通过JavaScript处理falsy值。创建一个bouncer(arr)函数,通过逻辑判断,删除数组中的falsy值.
//完整版:
function bouncer (arr) {
function isFalsy (value) {
return Boolean(value);
}
var result = arr.filter(isFalsy);
return result;
}
//简略版
function bouncer(arr) {
return arr.filter(Boolean);
}
var arr=[null,1,2,"null",undefined,"1"]
console.log(bouncer(arr));-->[1, 2, "null", "1"]
从数组中寻找元素并删除元素
同样转载自w3cplus
1. 概念:有个数组ary=[[1,2,3,4,5,6,7,8],1,2];从中第一项单独与第二、三项分开来,并将第一项里面跟第二、三项值相同的数删除。
2. 实例
1. 利用push,分离原数组,然后用filter遍历原数组第一项,并过滤掉新数组中有同一项的值
function destroyer(arr) {
var newArray = arguments[0]; // [1, 2, 1, 3, 2, 1, 3, 4, 2, 6]// 获取目标数组
var removeArgs = []; // 声明一个空数组,用来存储需要从`newArray`中删除的元素
for (var i = 1, len = arguments.length; i < len; i++) {
removeArgs.push(arguments[i]);
}
function isFalse (value) {// 声明filter()方法的callback函数
return removeArgs.indexOf(value) === -1;
}
return newArray.filter(isFalse);// newArray中删除1,2
}
destroyer([1,2,1,3,2,1,3,4,2,6],1,2);
2. 第一步与方法一相同,只是后面一步用了一个判断,跟Mutation方法原理相同
function destroyer(arr) {
var newArray = arguments[0]; //[1,2,1,3,2,1,3,4,2,6]
var removeArgs = [];
for (var i = 1, len = arguments.length; i < len; i++)
{
removeArgs.push(arguments[i]);
}
function isFalse (value) {
for (var j = 0; j < removeArgs.length; j++) {
if (removeArgs[j] == value) {
return false;
}
} return true;
}
return newArray.filter(isFalse); // 从newArray数组中删除1,2
}
3. 一种简化版
function destroyer(arr) {
var removeArgs =Array.prototype.slice.call(arguments, 1); // [1, 2]
// arr = [1, 2, 1, 3, 2, 1, 3, 4, 2, 6]
return arr.filter(function(value) {
return removeArgs.indexOf(value) < 0;
});
}
4. 利用套了两个for循环的遍历,将重复的项删除,同时注意防止数组坍塌
function destroyer(arr) {
// arguments = [[1, 2, 1, 3, 2, 1, 3, 4, 2, 6],1,2]
for (var i= 1; i< arguments.length; i++) {
// arr = [1, 2, 1, 3, 2, 1, 3, 4, 2, 6]
for (var j = 0; j < arr.length; j++) {
if (arguments[i] == arr[j]) {
arr.splice(j, 1);
--j;
}
}
}
return arr;
}
5. 也是套用两个for循环的遍历,将重复的项删除,只是这里面的for循环是一个递减,所以不用考虑数组坍塌。
function destroyer(arr) {
for (var i = 1; i < arguments.length; i++) {
for (var j = arr.length - 1; j >= 0; j--) {
if (arguments[i] == arr[j]) {
arr.splice(j, 1);
}
}
}
return arr;
}
6. ES6的 new Set方法和has方法
function destroyer(arr) {
var removeArgs = new Set(Array.prototype.slice.call(arguments, 1)); //Set {1, 2}
function isFalse (value) {
return !removeArgs.has(value);
}
return arr.filter(isFalse);
}
或者
function destroyer(arr, ...items) {
var removeArgs = new Set(items); // Set {1, 2}
function isFalse (value) {
return !removeArgs.has(value);
}
return arr.filter(isFalse);
}
以上内容,都是从其他地方摘取