js算法题

1. == 与 === 的区别

①如果是两个相同类型的原始类型数据相比,==和===一样,都是比较他们的值。

②如果两个都是引用类型,==和===一样,都是看他们是否指向同一个对象。

③如果两个类型不同,===直接返回false,==会进行数据格式。如果是两个基本类型相比,==会把他们都转换为Number型,比较他们实际的值;如果是一个基本类型一个引用类型,则会调用引用类型的valueOf函数,得到他的原始值再与基本类型的数进行判断。

④null与undefined都与自身严格相等;但是null与undefined是普通相等。

注意下面这种情况也是相等的哦~

        var arr = ['a'];
        arr.valueOf = function(){
            return '1';
        };
        var num = 1;
        arr.toString = function(){
            return 'b';
        }
        console.log(arr == num);//true

会先调用arr的valueOf得到'1'这个字符串,再将字符串转成数值1与num进行比较。

2.实现add(1)(2)(3)

只有充分理解闭包,才能做对的一道题。

原本应该写成

       function add(x){
            return function(y){
                return function(z){
                    return x+y+z;
                }
            }
        }

但是当我再增加一个参数的时候,这个就又要再return,肯定是不可取的。

我们要做的是return出去一个函数,里面包含本次传来的参数:

        function add(x){
            var temp = function(y){
                return add(x+y);
            };
            return temp;
        };

由于我们要打印成字符串或是显示他原始值,因此我们需要:

            //JavaScript中,打印和相加计算,会分别调用toString(获得字符串)或valueOf(获得原始值)函数
            //所以我们重写tmp的toString和valueOf方法,返回sum的值
            temp.valueOf = temp.toString = function(){
                return x;
            }

完整的版本是:

        function add(x){
            var temp = function(y){
                return add(x+y);
            };
            //JavaScript中,打印和相加计算,会分别调用toString(获得字符串)或valueOf(获得原始值)函数
            //所以我们重写tmp的toString和valueOf方法,返回sum的值
            temp.valueOf = temp.toString = function(){
                return x;
            }
            return temp;
        };

十几天之后我又看到一道类似的题,但是他要求更加严格了,需要这个函数也能实现add(1,2,3)。

       function add(x) {
            if(arguments.length > 1) {
                var sum = 0;
                for(var i = 0; i < arguments.length; i++) {
                    sum += arguments[i];
                }
                return sum;
            } else {
                var sum = x;
                var tmp = function(y) {
                    sum = sum + y;
                    return tmp;
                };
                tmp.toString = function() {
                    return sum;
                };
                return tmp;
            }
        }

其实只是加了判断是否参数大于1,如果参数大于1可以直接加,如果不是就要使用我们之前的函数了。

3.验证素数

首先了解素数的含义:任何只能被1和自身整除的大于1的数。所以我们首先排除小于等于1的,其次搞清楚这个数如果再自己的一半这个数之前不能被整除,那他就永远不能被整除了,所以我们的循环只需要写到他的一半即可。

       function isPrime(n){
            if(n<=1) return false;
            for(var i=2; i<=n/2; i++){
                if(n%i === 0) return false;
            }
            return true;
        }

4.统计一个字符串出现最多的字母

首先我们需要声明一个对象,他的属性是传入字符串所拥有的字母,对应的值是字母重复的次数,最后我们通过比较出对象中最大值,找到字符串中重复次数最多的字母。得到对象可以通过先将字符串转化成数组,再使用forEach参数element、i、arr得到这个数组从i位之后是否还包含element。

       function count(str){
            var arr = str.split("");
            var obj = {};
            arr.forEach(function(element,i,arr) {
                obj[element] = obj[element]||0;
                if(arr.slice(i+1,arr.length).indexOf(element) !== -1){
                    obj[element]++;
                }
            }, this);
            var maxChar = '',maxValue = 1;
            for(var i in obj){
                if(obj[i]>=maxValue){
                    maxChar += i;
                    maxValue = obj[i];
                }
            };
            return maxChar;
        };

从其他地方看到一种获得对象的方法是通过charAt函数,此法不需要先把字符串转化为数组,很方便。

            let obj = {};
            for(var i=0; i<str.length; i++){
                if(!obj[str.charAt(i)]){
                    obj[str.charAt(i)] = 1;
                }else{
                    obj[str.charAt(i)] += 1;
                }
            }

5.数组从小到大或从大到小排列

冒泡排序:掌握一个重点,声明一个中间量,先将小的给中间量,再将大的给小的位置上,将中间量的值给大的位置,完成转换。

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

插入排序:插入排序的重点是选取一个关键数,并将此数与他之前排着的已经排序完毕的数进行比较,如果一直比那些数小,则把关键数前面的一个数向后错一位,直到找到比关键数小的数,就把关键数插到这个数的后面。

       function insertSort(arr){
            for(let i=1; i<arr.length; i++){
                let temp = arr[i];
                let j= i-1;
                while(j>0 && arr[j]>temp){
                    arr[j+1] = arr[j];
                    j--;
                }
                arr[j+1] = temp;//注意是j+1不是j,因为你是要把temp插到小于他的数后面
            }
            return arr;
        }

快速排序:找到中间那个点并从数组中取出他,并将小于它的放在左边,大于他的放在右边,然后再对左右两个数组进行递归快速排序,直到这个数组只有中间一个值构成的数组时,就返回这个数组(这是最底层)。

       function quickSort(arr){
            if(arr.length<=1){
                return arr;//如果数组只有一个数,就直接返回;
            }

            var num = Math.floor(arr.length/2);//找到中间数的索引值,如果是浮点数,则向下取整
            var numValue = arr.splice(num,1);//找到中间数的值并取出他
            var left = [];
            var right = [];

            for(var i=0;i<arr.length;i++){
                if(arr[i]<numValue){
                    left.push(arr[i]);//基准点的左边的数传到左边数组
                }
                else{
                    right.push(arr[i]);//基准点的右边的数传到右边数组
                }
            }
            return quickSort(left).concat(numValue,quickSort(right));//递归不断重复比较
        }

6.实现类似getElementsByClassName 的功能

本题最重要的是要搞清楚className这个DOM属性,它的样子是"className1 className2……"可以选择两种方式。

首先是简单的分成数组进行判断:

       function getElementsByClassName(oEle,sClass){
            var aEle = oEle.getElementsByTagName('*');
            var arr = [];
            for(var i=0; i<aEle.length; i++){
                var arrClass = [];
                arrClass = aEle[i].className.split(" ");
                if(arrClass.includes(sClass)){
                    arr.push(aEle[i]);
                }
            }
            return arr;
        }

也可以使用正则:

        function getElementsByClassName(oEle,sClass){
            var aEle = oEle.getElementsByTagName('*'),
            //reg = new RegExp('(^|[ \n\r\t\f])'+sClass+'($|[ \n\r\t\f])'),
            reg = new RegExp('(^|[ \s])'+sClass+'($|[ \s])'),
            //\n	匹配一个换行符
            //\r	匹配一个回车符
            //\t	匹配一个制表符
            //\f	查找换页符
            //\s与\n\r\t\f是等效果的,但是他不包括空格,所以需要加上空格
            arr = [];
            for(var i=0; i<aEle.length; i++){
                if(reg.test(aEle[i].className)){
                    //className是"myDic myDiv"这种形式
                    arr.push(aEle[i]);
                }
            }
            return arr;
        }

很多地方都能用到正则,正则帮助我们简化代码。

如果我们想获得url的某个参数的值,采用正则会非常简单:

        function getURL(name){
            let search = window.location.search,
            reg = new RegExp("[^&?]*"+name+"=([^&]*)","g"),//name前面0~无数个不为&或?的字符,后面0~无数个不为&的字符
            result = search.substring(1).match(reg);
            return result;
        }

如果我们使用split先根据&分再根据=分会很麻烦:

        function getParam(){
            //window.location.search是包括?的
            var search = window.location.search.substring(1);
            var arr = search.split("&");
            var obj = {};
            for(var i=0; i<arr.length; i++){
                obj[arr[i].split("=")[0]]=arr[i].split("=")[1];
            }
            return obj;
        };


7.实现一个去除字符串前后空格trim的函数

       function trim(str){
            //判断这个字符串的类型是否为string
            if(str&&typeof(str)==='string'){
                return str.replace(/^(\s)|(\s)$/g,"");
            }
        }

8.降维数组

我想的是使用扩展字符串...

        function Jw(obj){
            return [].concat(...obj);
        }

看到一个答案是使用Array.prototype.concat.apply,和我这个大同小异,只是一定要注意apply从第二个参数是作为参数的数组,数组中的每一项都是前面函数(concat)的参数。

        function Jw(obj){
            return Array.prototype.concat.apply([],obj);
        }

9.自定义一个bind函数

思想是先拿到此时的this,最后要返回一个函数,这个函数里要返回“改变this”

        Function.prototype.myBind = function(){
            var self = this;
            var content = Array.prototype.slice.call(arguments)[0];
            var args = Array.prototype.slice.call(arguments,1);
            return function(){
                return self.apply(content,args);
            }
        };
        var obj = {
            name:'Ann',
            getName: function(){
                return this.name;
            }
        };
        console.log(obj.getName());//Ann
        var getName2 = obj.getName;
        console.log(getName2());//空
        var getName3 = obj.getName.myBind(obj);
        console.log(getName3());//Ann

10.实现已经排序好的两个数组的排序合并

我犯了一个错误(i<a.length&&j<b.length)导致如果两个数组个数不一样就会扫不完长的那个。一定要扫完所有,所以需要判断:如果a已经扫完需要将b送进去,又或者a大于b而b也确实没扫完,此时也将b送进去。

       function mergeSortedArray(a,b){
            var arr = [],i=0,j=0;
            if(!a.length) arr=b;
            if(!b.length) arr=a;
            while(i<a.length||j<b.length){
                if(a[i]>b[j]&&j!==b.length||i===a.length){
                    arr.push(b[j]);
                    j++;
                }else{
                    arr.push(a[i]);
                    i++;
                }
            }
            return arr;
        }

11.实现单词翻转和字符翻转

单词只需要翻转一遍,通过空格将他们拆成一个一个数组:

        function reverseStr(str){
            var arr = str.split(" ");
            return arr.reverse().join(" ");
        }

字符反转需要翻转两遍,首先单词翻转,再整个句子翻转,实现内部字符翻转:

        function reverseWord(str){
            var arr = str.split(" ");
            return arr.reverse().join(" ").split("").reverse().join("");//反转两次
        }

12.查找这个数组中是否有两个数的和满足给定值

重点是扫到的每个数,将每个数与给定值的差值存入对象作为属性,接下来检测这个数是否是对象中的属性。

       function twoSum(arr,sum){
            var obj = {};
            for(var i=0; i<arr.length; i++){
                var n = sum-arr[i];
                if(!obj[n]){
                    obj[arr[i]]=true;
                }else{
                    return true;
                }
            }
            return false;
        }

13.实现js的五种基本类型(String,Number,Boolean,Array,Object)的值的复制

function copy(a){
    if(a instanceof Object){
        var obj = a instanceof Array? []:{};
        for(var i in a){
            obj[i] = copy(a[i]);
        }
        return obj;
    }else{
        return a;
    }
}

重点是验证的过程:

var b = {
    c:[12,3],
    d:{
        success:"success",
        bad: {
            "error":"error"
        }
    }
}
var bb = copy(b);
bb.t = 3;
console.log(b);

根据结果我们发现b上并未添加属性t,也就是bb与b是两块不同的对象。这就有区别于浅拷贝。

var cc = Object.assign(b);//浅拷贝
cc.t = 4;
console.log(b);//b上可以发现属性t的值为4

14.二维数组全排列

思路:将内部第一个数组扫进去形成三个数组['A','B','C']。将第二个数组中的每一个元素都分别排在他们后面形成3*3=9种方式,以此类推。

function getArrayByArrays(arrays){  
    var arr = [""];
    for(let i=0; i<arrays.length; i++){
        arr = getValuesByArray(arr, arrays[i]);
    }
    return arr;
}  

function getValuesByArray(arr1,arr2){ 
    var a = [];
    for(let i=0; i<arr1.length; i++){
        for(let j=0; j<arr2.length; j++){
            a.push(arr1[i]+arr2[j]);
        }
    }
    return a;
} 
var arr=[['A','B','C'],['A1','B1','C1'],['A2','B2']];
var array = getArrayByArrays(arr);  
console.log(array);

15.青蛙跳台阶(递归、尾递归、generator)

思路:n=100时递归会造成内存溢出,所以采用尾递归、generator的方式。尾递归每次记录上一次的结果,generator也类似,只不过使用for循环进行one、two的更新。

//递归
function step(n){
    if(n<=1) return n;
    return step(n-1)+step(n-2);
}
//尾递归
function step1(n, one, two){
    if(n<=1) return two;
    return step1(n-1, two, one+two);
}
//generator
function* step2(n){
    var one = 0, two = 1;
    for(var i=0; i<n; i++){
        [one, two] = [two, one+two];
        yield one;
    }
}
console.log(step(5));
console.log(step1(5, 0, 1));
var gen = step2(5);
for(let i of gen){
    console.log(i);
}

generator可以采用for/of循环代替调用next~

猜你喜欢

转载自blog.csdn.net/weixin_40322503/article/details/79980917