1、数组去重
1)ES6 的 Set
此时,有些面试官会刨根问底,为什么es6的Set()这个就可以去重?
回答:因为Set是类数组对象类型的,它的特性就决定它内部都是不重复的数据
此时,如果面试官还继续问,Set()里面是怎么进行重复数据去重的?
回答:
a、set() 函数中会先调用对象的__hash__()方法,获取 hash 结果;
b、如果 hash 结果相同,用比较操作符==(也就是调用函数__eq__())判断二者的值是否相等;
c、如果都相等,去重;否则,set() 认为二者不同,两个都保留到结果中
let arr = [1,1,2,3,4,5,5,6]
let arr2 = [...new Set(arr)]
// 类数组对象
let obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3
}
2)reduce()
let arr = [1,1,2,3,4,5,5,6];
let arr2 = arr.reduce(function(ar,cur) {
if(!ar.includes(cur)) {
ar.push(cur)
}
return ar
},[])
3)filter + indexOf
let arr = [1,1,2,3,4,5,5,6];
let arr2 = arr.filter(function(item,index) {
// indexOf() 方法可返回某个指定的 字符串值 在字符串中首次出现的位置
return arr.indexOf(item) === index
})
4)利用对象key值不能相同的特点
let arr = [1,1,2,3,4,5,5,6];
let tempObj = {};
let arr2 = [];
arr.forEach(elemnt => {
if(tempObj[elemnt] === undefined){
tempObj[elemnt] = 1;
arr2.push(elemnt);
}
});
2、计算1~n的和
function fn(n) {
if(n === 1){
return 1;
}
return n + fn(n-1);
}
console.log(fn(100));
3、判断回文
思路:利用现成的函数split(),将字符串转换成数组,然后利用reverse反转数组,再利用join合成字符串跟原来的字符串进行比较
function checkPalindrom(str) {
return str == str.split('').reverse().join('');
}
4、冒泡排序以及它的优化
思路:
步骤1: 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
步骤2: 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
步骤3: 针对所有的元素重复以上的步骤,除了最后一个;
步骤4: 重复步骤1~3,直到排序完成。
普通冒泡排序:
// 比较次数为n(n-1)/2
function bubbleSort(arr) {
if(arr.length <= 1){
return arr;
}
for(let i = 0,l=arr.length;i<l-1;i++) {
for(let j = i+1;j<l;j++) {
if(arr[i]>arr[j]) {
[arr[i], arr[j]] = [arr[j], arr[i]]; // es6的解构赋值
}
}
}
return arr;
}
第一次优化:
里面一层循环在某次扫描中没有发生交换,说明此时数组已经全部排好序,后面的步骤就无需再执行了。因此,增加一个标记,每次发生交换,就标记,如果某次循环完没有标记,则说明已经完成排序
function bubbleSort(arr) {
if(arr.length <= 1){
return arr;
}
let bSwaped = true;
for (let i = 0; i < arr.length - 1; i++) {
// 每次先重置为false
bSwaped = false;
for (let j = arr.length - 1; j > i ; j--) {
if (arr[j-1] > arr[j]) {
[arr[j-1], arr[j]] = [arr[j], arr[j-1]];
bSwaped = true;
}
}
// 如果上一次扫描没有发生交换,则说明数组已经全部有序,退出循环
if (!bSwaped){
break;
}
}
return arr;
}
第二次优化
在上一步优化的基础上进一步思考:如果R[0…i]已是有序区间,上次的扫描区间是R[i…n],记上次扫描时最后一次发生交换的位置是lastSwapPos,那么lastSwapPos会在在i与n之间,所以R[i…lastSwapPos]区间是有序的,否则这个区间也会发生交换;所以下次扫描区间就可以由R[i…n]缩减到[lastSwapPos…n]
var len = arr.length;
if(arr.length <= 1){
return arr;
}
let lastSwapPos = 0, lastSwapPosTemp = 0, bSwaped = true;
for (let i = 0; i < arr.length - 1; i++){
lastSwapPos = lastSwapPosTemp;
for (let j = arr.length - 1; j > lastSwapPos; j--){
if (arr[j - 1] > arr[j]){
[arr[j-1], arr[j]] = [arr[j], arr[j-1]];
lastSwapPosTemp = j;
bSwaped = true;
}
}
if (lastSwapPos == lastSwapPosTemp){
break;
}
if (!bSwaped){
break;
}
}
return arr;
5、斐波那契数列
含义:又称黄金分割数列,主要考察递归的调用
求其中第n个的数值
思路:当n为0,1时,返回0,1否则就是前两位数相加
function fib(n){
if(n < 2) {
return n;
}
return fib(n-1)+fib(n-2);
}
console.log(fib(10));
有些面试官是不会止步在这块的,会有这样的需求,我需要这个斐波那契算法带有缓存机制!!
let fibonacci = function() {
let temp = [0, 1];
return function(n) {
let result = temp[n];
if(typeof result != 'number') {
result = fibonacci(n - 1) + fibonacci(n - 2);
temp[n] = result; // 将每次 fibonacci(n) 的值都缓存下来
}
return result;
}
}(); // 外层立即执行
6、快速排序
思路:
- 在数组中,找一个基准点P,然后splice出去
- 比基准小的放到左边的数组,比基准大的放到右边的数组
- 按此方法对这两部分数据递归操作
- 直到不能再分后退出递归
- 将数组concat起来,并返回
function quickSort(arr) {
// 当数组元素<=1时,没有必要排序直接返回
if(arr.length<=1){
return arr;
}
let left=[], right=[];
// 找准基准点,并将基准点从原数组中删除
let basis = arr.splice(0,1);
arr.forEach((v) => {
if(v < basis){
left.push(v);
}else{
right.push(v);
}
});
return quickSort(left).concat(basis,quickSort(right));
}
7、二分查找
含义:是一种在有序数组中查找特定元素的搜索算法
思路:
从有序数组的中间的元素开始搜索,如果该元素正好是目标元素(即要查找的元素),则搜索过程结束,否则进行下一步
如果目标元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半区域查找,然后重复第一步的操作
如果某一步数组为空,则表示找不到目标元素
// 递归算法
function binary_search(arr,low, high, key) {
if (low > high){
return -1;
}
var mid = parseInt((high + low) / 2);
if(arr[mid] == key){
return mid;
}else if (arr[mid] > key){
high = mid - 1;
return binary_search(arr, low, high, key);
}else if (arr[mid] < key){
low = mid + 1;
return binary_search(arr, low, high, key);
}
};
var arr = [1,2,3,4,5,6,7,8,9,10,11,23,44,86];
var result = binary_search(arr, 0, arr.length - 1, 10);
alert(result); // 9 返回目标元素的索引值