【灵活应对前端面试中的JS算法题】
- 实现一个函数clone,可以对JavaScript中的5种主要的数据类型(包括Number、String、Object、Array、Boolean)进行值复制
function clone(obj){
var result;
switch(typeof obj){
case 'undefined':
break;
case 'string':
result = obj+'';
break;
case 'number':
result = obj-0;
break;
case 'boolean':
result =obj;
break;
case 'object':
if(obj ===null){
result = null;
} else {
if(Object.prototype.toString.call(obj).slice(8,-1)==='Array'){
result=[];
for(var i=0;i<obj.length;i++){
result.push(clone(obj[i]));
}
}else{
result=[];
for(var k in obj){
result[k]=clone(obj[k]);
}
}
};
break;
default:
result = obj;
break;
}
return result
}
- 判断一个单词是否是回文
回文是指把相同的词汇或句子,在下文中调换位置或颠倒过来,产生首尾回环的情趣,叫做回文,也叫回环。
很多人拿到这样的题目非常容易想到用for将字符串颠倒字母顺序然后匹配就行了。其实重要的考察的是对于reverse的实现。其实我们可以利用现成的函数,将字符串转换成数组,这个思路很重要,我们可以拥有更多的自由度去进行字符串的一些操作。
let reverseStr = function(str) {
return str = str.split('').reverse().join('');
}
reverseStr('abcdefg');
//gfedcba
- 在句子中反转词
如fix this one 变为 one this fix。重点是检测到空格时进行处理。
function reverseWord(str){
return str.split(' ').reverse().join(' ')
}
- 反转每个单词中字符的顺序
“I am the good boy” 反转成这样 “I ma eht doog yob”
function reverse(str){
return str.split(' ').reverse().join(' ').split('').reverse().join('');
}
-
去掉一组整型数组重复的值
题目如下输入: [3,13,24,11,11,14,1,2]
输出: [3,13,24,11,14,2]
需要去掉重复的11 和 1 这两个元素。
这道题有多重方法,我理解的主要是考察个人对Object的使用,利用key来进行筛选。
function unique(arr) {
let hashTable = {};
let data = [];
for(let i=0,l=arr.length;i<l;i++) {
if(!hashTable[arr[i]]) {
hashTable[arr[i]] = true;
data.push(arr[i]);
}
}
return data
}
unique([3,13,24,11,11,14,1,2])
//[3,13,24,11,14,2]
再来一个其他实现方式,这个方法常在我的项目中出现,用的时候确实觉得代码少了那么几行
function unique(arr) {
let data = [];
for(let i=0,l=arr.length;i<l;i++) {
if(data.indexOf(arr[i])==-1) {
data.push(arr[i]);
}
}
return data
}
unique([3,13,24,11,11,14,1,2])
//[3,13,24,11,14,2]
- 统计一个字符串出现最多的字母
输入一段英文连续的英文字符串 afjghdfraaaasdenas,找出重复出现次数最多的字母
function findMaxChar(str) {
if(str.length == 1) {
return str;
}
let charObj = {};
for(let i=0;i<str.length;i++) {
if(!charObj[str.charAt(i)]) {
charObj[str.charAt(i)] = 1;
}else{
charObj[str.charAt(i)] += 1;
}
}
return compare(charObj);
};
function compare(charObj){
let maxChar = '',
maxValue = 1;
for(var k in charObj) {
if(charObj[k] >= maxValue) {
maxChar = k;
maxValue = charObj[k];
}
}
return maxChar;
}
findMaxChar('afjghdfraaaasdenas')
//a
- 找到字符串中的第一个非重复的字符
遍历字符串,用一个对象当做hash表来存储重复字符。
function firstNonRepeatChar(str){
var count = {};
for(var i=0;i<str.length;i++){
if(count[str[i]]){
count[str[i]]++;
}else{
count[str[i]] = 1;
}
}
for(var prop in count){
if(count[prop] === 1){
return prop;
}
}
}
- 删除字符串中重复的字符
其实是在上一个问题的基础上再进行操作:
function firstNonRepeatChar(str){
var count = {};
var result = [];
for(var i=0;i<str.length;i++){
if(count[str[i]]){
count[str[i]]++;
}else{
count[str[i]] = 1;
}
}
for(var prop in count){
if(count[prop] === 1){
result.push(prop);
}
}
return result.join('');
}
- 在n和m之间生成随机整数
Math.floor(Math.random()*(m-n))+n
- 排序算法(冒泡排序)
冒泡排序JavaScript代码实现:
function bubbleSort(arr) {
var len = arr.length;
for (var i = 0; i < len; i++) {
for (var j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j+1]) { //相邻元素两两对比
var temp = arr[j+1]; //元素交换
arr[j+1] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
bubbleSort([3,5,2,8,9,7,6])
//[2, 3, 5, 6, 7, 8, 9]
- 排序算法(选择排序)
在时间复杂度上表现最稳定的排序算法之一,因为无论什么数据进去都是O(n²)的时间复杂度。。。所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。
function selectionSort(arr) {
var len = arr.length;
var minIndex, temp;
for (var i = 0; i < len - 1; i++) {
minIndex = i;
for (var 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;
}
selectionSort([3,5,2,8,9,7,6])
//[2, 3, 5, 6, 7, 8, 9]
- 从未排序的整数数组中找出缺失的数字
比如你有1到100的整数,而其中缺了一个,怎么找出这个数字?利用等差数列公式计算这些数应得的和,再计算当前数组所有数字的和,二者的差即为缺少的数。
function missingNumber(arr){
var n = arr.length+1;
var expectedSum = (1+n)*n/2;
var sum = 0;
for(var i=0;i<arr.length;i++){
sum+=arr[i];
}
return expectedSum - sum;
}
- 检查是否有任何两个数字的和是给定的数字
最暴力的方法就是两层循环,是O(n2).改进方法使用一个对象作为哈希表,用于存储数,这样在每次搜寻是否有另一个数与当前数的和为sum时就可以在O(1)的时间内找到。
function twoSum(arr,sum){
var obj = {};
var num;
for(var i=0;i<arr.length;i++){
num = sum - arr[i];
if(obj[num]){
return true;
}else{
//若没有的话为当前数字建立索引
obj[arr[i]] = true;
}
}
return false;
}
- 检查是否有任何两个数字的和是给定的数字,有的话将数字和位置以对象的方式返回值,否则返回false
最暴力的方法就是两层循环,是O(n2).改进方法使用一个对象作为哈希表,用于存储数,这样在每次搜寻是否有另一个数与当前数的和为sum时就可以在O(1)的时间内找到。
function twoSum(arr,sum){
var obj = {};
var num;
for(var i=0;i<arr.length;i++){
num = sum - arr[i];
obj[num]=arr.length+1;
if(obj[num]){
obj[arr[i]]=i;
obj[num]=arr.indexOf(num);
return obj;
}else{
//若没有的话为当前数字建立索引
obj[arr[i]] = i;
}
}
return false;
}
- 找到任意两个数字的最大和
找到两个最大的数并返回它们的和。
function topSum(arr){
if(arr.length<2) return null;
var first,second;
if(arr[0]>arr[1]){
first = arr[0];
second=arr[1];
}else{
first = arr[1];
second=arr[0];
}
for(var i=2;i<arr.length;i++){
if(arr[i]>first){
second = first;
first = arr[i];
}else if(arr[i]>second){
second = arr[i];
}
}
return first+second;
}
- 从1到n中0的总个数
n=50的话,有5个0,分别是10,20,30,40,50。
n = 120的话,分别是10到90,共九个,110到120,共2个,以及100的两个,一共13个。
也就是说10的整数次方会有多个零,如100,1000,那么就要利用现有的数计算包含了多少个10的平方数。
如2014,2014/10=201; 201/10 = 20; 20/10 = 2; 最后表明出现了两次10的三次方,即1000和2000。
function countZero(n){
var count = 0;
while(n>0){
count+=Math.floor(n/10);
n/=10;
}
return count;
}
- 匹配字符串的子字符串
function substr(str,subStr){
for(var i=0;i<str.length;i++){
for(var j=0;j<subStr.length;j++){
if(str[i+j] != subStr[j]){
break;
}
}
if(j == subStr.length){
return i;
}
}
return -1;
}
- 字符串的全排列
var result = [];
function permutations(str){
var arr= str.split('');
helper(arr,0,[]);
return result;
}
function helper(arr,index,list){
if(index === arr.length){
result.push(list.join(''));
return;
}
for(var i = 0;i<arr.length;i++){
if(list.indexOf(arr[i]) != -1){
continue;
}
list.push(arr[i]);
helper(arr,index+1,list)
list.pop();
}
}
- 不借助临时变量,进行两个整数的交换
把 a = 2, b = 4 变成 a = 4, b =2
这种问题非常巧妙,需要大家跳出惯有的思维,利用 a , b进行置换
主要是利用 + – 去进行运算,类似 a = a + ( b – a) 实际上等同于最后 的 a = b;
function swap(a , b) {
b = b - a;
a = a + b;
b = a - b;
console.log('a='+a);
console.log('b='+b)
}
var a=2,b=4;
swap(a,b)
//a=4;b=2
- 斐波那契数列(黄金分割数列)不说换金分割我也不知道是啥玩意儿啦
斐波那契数列,又称黄金分割数列,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列主要考察递归的调用。
function getFibonacci(n) {
var fibarr = [];
var i = 0;
while(i<n) {
if(i<=1) {
fibarr.push(i);
}else{
fibarr.push(fibarr[i-1] + fibarr[i-2])
}
i++;
}
return fibarr;
}
getFibonacci(9); //拿到9个
//[0、1、1、2、3、5、8、13、21]
- 获得第n个斐波那契数字
解法一:迭代
var fibonacci = function(n){
var fibo = [0,1];
for(var i=2;i<=n;i++){
fibo[i] = fibo[i-1]+fibo[i-2];
}
return fibo[n];
}
解法二:递归
var fibonacci = function(n){
if(n>=2){
return fibonacci(n-1)+fibonacci(n-2);
}else{
return n;
}
}
- 找到两个数的最大公约数
解法一:遍历
var greatestCommonDivisor= function(a,b){
if(a<2 || b<2) return 1;
var divider = 2;
var greatestDivisor = 1;
while(divider<=a && divider<=b){
if(a%divider == 0 && b%divider == 0){
greatestDivisor = divider;
}
divider++;
}
return greatestDivisor;
}
解法二:辗转相除法
又名欧几里德算法(Euclidean algorithm)乃求两个正整数之最大公因子的算法。它是已知最古老的算法……
有解释的,但我选择不去理解……
function greatestCommonDivisor(a, b){
if(b == 0)
return a;
else
return greatestCommonDivisor(b, a%b);
}
- 合并两个排序数组
var mergeSortedArray = function(a,b){
var merge = [];
var i = 0,j = 0;
var k = 0;
while(i<a.length || j<b.length){
if(i == a.length || (j!=b.length && a[i]>b[j])){
merge[k++] = b[j++];
}else{
merge[k++] = a[i++];
}
}
return merge;
}
- 验证一个数是否是质数
质数只能被1和它自己整除,因此令被除数从2开始,若能整除则不是质数,若不能整除则加一,直到被除数到达根号n,此时n则是质数。
function isPrime(n){
var divider = 2;
var limit = Math.sqrt(n);
while(divider<=limit){
if(n%divider == 0){
return false;
}
divider++;
}
return true;
}
isPrime(3);
//true
- 查找数字的所有质数因子
divider从2开始,如果n能整除divider,则将divider加入到结果中,n为此次计算后的商,如果n不能整除divider,则divider++
var primeFactors = function(n){
var factors = [];
var divider = 2;
while(n>2){
if(n%divider == 0){
factors.push(divider);
n /= divider;
}else{
divider++;
}
}
return factors;
}
- 找出正数组的最大差值比
这是通过一道题目去测试对于基本的数组的最大值和最小值的查找
function getMaxProfit(arr) {
var minPrice = arr[0];
var maxProfit = 0;
for (var i = 0; i < arr.length; i++) {
var currentPrice = arr[i];
minPrice = Math.min(minPrice, currentPrice);
var potentialProfit = currentPrice - minPrice;
maxProfit = Math.max(maxProfit, potentialProfit);
}
return maxProfit;
}
getMaxProfit([10,5,11,7,8,9])
//6
- 随机生成指定长度的字符串
function randomString(n) {
let str = 'abcdefghijklmnopqrstuvwxyz9876543210';
let tmp = '',
i = 0,
l = str.length;
for (i = 0; i < n; i++) {
tmp += str.charAt(Math.floor(Math.random() * l));
}
return tmp;
}
randomString(9); //指定长度为9
//4ldkfg9j7
- 实现类似getElementsByClassName 的功能
自己实现一个函数,查找某个DOM节点下面的包含某个class的所有DOM节点?不允许使用原生提供的 getElementsByClassName querySelectorAll 等原生提供DOM查找函数。
function queryClassName(node, name) {
var starts = '(^|[ \n\r\t\f])',
ends = '([ \n\r\t\f]|$)';
var array = [],
regex = new RegExp(starts + name + ends),
elements = node.getElementsByTagName("*"),
length = elements.length,
i = 0,
element;
while (i < length) {
element = elements[i];
if (regex.test(element.className)) {
array.push(element);
}
i += 1;
}
return array;
}
queryClassName()
- 使用JS 实现二叉查找树
一般叫全部写完的概率比较少,但是重点考察你对它的理解和一些基本特点的实现。 二叉查找树,也称二叉搜索树、有序二叉树(英语:ordered binary tree)是指一棵空树或者具有下列性质的二叉树:
任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 任意节点的左、右子树也分别为二叉查找树;
没有键值相等的节点。二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低。为O(log
n)。二叉查找树是基础性数据结构,用于构建更为抽象的数据结构,如集合、multiset、关联数组等。
class Node {
constructor(data, left, right) {
this.data = data;
this.left = left;
this.right = right;
}
}
class BinarySearchTree {
constructor() {
this.root = null;
}
insert(data) {
let n = new Node(data, null, null);
if (!this.root) {
return this.root = n;
}
let currentNode = this.root;
let parent = null;
while (1) {
parent = currentNode;
if (data < currentNode.data) {
currentNode = currentNode.left;
if (currentNode === null) {
parent.left = n;
break;
}
} else {
currentNode = currentNode.right;
if (currentNode === null) {
parent.right = n;
break;
}
}
}
}
remove(data) {
this.root = this.removeNode(this.root, data)
}
removeNode(node, data) {
if (node == null) {
return null;
}
if (data == node.data) {
// no children node
if (node.left == null && node.right == null) {
return null;
}
if (node.left == null) {
return node.right;
}
if (node.right == null) {
return node.left;
}
let getSmallest = function(node) {
if(node.left === null && node.right == null) {
return node;
}
if(node.left != null) {
return node.left;
}
if(node.right !== null) {
return getSmallest(node.right);
}
}
let temNode = getSmallest(node.right);
node.data = temNode.data;
node.right = this.removeNode(temNode.right,temNode.data);
return node;
} else if (data < node.data) {
node.left = this.removeNode(node.left,data);
return node;
} else {
node.right = this.removeNode(node.right,data);
return node;
}
}
find(data) {
var current = this.root;
while (current != null) {
if (data == current.data) {
break;
}
if (data < current.data) {
current = current.left;
} else {
current = current.right
}
}
return current.data;
}
}
module.exports = BinarySearchTree;
- 统计数组中每个元素及出现的次数,并输出到页面
function getArrayMess(arr) {
if(arr.length == 1) {
console.log("{"+arr[0]+":1")
}
let charObj = {};
for(let i=0;i<arr.length;i++) {
if(!charObj[arr[i]]) {
charObj[arr[i]] = 1;
}else{
charObj[arr[i]] += 1;
}
}
console.log(charObj)
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。