『前端积累』算法汇总【持续更新】

简单

1.两数之和(数组)

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

解题思路:

  • 用 hashMap 存储遍历过的元素和对应的索引。
  • 每遍历一个元素,看看 hashMap 中是否存在满足要求的目标数字。
  • 所有事情在一次遍历中完成(用了空间换取时间)

代码:

  • 方式一:
**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    var preNumObj = {}
    for(let i = 0, j = nums.length; i < j; i++) {
        let targetNum = target - nums[i];
        if(preNumObj[targetNum] !== undefined) {
            return [preNumObj[targetNum],i]
        } else {
            preNumObj[nums[i]] = i;
        }
    }
};
  • 方式二:使用Map()
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    let len = nums.length;
    // 创建 MAP
    const MAP = new Map();
    // 由于第一个元素在它之前一定没有元素与之匹配,所以先存入哈希表
    MAP.set(nums[0], 0);
    for (let i = 1; i < len; i++) {
        // 提取共用
        let other = target - nums[i];
        // 判断是否符合条件,返回对应的下标
        if (MAP.get(other) !== undefined) return [MAP.get(other), i];
        // 不符合的存入hash表
        MAP.set(nums[i], i)
    }
};

2.找出数组重复次数最多的元素(数组)

描述:

给定一个字符串数组, 每一个元素代表一个 IP 地址,找到出现频率最高的 IP。

注:给定数据只有一个频率最高的 IP

样例:

const lines = ['192.168.1.1', '192.118.2.1', '192.168.1.1'];
return '192.168.1.1';

题目分析: 用对象来处理,将元素赋值到属性上,判断之前有没有这个属性。

代码

const lines = ['192.168.1.1', '192.118.2.1', '192.168.1.1'];

const mostTimes = function(arr) {
  let [obj, max, name] = [{},1,''];
  arr.forEach((item) => {
    if(obj[item]) {
      obj[item] ++;
      if(obj[item] > max) {
        max = obj[item];
        name = item;
      }
    } else {
      obj[item] = 1; // 没有值,初始化
    }
  })
  return name;
}
console.log(mostTimes(lines)); // 192.168.1.1

3.水仙花数

水仙花数的定义: 一个 N 位非负整数,其各位数字的 N 次方和等于该数本身。

举例:

153 = 1^3 + 5^3 + 3^3
370 = 3^3 + 7^3 + 0^3
371 = 3^3 + 7^3 + 1^3
1634 = 14^4 + 64^4 + 34^4 + 44^4等等。

描述: 给出n,找到所有的n位十进制水仙花数。

样例:

比如 n = 1, 所有水仙花数为:[0,1,2,3,4,5,6,7,8,9]。

而对于 n = 2, 则没有 2 位的水仙花数,返回 []。

判断一个数是否为水仙花数: 要找出水仙花数,首先我们需要能识别一个数是否为水仙花数

// 判断一个数是否为水仙花数
const isTrue = num => {
  const len = num.toString().length; // 数的长度
  const str = num.toString(); // 数转成字符串
  let total = 0;
  for(let i = 0; i < len; i++) {
    total += Math.pow(str.charAt(i),len);
  }
  if(total == num) {
    return true;
  }
  return false;
}

console.log(isTrue(153)) // true
console.log(isTrue(154)) // false

找出所有的n位十进制水仙花数

  • 确定查找的范围(找出n位的最大值与最小值)
  • 遍历每个数,判断为水仙花数,添加到数组中
const getNumbersArr = n => {
  let resultArr = []; // 满足水仙花数的结果数组
  let min = Math.pow(10, n - 1);
  let max = Math.pow(10, n);

  for(let i = min; i < max; i++) {
    const str = i.toString();
    let total = 0;
    let j = 0;
    while(j < n) {
      total += Math.pow(str.charAt(j),n);
      j++;
    }
    if(total == i) {
      resultArr.push(i);
    }
  }
  return resultArr;
}
console.log(getNumbersArr(1)) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(getNumbersArr(2)) // []
console.log(getNumbersArr(3)) // [153, 370, 371, 407]

注意:查找位数过大会出现性能问题,以及最大值溢出问题。

4.姓名去重(数组)

描述: 给一串名字,将他们去重之后返回。两个名字重复是说在忽略大小写的情况下是一样的。

说明: 你可以假设名字只包含大小写字母和空格。

样例, 给出:

[
  'James',
  'james',
  'Bill Gates',
  'bill Gates',
  'Hello World',
  'HELLO WORLD',
  'Helloworld'
];

返回:

['james', 'bill gates', 'hello world', 'helloworld'];

题目分析: 先全部转换为小写再去重。

代码:

const  removeDuplicateName = nameArr => {
  nameArr.forEach((item,index) => {
    nameArr[index] = item.toLowerCase();
  })
  return [...new Set(nameArr)].sort();
}

const arr = [
  'James',
  'james',
  'Bill Gates',
  'bill Gates',
  'Hello World',
  'HELLO WORLD',
  'Helloworld'
];

console.log(removeDuplicateName(arr)); // ["bill gates", "hello world", "helloworld", "james"]

5.反转一个3位整数

描述: 反转一个只有 3 位数的整数

样例: 123 反转之后是 321。 900 反转之后是 9

代码:

const reverseNumber = num => parseInt(`${num}`.split('').reverse().join(''));

console.log(reverseNumber(123)) // 321
console.log(reverseNumber(900)) // 9

6.反转数字(包含小数、正数、负数)

描述: 将一个数进行反转,得到反转后的值

样例: 123反转后为321;1.23反转后为32.1;-500反转后为-5;

代码:

const reverseNumber = num => parseFloat(`${num}`.split('').reverse().join('')) * Math.sign(num);

console.log(reverseNumber(123)) // 321
console.log(reverseNumber(-123)) // -321
console.log(reverseNumber(1.23)) // 32.1
console.log(reverseNumber(-500)) // -5

7.查找斐波纳契数列中第 N 个数

描述: 所谓的斐波纳契数列是指:

  • 前 2 个数是 0 和 1;
  • 第 i 个数是第 i-1 个数和第 i-2 个数的和。

斐波纳契数列的前 10 个数字是:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34

题目分析: 前两个数字可以算成是起始元素,从第三个元素才开始有规则

代码方式一:递归

const fibonacci = num => {
	if(!(typeof num === 'number' && num > 0 && num % 1 === 0)) {
		throw '请传入大于0的整数数字'
	}
	
	let arr = [0,1];
	let temp = n => {
		if(n == 1 || n == 2) {
			return arr[n - 1];
		}
		arr[n - 1] = temp(n - 1) + temp(n - 2);
		return arr[n - 1];
	}
	return temp(num);
}

console.log(fibonacci(1)) // 0
console.log(fibonacci(2)) // 1
console.log(fibonacci(10)) // 34

代码方式二:循环

const fibonacci = num => {
	if(!(typeof num === 'number' && num > 0 && num % 1 === 0)) {
		throw '请传入大于0的整数数字'
	}
	
	let arr = [0,1]; // 把前两位数字当起始元素
	if(num == 1 || num == 2) {
		return arr[num - 1];
	}

	for(let i = 3; i < num + 1; i++ ) {
		arr[i - 1] = arr[i - 2] + arr[i - 3];
	}
	return arr[arr.length - 1];
}

console.log(fibonacci(1)) // 0
console.log(fibonacci(2)) // 1
console.log(fibonacci(10)) // 34

代码方式三:循环优化

const fibonacci = num => {
	if(!(typeof num === 'number' && num > 0 && num % 1 === 0)) {
		throw '请传入大于0的整数数字'
	}
	
	let arr = new Array(num).fill(0); // 初始化默认填充值
	arr[1] = 1; // 默认设置第二位的值
	for(let i = 2; i < num; i++) {
		arr[i] = arr[i - 1] + arr[i - 2];
	}
	return arr[num - 1];
}

console.log(fibonacci(1)) // 0
console.log(fibonacci(2)) // 1
console.log(fibonacci(10)) // 34

8.合并排序数组

描述: 合并两个排序的整数数组 A 和 B 变成一个新的排序数组

样例: 给出A=[1,2,3,4]B=[2,4,5,6],返回 [1,2,2,3,4,4,5,6]

使用sort排序:

const mergeSortedArray = (arrA,arrB) => arrA.concat(arrB).sort((a,b) => a - b);

let a = [1,2,3,4];
let b = [2,4,5,6];
console.log(mergeSortedArray(a,b)); 
// 结果: [1, 2, 2, 3, 4, 4, 5, 6]

使用for循环:

const mergeSortedArray = (arrA,arrB) => {
	let i;
	let j;
	let arr = [];
	
	for(i = 0, j = 0; i < arrA.length && j < arrB.length;) {
		if(arrA[i] < arrB[j]) {
			arr.push(arrA[i++]);
		} else {
			arr.push(arrB[j++])
		}
	}
	while(i < arrA.length) {
		arr.push(arrA[i++]);
	}
	while(j < arrB.length) {
		arr.push(arrB[j++])
	}
	return arr;
}

let a = [1,2,3,4];
let b = [2,4,5,6];
console.log(mergeSortedArray(a,b)); 
// 结果: [1, 2, 2, 3, 4, 4, 5, 6]

9.分解质因数

质因数的定义: 能整除给定正整数的质数。

描述:

  • 将一个整数分解为若干质因数之乘积
  • 你需要从小到大排列质因子

样例:

给出 10, 返回 [2, 5]
给出 660, 返回 [2, 2, 3, 5, 11]

题目分析:

从小到大排列质因子,需要将同一个质因子整除干净。

比如:20 可以被 2 整除两次。

代码:

const primeFactorization = (num) => {
	let arr = [];
	for(let i = 2; i * i <= num; i++) {
		while(num % i === 0) { // 发现质数
			num = num / i;
			arr.push(i);
		}
	}
	if(num !== 1) arr.push(num);
	return arr;
}
console.log(primeFactorization(27)); // [3, 3, 3]
console.log(primeFactorization(29)); // [29]
console.log(primeFactorization(20)); // [2, 2, 5]

10.删除一个字母的回文

描述:

  • 给定非空字符串 s,您最多可以删除一个字符。判断是否可以成为回文。
  • 该字符串仅包含小写字符 a-z,字符串的最大长度为 50000。

说明:“回文”的意思是“正读和反读都一样”,既要读之自然,更要顺理成章。“上海自来水来自海上”堪称一回文佳句。

样例:

Given s = "aba" return true
Given s = "abca" return true // delete c

题目分析:

如果单单是回文的话,就很简单了:

s === [...s].reverse().join(''); // 翻转字符串与原字符相比

题目中还有一个要求:删除一个字符,也就是允许一个字符的不同

下面我们的解法主体思路就是通过循环,从两侧向中间比较,然后解决当出现不同的情况,如何保证只允许出现一个不同

代码:

1.出现一处不同 将值传入一个新函数,再进行判断字符串:

const valid = s => {
	let left = 0;
	let right = s.length - 1;
	
	while(left < right) {
		if(s[left] !== s[right]) {
			return (innerVaild(s,left + 1,right) || innerVaild(s,left,right - 1))
		}
		left++;
		right--;
	}
	return true;
}

const innerVaild = (s,left,right) => {
	while(left < right) {
		if(s[left] !== s[right]) {
			return false; // 去掉一个字符后,还是不同直接返回false
		}
		left++;
		right--;
	}
	return true;
}

console.log(
  '回文验证:',
  valid('abaacaaa'), 
  valid('ab'),
  valid('abc'),
  valid('aabsjdbaa')
);
// 结果:回文验证: true true false false

上面的代码可以看出,编写了两个函数,思考能不能只用一个方法?

2.递归方法:

const valid = (s, left = 0, right = s.length - 1, type = 'init') => {
	if(type == 'init') {
		while(left < right) {
			if(s[left] !== s[right]) {
				return (valid(s,left + 1,right,'again') || valid(s,left,right +1,'again'));
			}
			left++;
			right--;
		}
		return true;
	} else {
		while(left < right) {
			if(s[left] !== s[right]) {
				return false;
			}
			left++;
			right--;
		}
		return true;
	}
}


console.log(
  '回文验证:',
  valid('abaacaaa'),
  valid('ab'),
  valid('abc'),
  valid('aabsjdbaa')
);
// 结果:回文验证: true true false false

能不能通过设置变量来处理?出现两次不同即失败。

3.根据出现两次不同即不为回文

const valid = s => {
	let state = false;
	for(let [i,j] = [0,s.length - 1]; i < j; i++, j--) {
		if(s[i] !== s[j]) { 
			if(state) { // 第二次字符不同,直接返回false
				return false;
			}
			if(s[i] === s[j - 1]) {
				j--;
				state = true;
			} else if(s[i + 1] === s[j]) {
				i++;
				state = true;
			} else {
				return false; 
			}
		}
	}
	return true;
}

console.log(
  '回文验证:',
  valid('abaacaaa'),
  valid('ab'),
  valid('abc'),
  valid('aabsjdbaa')
);
// 结果:回文验证: true true false false

11.最大子数组

描述: 给定一个整数数组,找到一个具有最大和的子数组,返回其最大和。

样例: 给出数组[-2,2,-3,4,-1,2,1,-5,3],符合要求的子数组为[4, -1,2,1],其最大和为 6

思路分析: 只要找出最大和即可,保存两个值,一个为元素之间相加的值(需比较元素相加的值与当前元素的大小),一个为最大值。

代码:

const maxSubArray = arr => {
	let max = arr[0]; // 初始化最大值
	let newMax = arr[0]; // 数组元素相加的缓存值
	
	for(let i = 1 ; i < arr.length; i++) {
		newMax = Math.max(newMax + arr[i], arr[i]); // 相加是否大于当前值
		max = Math.max(newMax,max); // 与最大值相比较
	}
	return max;
};
console.log(maxSubArray([-2,2,-3,4,-1,2,1,-5,3])) // 6

方式二:

const maxSubArray = arr => {
	let max = arr[0]; // 初始化最大值
	let newMax = arr[0]; // 数组元素相加的缓存值
	
	for(let i = 1; i < arr.length; i++) {
		let curNum = arr[i];
		if(curNum >= 0) {
			newMax = newMax > 0 ? newMax + curNum : curNum;
		} else {
			newMax = newMax < curNum ? curNum : newMax + curNum;
		}
		max = Math.max(newMax,max);
	}
	return max;
}
console.log(maxSubArray([-2,2,-3,4,-1,2,1,-5,3])) // 6

保存最大和的子数组:

const maxSubArray = arr => {
	const max = { 
		num: arr[0],
		start: 0,
		end: 1
	}
	const newMax = { 
		num: arr[0],
		start: 0,
		end: 1
	}
	
	for(let i = 1; i < arr.length; i++) {
		let curNum = arr[i];
		if(newMax.num + curNum > curNum) {
			newMax.num = newMax.num + curNum;
			newMax.end = i + 1;
		} else {
			newMax.num = curNum;
			newMax.start = i;
			newMax.end = i + 1;
		}
		
		// 最大值被超过,重新赋值最大值
		if(max.num < newMax.num) {
			max.num = newMax.num;
			max.start = newMax.start;
			max.end = newMax.end;
		}
	}
	
	let resutArr = arr.slice(max.start,max.end);
	return {
		arr: resutArr,
		max: max.num
	}
}

let result = maxSubArray([-2,2,-3,4,-1,2,1,-5,3]);
console.log(result.arr); // [4, -1, 2, 1]
console.log(result.max); // 6
posted @ 2021-07-29 21:30  风雨后见彩虹  阅读(176)  评论(0编辑  收藏  举报