前言
和数据结构一样,考算法题也备受面试官的青睐,很多时候我们可以根据题目的特点,甚至 可以和面试官讨论选择适合的方法编程,通常排序和查找是面试时考查算法的重点,二分查找,归并、快排要能做到随时准确,完整的写出它们的代码。
影响代码规范性的因素:书写、布局、命名
代码的完整性:边界判断一定要加上!
数组篇:
数组的基础知识:
let arr1 = [1, 2, 10, 30]
let arr2 = [3, 4, 50, 90]
let arr3 = ['banana', 'apple', 'peach']
// arr1.concat(arr2) // [ 1, 2, 10, 30, 3, 4, 50, 90 ] 合并数组
// const a = arr1.entries() // Object [Array Iterator] {} 返回一个数组迭代对象,可用for of遍历
// arr1.fill('bob') // [ 'bob', 'bob', 'bob', 'bob' ] 使用一个固定值来填充数组,改变原数组
// const flag = arr1.every(item => { return item >= 1 }) // true 数组内每一个元素都满足>=1返回true,有一个不满足就返回flase,并不再遍历,不改变原数组
// const flag = arr1.some(item => { return item > 11 }) // true 数组内只要有一个元素满足条件就返回true,不再遍历数组,都不满足返回false,不改变原数组
// const newArr = arr1.filter(item => { return item > 6 }) // [ 10, 30 ] 返回满足判断条件的元素组成的数组,不改变原数组
// const target = arr1.find(item => { return item > 99 }) // 10 返回满足条件的第一个元素,找到目标元素便不再遍历,无就返回undefind,不改变原数组
// const targetIndex = arr1.findIndex(item => {return item >= 10}) // 2 返回满足条件的元素的索引,无就返回-1
// const newArr = arr1.forEach(item =>{ if(item===2) return ;console.log('执行')}) // 执行 执行 执行 将数组内的每个元素都执行一次函数,这里的return相当于continue,跳出循环要使用try catch实现,改变元素组,没有返回值
try{
array.forEach(function(item,index){
if(item == "third"){
throw new Error("ending");//报错,就跳出循环
}else{
console.log(item);
}
})
}catch(e){
if(e.message == "ending"){
console.log("结束了") ;
}
}
// const newArr2 = Array.from('HBGSV') // [ 'H', 'B', 'G', 'S', 'V' ]将一个字符串转化为数组
// const newArr = new Set(arr1)
// const newArr2 = Array.from(newArr, x => x * 10) // [ 10, 20, 100, 300 ]将map结构转化为数组结构,第二个参数是一个函数,数组内的每一个数都会执行它
// const flag = arr1.includes(2) // true 看数组内是否包含2这个元素,返回布尔值
// const flag = arr1.indexOf(10) // 2 返回指定元素在数组内的索引位置,无就返回-1
// const flag = Array.isArray(arr1) // true 判断一个对象是否是一个数组
// const string1 = arr1.join() // 1,2,10,30 将一个数组转化为一个字符串,参数不写默认以逗号分隔
// arr1.keys() // Object [Array Iterator] {}返回一个实现Iterator接口的对象,需要调用.next() // { value: 0, done: false } .value: // 0
// const index = arr1.lastIndexOf(10) // 2 返回目标元素在数组里出现的最后一个位置
// const newArr = arr1.map(item => { return item * 2 }) // [ 2, 4, 20, 60 ] 返回处理过的数组,不改变原数组
// const popRes = arr1.pop() // 30 删除数组最后一个元素,并返回被删除的元素
// const newArrLength = arr1.push(1,2) // 6 向数组的末尾添加元素,并返回新数组的长度
// const arrTotal = arr1.reduce((total, current) => { return total + current }, 0) // 43 返回数组内所有元素计算的结果(从左到右) 第一个参数是一个函数,函数有两个参数,分别是:上一次相加的结果和当前正要加的值,另一个参数是相加的初始值
// const arrTotal = arr1.reduceRight((total, current) => { return total - current }, 0) // -43 功能和上一个一样,不同之处是它(从右到左)
// const newArr = arr1.reverse() // [ 30, 10, 2, 1 ] 颠倒数组中的顺序,返回颠倒顺序的数组,并改变原数组!!
// const newArr = arr3.sort() // [ 'apple', 'banana', 'peach' ] 对数组内的元素(字母)进行升序排序,但是数字排序会出错(下一个),返回排好顺序的数组,并改变原数组!!
// const newArr = arr1.sort((a, b) => { return a - b }) // [ 1, 2, 10, 30 ] 对数组内的数字执行排序时要传入一个函数,a-b为升序,b-a为降序,返回排好顺序的数组,并改变原数组!!
// const shiftRes = arr1.shift() // 1 删除并返回数组的第一个元素 改变原数组
// const newArrLength = arr1.unshift(99) // 5 向数组的开头添加元素,返回数组的新长度 改变原数组
// const newArr = arr1.slice(0, 2) // [ 1, 2 ] 两个参数分别是切的开始位置和结束位置,返回由选定的元素组成的新数组,不改变原数组
// array.splice(index,howmany,item1,.....,itemX) // index:删除或添加函数的位置;howmany:从index位置删除howmany个元素(必须),不写就直接删除index之后的所有元素(非必须);item1,.....:为添加的元素,不写就不添加(非必须),函数返回被删除的元素
// const newArr = arr1.splice(2,0,99) // [] 返回值为删除的元素所组成的数组,这里我们从数组为2的位置插入(删除)元素,删除0个元素,添加99这个元素,改变原素组!!
// const newString = arr1.toString() // 1,2,10,30 将数组转化为字符串,以逗号分隔数组元素,不改变原数组
// const newString = arr1.valueOf() // 返回元素本身[ 1, 2, 10, 30 ],运算是先调用valueOf再调用toString,日期对象的话会返回1970 年 1 月 1 日到当前对象的毫秒数
1、找出数组内重复的的数字
问题描述:找出数组内重复的的数字,有便返回重复的值,无便返回-1,例如[3,4,5,3,7,9,4],返回3或4均正确。
思路:利用对象的属性名是唯一的
function getRes() {
const arr = [3, 4, 5, 3, 7, 9, 4]
const len = arr.length
const obj = {
}
let resArr = []
if (Array.isArray(arr) && len > 0) {
for (let i = 0; i < len; i++) {
if (obj[arr[i]] === undefined) {
obj[arr[i]] = 1
}
else {
obj[arr[i]]++
}
}
for (let key in obj) {
if (obj[key] >= 2) {
resArr.push(key)
}
}
return resArr.length === 0 ? -1 : resArr[0]
}
else {
return -1
}
}
console.log(getRes()) // 3
2、构建乘积数组
问题描述:给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],
其中B中的元素B[i]=A[0]A[1]…*A[i-1]A[i+1]…A[n-1](说白了:除掉i之后,数组[i]左边右边的数的乘积)。
不能使用除法。(注意:规定B[0] = A[1] * A[2] * … * A[n-1],
B[n-1] = A[0] * A[1] * … * A[n-2];)
对于A长度为1的情况,B无意义,故而无法构建,因此该情况不会存在。
思路:先将前i个数的乘积求出来(前缀乘),再将后i个数的乘积求出来(后缀乘积),最后将前缀*后缀既得B[i]。
function getRes() {
let arr = [1, 2, 3, 4, 5] // 输入数组
let preArr = [] // 前缀乘积
preArr[0] = 1
for (let i = 1; i < arr.length; i++) {
preArr[i] = preArr[i - 1] * arr[i - 1]
}
let sufArr = [] // 后缀乘积
sufArr[arr.length - 1] = 1
for (let j = arr.length - 2; j >= 0; j--) {
sufArr[j] = sufArr[j + 1] * arr[j + 1]
}
let resArr = [] // 结果数组
for (let i = 0; i < arr.length; i++) {
resArr[i] = preArr[i] * sufArr[i]
}
return resArr
}
console.log(getRes())
3、二维数组中的查找:
问题描述:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
[
[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]
]
给定 target = 7,返回 true。
给定 target = 3,返回 false。
思路:从右上角(左下角也可以,因为这样我们没走一步都能淘汰一行或一列,但是右下角和左上角每走一步却不能淘汰一行或一列效率不高)开始查找,先往左走,遇到比target大的数就继续左走,遇到比target小的数就往下走,就能找到target,循环完还找不到就说明没有,返回return。最后ES6中有将多维数组转化为一维数组的方法,arr.flat(数组的维度),例如[[1, 2, 8, 9], [2, 4, 9, 12, [1,1,1]]]. flat( 2 ) // [1, 2, 8, 9, 2,4, 9, 12, 1, 1,1] ,对二维没有思路的话,转为一维就很简单了,如果面试官能让这样解的话。。。
function Find(target, array) {
if (Array.isArray(array) && array.length > 0 && Array.isArray(array[0]) && array[0].length > 0) {
let x = array.length // 获取二维数组的行数
let y = array[0].length // 获取二维数组的列数
// 从二维数组的右上角开始查找target
let i = array[0].length - 1 // 列位置
let j = 0 // 行位置
while (i >= 0 && j <= x - 1) {
if (target < array[i][j]) {
// 如果目标值前一个数小,往左走
i--
}
else if (target > array[i][j]) {
// 如果目标值比下一个数大,往下走
j++
}
else {
// 不大也不小了,说明相等了,找到目标值,返回true
return true
}
}
return false // 循环完没找到,说明没有,return false
}
}
const res = Find(15, [
[1, 2, 8, 9],
[2, 4, 9, 12],
[4, 5, 10, 13],
[6, 8, 11, 15]
])
console.log(res) // true
4、两数之和
问题描述:给出一个整数数组,请在数组中找出两个加起来等于目标值的数,
你给出的函数twoSum 需要返回这两个数字的下标(index1,index2),需要满足 index1 小于index2.。注意:下标是从1开始的
假设给出的数组中只存在唯一解
例如:
给出的数组为 {20, 70, 110, 150},目标值为90
输出 index1=1, index2=2
思路:最开始肯定会使用循环暴力解,两个for循环很快解出来了,但是去看最优解才知道,这道题可以使用hash表的方式解出来,每一次循环的时候都将数组中的值和索引值放到hash表中,target依次减去数组中的数,如果得到的结果恰巧hash表中已经存进去了(就是6-2 = 4 ;4在hash表中还没有将2存进去,但是下次轮询 6-4 = 2 ,2刚好在上一轮被放进去了,就有,得出结果数组 ),那么就找到了,返回hash表中的索引和当前遍历的索引即可。
function twoSum(numbers, target) {
let len = numbers.length
if (Array.isArray(numbers) && len > 0){
let resArr = new Map()
let res = undefined
for (let i = 0; i < len; i++) {
res = target - numbers[i]
if (resArr.has(res)) {
return [resArr.get(res) + 1, i + 1]
}
resArr.set(numbers[i],i)
}
}
}
twoSum([3, 2, 4], 6) // [ 2, 3 ]
5、将数组中的0放到数组的末尾
问题描述:将数组中的0放到数组的末尾
例如:输入:[ 9, 10, 0, 1, 2, 2, 3, 0,12] 输出:[ 9, 10, 1, 2, 2, 3, 12, 0, 0 ]
思想:用两个指针,例如是指针A与指针B,指针A跟随for循环正常指到数组中的每一项,而指针B当指针A指的不是0才跟着往下指,一旦B指到数组中的0就停止走动,当指针A指到下一个不是0的数值时,就和指针B指到的值交换,指针A直接赋值为0,渐渐把不是0的值放在数组的前面,0放到数组的后面
function twoSum(nums) {
let tem = 0
for (let i = 0; i < nums.length; i++) {
if (nums[i] !== 0) {
// 当tem指到0了tem就不移动,让i++
if (nums[tem] == 0) {
nums[tem] = nums[i]
nums[i] = 0
}
tem++ // 当指到数组中不是0的数tem才继续移动
}
}
return nums
}
twoSum([9, 10, 1, 0, 2, 2, 0, 3, 12])
6、斐波那契数列
问题描述:大家都知道斐波那契数列(除了前三项,之后项都等于前两项之和,现在要求输入一个整数n,请你输出斐波那契数列的第n项
思路:可以用递归实现,优点:代码可读性高,缺点:时间复杂度和空间复杂度高,重复计算部分较多;所以改用循环加三数实现
function getRes(n) {
if (n < 2) {
return n
}
let first = 1;
let second = 1;
let res = 0;
for (let i = 2; i < n; i++) {
res = first + second
first = second
second = res
}
return res
}
6.1青蛙跳台阶问题
问题描述:一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
分析:
当n=1时结果为1
当n=2时结果为2
当n=3时结果为3
当n=4时结果为5
当n=5时结果为8
…
可以得到规律当n>=3时有 f(3) = f(2) + f(1)即f(n) = f(n-1) + f(n-2);也就是斐波那契数列
function qwJump(n) {
if (n <= 1) {
return 1
}
if (n === 2) {
return 2
}
let res = 0
let t1 = 1
let t2 = 2
for (let i = 3; i <= n; i++) {
res = (t1 + t2) % 1000000007
t1 = t2
t2 = res
}
return res
}
矩形覆盖算法:
用21的小矩形去覆盖2n的大矩形,要求无重叠
f(8) = f(7)+f(6) 任然是斐波那契数列。
6.2青蛙变态跳台阶问题(动态规划)
问题描述:一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶,也可以跳上3阶台阶…也可以跳上n阶台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
思路:
- n=1 有1种跳法
- n=2 有2种跳法
- n=3 有4种跳法
- n=4 有8种跳法
- n=5 有16种跳法
- …
由规律可知,n = 2*(n-1),所以我们可以用到动态规划:
function jump2(n) {
const dp = Array.from(new Array(n + 1), () => 0);
dp[2] = 2;
for (let i = 3; i < n + 1; i++) {
dp[i] = 2 * dp[i - 1];
}
return dp[n];
}
console.log(jump2(4)) // 8
7、把数组排成最小的数
问题描述:输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组[3,32,321],则打印出这三个数字能排成的最小数字为321323。
思路:利用两个for循环,一个指向数组的前一个数,一个指向数组的后一个数,由前一个数和后一个数组成一个字符串,比较这两个字符串的大小,如果前一个比后一个大,那就利用第三变量换位置,循环结束,就得出了结果
function getRes(numbers) {
let len = numbers.length
let t = 0
let str = ''
for (let i = 0; i < len; i++) {
for (let j = i + 1; j < len; j++) {
let s1 = `${
numbers[i]}${
numbers[j]}`
let s2 = `${
numbers[j]}${
numbers[i]}`
if (parseInt(s1) > parseInt(s2)) {
t = numbers[i]
numbers[i] = numbers[j]
numbers[j] = t
}
}
}
for (let i = 0; i < len; i++) {
str += `${
numbers[i]}`
}
return str
}
9、三数之和
问题描述:给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
思路:先将给定数组进行排序,让它从小到大排列,这样方便当前sum<target时移动left指针,sum>taget时移动right指针,三数之和(利用排序加指针)的思想和两数之和(利用hash)一样,都是利用a+b = c,c -b= a,就在数组里面找a就可以了。
function threeSum(nums) {
let num2 = nums.sort((a, b) => {
return a - b })
let resArr = []
let len = nums.length
for (let i = 0; i < len-2; i++) {
let target = -num2[i]
let left = i + 1
let right = len - 1
// let target = left + right
if (num2[i] === num2[i - 1]) {
// 前面出现过的数,那结果肯定有了,不用再去遍历了
continue;
}
while (left < right) {
let sum = num2[left] + num2[right]
if (sum < target) {
left++
}
else if (sum > target) {
right--
}
else {
resArr.push([num2[i], num2[left], num2[right]])
while (num2[left] === num2[left + 1]) {
// 排除重复的结果
left++
}
left++
while (num2[right] === num2[right - 1]) {
right--
}
right--
}
}
}
return resArr
};
console.log(threeSum([-1, 0, 1, 2, -1, -4]))
输出:[ [ -1, -1, 2 ], [ -1, 0, 1 ] ]
10、找出旋转数组的最小元素
用一般的顺序查找,时间复杂度为O(n),使用二分查找时间复杂度为O(logn)
function minNumberInRotateArray(rotateArray){
const length = rotateArray.length;
if (!length) {
return 0;
}
let left = 0, right = length - 1;
while (left < right) {
let mid = Math.floor((left + right) / 2);
// 子数组有序
if (rotateArray[left] < rotateArray[right]) {
return rotateArray[left];
}
// 左子数组有序,最小值在右边
// 那么mid肯定不可能是最小值(因为rotateArray[mid]大于rotateArray[left])
if (rotateArray[left] < rotateArray[mid]) {
left = mid + 1;
// 右子数组有序,最小值在左边
// 这里right=mid因为最小值可能就是rotateArray[mid]
} else if (rotateArray[mid] < rotateArray[right]) {
right = mid;
} else {
// 无法判断,缩小下范围
++left;
}
}
return rotateArray[left];
}
console.log('minNumberInRotateArray: ', minNumberInRotateArray([3,4,5,1,2])); // 1
11、剪绳子(贪心)
题目描述:给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1,m<=n),每段绳子的长度记为k[1],…,k[m]。请问k[1]x…xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
思路:把绳子剪为长度为3能保证剪出来的绳子长度相乘最大,但当绳子的长度为7时,剪成34比33*1大,所以当能裁出一个4的时候,就把4裁出,其余都裁成3
function cutRope(number){
if(number===2){
return 1
}
if(number===3){
return 2
}
let x=number%3; // 取出余数
let y=parseInt(number/3); // number有多少个3
if(x===0){
// 没有余数时
return Math.pow(3,y) // 将取出来的所有3相乘
}else if(x===1){
// 余数为1时,说明可以拿出一个3构成一个4
return 2*2*Math.pow(3,y-1)
}else{
// 当余数为2时,直接将2乘进来即可
return 2*Math.pow(3,y)
}
}
12、实现chunk函数
问题描述:将一个数组分割成多个数组,其中每个数组的单元数目由 length 决定。最后一个数组的单元数目可能会少于 length 个。
示例:
const a = [‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’, ‘h’];
chunk(a, 4);
结果:
[[‘a’, ‘b’, ‘c’, ‘d’],[‘e’, ‘f’, ‘g’, ‘h’]]
function chunk(arr,size){
var arr1=[];
for(var i=0;i<arr.length;i=i+size){
var arr2=arr;
arr1.push(arr2.slice(i,i+size));
}
return arr1;
}
字符串篇:
字符串的基础知识:
let str = "HELLO "
let str2 = "WORLD"
// 字符串涉及到位置的,都是从0开始数
// const res = str.charAt(2) // L ,返回指定位置的字符串,从0开始数
// const res = str.concat(str2) // HELLO WORLD,返回连接好的字符串,不改变原字符串
// const res = str.indexOf('LO') // 3 返回某个字符串在原字符串中出现的第一个位置,没有就返回-1,从0开始数,区分大小写
// const res = str.includes('HEL') // true,返回布尔值,查看字符串中是否包含子串
// const res = str.replace('LO','lo') // HELlo ,将字符串替换成指定字符串,如果没找到相应字符串就返回原字符串(HELLO),返回已替换好的字符串,不改变原字符串
// 'qe.r'.replace(/\w/g, '@') //replace支持正则,将'qe.r'中的字母数字下划线都替换成@
// const res = str.slice(2,4) // LL ,将字符串按指定位置进行切片,前闭后开,slice(start, end)
// const res = str.split('') //[ 'H', 'E', 'L', 'L', 'O', ' ' ], 将字符串分隔为字符串数组,空字符串 ("") 用作参数,那么 str中的每个字符之间都会被分割。参数的意思是从参数指定的地方分割
// const res = str.startsWith('HE') // true,查看字符串是否以指定字符串开头
// const res = str.toLowerCase(str) // hello 将字符串转为小写
// const res = str.toUpperCase(str) // HELLO 将字符串转为大写
// const res = str.trim(str) // 去除字符串两边的空格
1、判断一个字符串是否是回文字符串
问题描述:判断一个字符串是否是回文字符串
解决方案一、使用api:将字符串转化为数组(split),使用数组的(reverse)将数组中的元素逆序排练,将逆序排列的数组再转化为字符串(join)和原字符比较,相等就是回文,不相等就不是回文。
function getRes(inputString) {
if (typeof inputString !== 'string') {
return false
}
else {
return inputString.split('').reverse().join('') === inputString
}
}
解决方案二、不使用api:使用两个指针,一个从头部开始移动,一个从尾部开始移动,每移动一次就判断当前位置是否相等,只要不相等就返回false,循环完了没有返回false那就是回文字符串了
function getRes(inputString) {
if (typeof inputString !== 'string') {
return false
}
let i = 0, j = inputString.length - 1
while (i < j) {
if (inputString[i] !== inputString[j]) {
return false
}
i++
j--
}
return true
}
2、无重复字符的最长子串
描述:给定一个字符串,找出其中不含有重复字符的长子串的长度
输入:“abcabcbb” ,输出:3
思路:一个for循环,将字符串的字母依次加入到第三方数组,在加入之前,判断一下数组中是否已经有了这个字母,如果没有就直接加入,如果有了这个字符串,就从数组的最开头开始删除这个数组,到当前这个字母位置结束,再将当前字母加入到数组,使用Math.max()保留下数组最长的时候,最后返回max即可。
if (typeof inputString !== 'string') {
return false
}
let arr = [], max = 0
for (let i = 0; i < inputString.length; i++) {
let index = arr.indexOf(inputString[i])
if (index !== -1) {
arr.splice(0, index + 1)
}
arr.push(inputString.charAt(i))
max = Math.max(arr.length, max)
}
return max
}
3、字符流中第一个不重复的数字
问题描述:请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
后台会用以下方式调用Insert 和 FirstAppearingOnce 函数
思路:将字符串转化为数组,利用对象key的唯一性,将字符串的每一项都作为obj的key放入对象中,第一次出现赋值为0,第二次乃至以后都还出现就赋值为1,最后遍历对象,输出对象值为一的key即可。
function getRes(ch) {
let strArr = ch.split('')
let obj = {
}
for (let i = 0; i < strArr.length; i++) {
if (obj[strArr[i]] === undefined) {
obj[strArr[i]] = 0
}
else {
obj[strArr[i]] = 1
}
}
for (key in obj) {
if (obj[key] === 0) {
return key
}
}
return '#'
}
4、替换空格
题目描述:请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
思路:1、将字符串转化为由空格划分的数组,将数组再转化为利用%20划分的字符串:
function getRes(s) {
return s.split(" ").join("%20")
}
2、创建一个新的字符串,将目标字符串一一添加到新的字符串,遇到空格就替换为%20:
function getRes(s) {
let newS = ''
for (let i = 0; i < s.length; i++) {
if (s[i] !== " ") {
newS += s[i]
}
else {
newS += '%20'
}
}
return newS
}
5、版本号比较
function toNum(a, b) {
let aArr = a.split("")
let bBrr = b.split("")
let len = Math.max(a.length, b.length)
bBrr.map((item, index, arr) => {
if (item == '.') {
arr.splice(index, 1)
}
})
aArr.map((item, index, arr) => {
if (item == '.') {
arr.splice(index, 1)
}
})
for (let i = 0; i < len; i++) {
if (/[a-z]/.test(aArr[i]) && /[a-z]/.test(bBrr[i])) {
if (aArr[i] == bBrr[i]) {
continue
}
else if (aArr[i] > bBrr[i]) {
console.log(`${
a}是最新的版本`)
return
}
else {
console.log(`${
b}是最新的版本`)
return
}
}
else {
if (aArr[i] == bBrr[i]) {
continue
}
else if (aArr[i] > bBrr[i]) {
console.log(`${
a}是最新的版本`)
return
}
else {
console.log(`${
b}是最新的版本`)
return
}
}
}
}
var a = "v2.23a.3"; b = "v2.23b.8";
toNum(a, b); // v2.23b.8是最新的版本
链表篇
1、从尾到头打印链表
问题描述:输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
输入:{67,0,24,58},返回值:[58,24,0,67]
思路:使用while循环将链表中的值一一加入到数组中,每加一个就让链表的指针下移一个,最后数组调用reverse方法,倒序输出。
function printListFromTailToHead(head)
{
let node = head;
let arr = []
let i = 0
while(node){
arr[i] = node.val
i++
node = node.next
}
return arr.reverse()
// 数组的倒序也可以如下:
let resArr = []
for(let j = 0; j < i; j++){
resArr[j] = arr[i-j-1]
}
}
2、反转链表
问题描述:输入一个链表,反转链表后,输出新链表的表头。
思路:我们需要用到三个指针,实现链表的反转,有两点很重要,一、pre指向null,curr和next都指向链表的第一个元素,二、转化的顺序不能变,1、先用next占住下一个元素,因为curr的next指向前一个元素它和后一个元素的连接就断,再也无法找到下一个元素,所以需要占位,2、将curr的next指向pre(null),完成指向后,移动pre(pre = curr),移动curr(curr = next),最后next和curr都指向null,而pre刚好指向链表的表头
function ReverseList(pHead)
{
let pre = null;
let curr = pHead;
let next = pHead
while(curr){
next = curr.next
curr.next = pre
pre = curr
curr = next
}
return pre
}
3、判断给定的链表中是否有环
思路:判定链表中是否有环,关键在于这个链表是不是可以一直循环走下去。
那么可以设置两个指针,快指针和慢指针,一个走两步,一个走一步。如果链表中有环,那么它们迟早是会相遇的,而且复杂度恒定为O(1)。如果链表为null,单节点,双节点,都不会有环,直接返回false就行了。
/*
* function ListNode(x){
* this.val = x;
* this.next = null;
* }
*/
/**
*
* @param head ListNode类
* @return bool布尔型
*/
function hasCycle( head ) {
// write code here
let fast = head;
let slow = head;
while (fast!= null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if(fast === slow) {
return true;
}
}
return false;
}
树篇
递归
树的算法一般来说都是需要递归,递归需要栈的支持,每调用一次函数都要创建一个 函数执行上下文压入栈 ,拿阶乘的例子来说:
当递归满足return条件的时候,就不需要往下执行了,所以factorial(0)会弹出栈,把结果1传递给下面的执行上下文,接着factorial(1)执行,弹出栈,把结果1传递给factorial(2),着factorial(2)执行,弹出栈,把结果1传递给factorial(3)…
所以递归就是不撞南墙不回头,撞到南墙了由第一个撞到的开始依次回头。
0、驼峰转下划线
问题描述:
let a = { aB: { aBc: 1 }, aBC: 2 }
let b = [{ aB: 1 }, { a: { aBcD: 1 } }]
转化成:
let a = { a_b: { a_bc: 1 }, a_b_c: 2 }
let b = [{ a_b: 1 }, { a: { a_bc_d: 1 } }]
let a = {
aB: {
aBc: 1 }, aBC: 2 }
let b = [{
aB: 1 }, {
a: {
aBcD: 1 } }]
function get_(a) {
let newobj = {
}
for (key in a) {
if (a[key] instanceof Object) {
newobj[key.replace(/([A-Z])/g, "_$1").toLowerCase()] = get_(a[key]) // 递归这一定要重新赋值!
}
else {
newobj[key.replace(/([A-Z])/g, "_$1").toLowerCase()] = a[key] // key.replace(/([A-Z])/g, "_$1" 将key中的大写字母前加上_
}
}
return newobj
}
function getRes(a) {
let newArr = []
if (a instanceof Object && !(a instanceof Array)) {
// 注意a instanceof Object包括数组
return get_(a) // 对象直接返回
}
else if(a instanceof Array) {
for (item of a) {
newArr.push(get_(item))
}
return newArr
}
}
console.log('getRes: ', getRes(b));
1、重建二叉数
问题描述:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
思路:先序遍历的第一个元素就是目标树的根节点,由根节点获取到中序遍历中根节点所在的索引,由索引在中序遍历中切出左右子树,剩下来的思路都是一样的,递归即可
console.log(reConstructBinaryTree([1, 2, 4, 7, 3, 5, 6, 8], [4, 7, 2, 1, 5, 3, 8, 6]))
function TreeNode(x) {
this.val = x
this.left = null
this.right = null
}
function reConstructBinaryTree(pre, vin) {
if (pre.length === 0 || vin.length === 0) {
return null
}
let index = vin.indexOf(pre[0])
let vinLeft = vin.slice(0, index) // 中序遍历左子树
let vinRight = vin.slice(index + 1) // 中序遍历右子树
let preLeft = pre.slice(1,index+1) // 先序遍历左子树
let preRight = pre.slice(index+1) // 先序遍历右子树
let node = new TreeNode(pre[0])
node.left = reConstructBinaryTree(preLeft, vinLeft)
node.right = reConstructBinaryTree(preRight, vinRight)
return node
}
2、二叉树的下一个节点
问题描述:给定一个二叉树其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的next指针
思路:1、有右子树的,那么下个结点就是右子树最左边的点;(eg:D,B,E,A,C,G) 2、没有右子树的,也可以分成两类,a)是父节点左孩子(eg:N,I,L) ,那么父节点就是下一个节点 ; b)是父节点的右孩子(eg:H,J,K,M)找他的父节点的父节点的父节点…直到当前结点是其父节点的左孩子位置。如果没有eg:M,那么他就是尾节点。
function TreeLinkNode(x) {
this.val = x;
this.left = null;
this.right = null;
this.next = null;
}
function GetNext(pNode) {
// // write code here
if(pNode == null) return pNode;
if(pNode.right !== null){
//如果有右子树
pNode = pNode.right;
while(pNode.left != null){
pNode = pNode.left;
}
return pNode;
}
while(pNode.next !== null){
if(pNode === pNode.next.left){
// 2.1 该节点为父节点的左子节点,则下一个节点为其父节点
return pNode.next;
}
// 2.2 该节点为父节点的右子节点,则沿着父节点向上遍历,知道找到一个节点的父节点的左子节点为该节点,则该节点的父节点下一个节点
pNode = pNode.next;
}
return null;
}
3、二叉搜索树的后序遍历
题目描述:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出true,否则输出false。假设输入的数组的任意两个数字都互不相同。例如输入 [4,8,6,12,16,14,10] 输出:true ; 输入[7,4,6,5] 输出:false
思路:二叉搜索树的后序遍历中最后 一个元素一定是树的根节点,我们在数组中找到第一个比它大的元素,这个元素的左边一定是其左子树,右边是其右子树,左子树中的每一个元素一定比根节点要小,右子树中的每一个元素一定比根节点要大,这两个条件一旦有一个不满足就return false,左子树中也是一样的,左子树的最后一个元素一定是左子树的根节点,又需要在左子树中找到第一个比它大的元素,元素的左边一定是其左子树,右边是其右子树…右子树也一样,所以我们用递归来解。
function getRes(arr) {
if (arr <= 1) {
return true
}
let rootItem = arr[arr.length - 1] // 找到中序遍历的根节点
let index = 0, leftRoot, rightRoot, leftTree, rightTree, res = true
for (let i = 0; i < arr.length; i++) {
if (arr[i] > rootItem) {
// 找到第一个比根节点大的值,用它的切出左右子树
index = arr[i]
break
}
}
leftRoot = arr.indexOf(index) - 1 // 左子树的根节点
rightRoot = arr[arr.length - 2] // 右子树的根节点
leftTree = arr.slice(0, leftRoot + 1) // 根据根节点切出左子树
rightTree = arr.slice(leftRoot + 1, arr.length - 1) // 根据根节点切出右子树
if (leftTree.length > 0) {
for (let i = 0; i < leftTree.length; i++) {
if (leftTree[i] > rootItem) {
// 查看是否左子树中的每个值都比根节点小,比根节点大就不满足条件,return false
res = false
}
}
}
if (rightTree.length > 0) {
for (let i = 0; i < rightTree.length; i++) {
if (rightTree[i] < rootItem) {
// 查看是否右子树中的每个值都比根节点大,比根节点小就不满足条件,return false
res = false
}
}
}
getRes(leftTree) // 递归切出来的左子树
getRes(rightTree) // 递归切出来的右子树
return res
}
console.log('getRes(ch): ', getRes([4, 8, 6, 12, 16, 14, 10])); // true
层次遍历二叉树
问题描述:如图所示为二叉树的层次遍历,即按照箭头所指方向,按照1、2、3、4的层次顺序,对二叉树中各个结点进行访问
递归解决:
var levelOrder = function(root) {
if(!root)return []
let res=[]
let aux=[root]
while(aux.length>0){
let len=aux.length
let vals=[]
for(let i=0;i<len;i++){
let node=aux.shift()
vals.push(node.val)
if(node.left)aux.push(node.left)
if(node.right)aux.push(node.right)
}
res.push(vals)
}
return res
};
非递归解决:
var levelOrder = function(root) {
if(!root)return []
let res=[]
let aux=[root]
while(aux.length>0){
let len=aux.length
let vals=[]
for(let i=0;i<len;i++){
let node=aux.shift()
vals.push(node.val)
if(node.left)aux.push(node.left)
if(node.right)aux.push(node.right)
}
res.push(vals)
}
return res
};
栈篇
1、两个栈实现队列
题目描述:用两个栈来实现一个队列,分别完成在队列尾部插入整数(push)和在队列头部删除整数(pop)的功能。 队列中的元素为int类型。保证操作合法,即保证pop操作时队列内已有元素。
思路:栈先入后出、队列先入先出,两个栈stack1、stack2,stack1用来入栈,stack2用来出栈,stack2中的元素都来自于stack1弹栈的元素,只要stack2中还有元素,每次要求出栈时就让stack2弹栈,stack2无元素了才从stack1的弹栈来入栈。
let stack1 =[]
let stack2 = []
function push(node) {
// write code here
stack1.push(node)
}
function pop() {
// write code here
if (stack1.length==0 && stack2.length==0) {
return null
}
else if(stack2.length){
return stack2.pop()
}
else{
while(stack1.length){
stack2.push(stack1.pop())
}
}
return stack2.pop()
}
查找篇
1、顺序查找
就是一般的for循环查找,时间复杂度O(n)
function sequenceSearch(arr, num) {
const len = arr.length
for (let i = 0; i < len; i++) {
if(arr[i] == num){
return i
}
}
return null
}
console.log(sequenceSearch([1,4,7,9],7)) // 2
2、二分查找
思路:left和right分别指向数组的第一个和最后一个元素,mid指向中间元素,当target小于中间元素时,right = mid+1,当target大于中间元素时left = mid+1,当中间元素 = target时,返回target,三个条件都不满足,说明数组中没有这个元素,返回undefined
每一次查找,范围都会缩小到前一次的一半,时间复杂度为O(logn)
function getRes(rotateArray, target) {
let len = rotateArray.length
let left = 0
let right = len - 1
while (left <= right) {
let mid = Math.floor((left + right) / 2)
if (target > rotateArray[mid]) {
left = mid + 1
}
else if (target < rotateArray[mid]) {
right = mid - 1
}
else if (target = rotateArray[mid]) {
return mid
}
else {
return undefined
}
}
}
3、哈希表查找
题目:字符流中第一个不重复的数字
详情见 上述 字符串篇 第三题
4、二叉排序树查找
二叉排序树查找算法对应的数据结构是二叉搜索树,算法示例见上述 树篇的第三题
排序篇
1、冒泡排序
时间复杂度: 平均时间复杂度O(nn) 、最好情况O(n)、最差情况O(nn)
空间复杂度: O(1)
稳定性: 稳定
function swap(i,j,arr){
let temp = 0
temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
}
function bubbleSort(arr) {
let len = arr.length
for (let i = 0; i < len; i++) {
for (let j = i + 1; j < len; j++) {
if (arr[i] > arr[j]) {
swap(i,j,arr)
}
}
}
return arr
}
console.log(bubbleSort([5, 2, 7, 9, 1])) // [ 1, 2, 5, 7, 9 ]
2、插入排序
插入排序(依次取出数组中的元素,让被取出的元素和前面已排好顺序的元素一一作比较,遇到比它大的就再往前比较,直到遇到比他小的元素,就将当前元素插入到他遇到的第一个比他小的元素的前面)
空间上:它只需要一个记录的辅助空间,因此关键看时间复杂度。
最好情况:也就是排序表本身就是有序的,则只需要进行n-1次比较,由于每次都是arr[i] > arr[i+1],没移动记录,时间复杂度为O(n).
最坏情况:是排序表逆序,需要比较 (n+2)(n-1)/2次,移动(n+4)(n-1)/2次。如果排序记录是随机的,根据概率相同的原则,平均比较和移动的次数约为O(n²)/4次。
时间复杂度为O(n²),比冒泡排序和简单选择排序算法性能好一些。
function insertSort(arr) {
let len = arr.length
for (let i = 1; i < len; i++) {
let currentValue = arr[i] // 获取数组中的当前元素
let j = i - 1 // 指到当前元素的前一个元素
while (j >= 0 && arr[j] > currentValue) {
// 当前一个元素大于当前元素时
if (arr[j] >= currentValue) {
// 如果前一个元素大于当前元素
arr[j + 1] = arr[j] // 将前一个元素往后移一位,因为当前元素本来就被currentValue保存下来了,i的位置基本可以认为是空的
}
j--
}
arr[j + 1] = currentValue // while循环结束,j指到比当前元素小的位置,我们就把当前元素插到它的前面
}
return arr
}
console.log('insertSort(ch): ', insertSort([4, 8, 6, 12, 16, 4, 14, 10])); // [ 4, 4, 6, 8, 10, 12, 14, 16]
3、快速排序
快速排序(而在最坏情况下,即数组已经有序或大致有序的情况下,每一次划分都是以数组最后一个元素作为标准,每次划分只能减少一个元素,快速排序将不幸退化为冒泡排序,最坏情况为O(n²)。快速排序和归并排序一样,每一层的总时间复杂度都是O(n),因为需要对每一个元素遍历一次。在实际应用中,快速排序的平均时间复杂度为O(nlogn)。空间复杂度:快排这种递归算法需要栈的支持,栈的大小最多为n所以是O(n))
关键点1:划分子区间,每一次的子区间长度是上一次的两倍,所以merge_sort递归需要执行logn次
关键点2:对于每一个区间,处理的时候,都需要遍历一次区间中的每一个元素,每一层的总时间复杂度都是O(n)
function quickSort(arr) {
if (arr.length <= 1) {
// 如果数组中只有一个值或者是空数组,直接将其返回,这是递归的条件
return arr
}
let currentIndex = Math.floor(arr.length / 2) // 获取数组最中间的数
let currentItem = arr.splice(currentIndex, 1) // 将最中间的数取出并在数组中将最中间的数删除
let leftArr = [], rightArr = []
// 关键点2:对于每一个区间,处理的时候,都需要遍历一次区间中的每一个元素,每一层的总时间复杂度都是O(n)
arr.forEach(item => {
if (item < currentItem) {
leftArr.push(item) // 将数组中比中间值小的都放在中间值的左边
}
else {
rightArr.push(item) // 将数组中比中间值大的都放在中间值的左边
}
})
// 左数组和右数组操作一样,递归再进行上面的部分
return quickSort(leftArr).concat(currentItem).concat(quickSort(rightArr))
// 关键点1:划分子区间,每一次的子区间长度是上一次的两倍,所以merge_sort递归需要执行logn次
}
console.log(quickSort([5, 2, 7, 9, 1])) // [ 1, 2, 5, 7, 9 ]
4、归并排序
首先将数组一分为二一分为二…直到分到一个数组中只有一个元素,然后在将元素比较完放入一个数组,由递归的特性传给下一个递归函数,最后合并起来
平均时间复杂度:O(nlogn)
最佳时间复杂度:O(n)
最差时间复杂度:O(nlogn)
空间复杂度:O(n)
排序方式:In-place
稳定性:稳定
function merge_sort(arr) {
// 将数组由最中间的数作为基准一分为二,递归执行,最终将一个大数组分为由一个元素组成的数组
if (arr.length == 1){
return arr
}
var mid = Math.floor(arr.length / 2)
var leftArr = arr.slice(0, mid)
var rightArr = arr.slice(mid)
// 关键点1:划分子区间,每一次的子区间长度是上一次的两倍,所以merge_sort递归需要执行logn次
return Merger(merge_sort(leftArr), merge_sort(rightArr)); //合并左右部分
// 关键点2:Merger方法每次执行的时间复杂度为O(n),具体看下方
}
function Merger(leftArr, rightArr) {
var leftLen = leftArr && leftArr.length;
var rightLen = rightArr && rightArr.length;
var resArr = [];
var i = 0, j = 0;
while (i < leftLen && j < rightLen) {
if (leftArr[i] < rightArr[j]) {
resArr.push(leftArr[i++]);
}
else {
resArr.push(rightArr[j++]);
}
}
while (i < leftLen) {
resArr.push(leftArr[i++]);
}
while (j < rightLen) {
resArr.push(rightArr[j++]);
}
console.log("将数组", leftArr, '和', rightArr, '合并为', resArr)
return resArr;
递归过程:
// 将数组 [ 8 ] 和 [ 2 ] 合并为 [ 2, 8 ]
// 将数组 [ 1 ] 和 [ 2, 8 ] 合并为 [ 1, 2, 8 ]
// 将数组 [ 4 ] 和 [ 9 ] 合并为 [ 4, 9 ]
// 将数组 [ 6 ] 和 [ 4, 9 ] 合并为 [ 4, 6, 9 ]
// 将数组 [ 1, 2, 8 ] 和 [ 4, 6, 9 ] 合并为 [ 1, 2, 4, 6, 8, 9 ]
}
console.log('merge_sort(ch): ', merge_sort([1,8,2,6,4,9])); // [ 4, 4, 6, 8, 10, 12, 14, 16]
回溯算法篇
1、矩阵中的路径
问题描述:请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。[[a,b,c,e],[s,f,c,s],[a,d,e,e]],“abcced”
思想:任选一个位置作为路径的起点,就选(0,0),以下的每一步都可以在矩阵中上、右、下、左的移动一格,找不到就return false,找到了就把布尔值矩阵的对应位置设置为true,再在这个上开始上、右、下、左;在移动之后,需要做边界判断:1、判断这个位置是否还在矩阵内;2、是否已经被走过了;当找到字符串中对应的数字时,在矩阵的位置它又可以上、右、下、左的移动一格来找下一个与目标字符串匹配的字符,移动的操作方法都是一样的,所以这里我们使用递归实现,路径可被看作一个栈,由于重复的路径不能重复的加入格子,所以还需要定义一个与字符矩阵大小相同的布尔值矩阵,用来标识路径是否进入了每一个格子。
// [[a,b,c,e],[s,f,c,s],[a,d,e,e]],"abcced"
let m, n // m行 n列
let p = [[-1, 0], [1, 0], [0, 1], [0, -1]] // 向左 右 下 上 的方向走
let isUsed // 判断格子是否被走过,走过就为true,没走过为false
function hasPath(matrix, word) {
m = matrix.length
n = matrix[0].length
isUsed = new Array(m).fill(false).map(p => new Array(n).fill(false)) // 创建一个m行 n列的矩阵
// 从[0,0]开始找, 循环多次找到首个字母的初始位置
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
if (findWords(matrix, word, 0, i, j)) {
return true
}
}
}
return false;
}
function findWords(matrix, word, index, startX, startY) {
if (index === word.length - 1) {
return matrix[startX][startY] === word[index]
}
if (matrix[startX][startY] == word[index]) {
// 在矩阵中找到了words中的字母
isUsed[startX][startY] = true // 表明这个格子已经找过了
for (let i = 0; i < 4; i++) {
// 从矩阵中已经找到words中的字母的位置开始从左 右 上 下的方向又开始寻找words中的下一个字母
let nextX = startX + p[i][0]
let nextY = startY + p[i][1]
if (isInArea(nextX, nextY) && !isUsed[nextX][nextY]) {
// 判断是否走超出了格子 && 判断这个格子是否被走过
if(findWords(matrix, word, index+1, nextX, nextY)){
// index+1 因为index的位置的words字母已经被找到了,相同的方法比较矩阵中值和words中的字母是否相同
return true
}
}
}
}
isUsed[startX][startY] = false
return false // 在矩阵中没找到与words中相同的值就return false
}
function isInArea(x, y) {
return x >= 0 && y >= 0 && x < m && y < n
}
console.log('hasPath: ', hasPath([
['a', 'b', 'c', 'e'],
['s', 'f', 'c', 's'],
['a', 'd', 'e', 'e']], "abcced"));
// true
2、机器人的运动范围
地上有一个rows行和cols列的方格。坐标从 [0,0] 到 [rows-1,cols-1]。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于threshold的格子。 例如,当threshold为18时,机器人能够进入方格[35,37],因为3+5+3+7 = 18。但是,它不能进入方格[35,38],因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
思想:让机器人从0,0位置开始走,它的下一步可以向上、下、左、右走,但是在它向下走之前,我们需要判断;1、它当前所在的位置在矩阵之内 2、他所在的矩阵格子是否满足行坐标和列坐标的数位之和大于threshold;3、是否已经是走过的格子,最后一个条件我们可以创建一个rows行,cols列的矩阵,最开始里面放的都是false,当它的某格被走过,我们就那格置为true,所以true就代表已经被走过。
function movingCount(threshold, rows, cols) {
if (threshold < 0 || rows < 0 || cols < 0) {
return 0
}
let isVisited = new Array(rows).fill(false).map(item => new Array(cols).fill(false))
let count = movingCountCore(threshold, rows, cols, 0, 0, isVisited)
return count
}
function movingCountCore(threshold, rows, cols, startX, startY, isVisited) {
let count = 0
if (checkBoard(rows, cols, startX, startY) && getDigitSum(startX, startY) <= threshold && !isVisited[startX][startY]) {
isVisited[startX][startY] = true
count = 1 + movingCountCore(threshold, rows, cols, startX - 1, startY, isVisited)
+ movingCountCore(threshold, rows, cols, startX, startY - 1, isVisited)
+ movingCountCore(threshold, rows, cols, startX + 1, startY, isVisited)
+ movingCountCore(threshold, rows, cols, startX, startY + 1, isVisited)
}
return count
}
function checkBoard(rows, cols, startX, startY) {
if (startX >= 0 && startX < rows && startY >= 0 && startY < cols) {
return true
}
return false
}
function getDigitSum(startX, startY) {
let sum = 0
let str = startX + '' + startY
for (let i = 0; i < str.length; i++) {
sum += str.charAt(i) / 1
}
return sum
}
console.log(movingCount(5, 10, 10)); // 21
高质量的代码篇
1、数值的整数次方
题目描述:给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。保证base和exponent不同时为0。不得使用库函数,同时不需要考虑大数问题,也不用考虑小数点后面0的位数。
注意:这道题虽然简单,往往简单的题才不能掉以轻心,要考虑完整的边界判断,整数,负数和零都要考虑进去,简单的题不考虑边界给面试官的影响就很差。
function Power(base, exponent) {
if (base === 0) {
return 0
}
else if (exponent === 0) {
return 1
}
else {
let index = 0
let res = 1
let numType = true
if (exponent < 0) {
numType = false
exponent = -exponent
}
while (index < exponent) {
res *= base
index++
}
return numType ? res : 1 / res
}
}
console.log('Power: ', Power(2, 3)); // 8
码字不易,都是自己总结的,点赞鼓励下啦~
持续更新中…