日常算法题练习

474 阅读6分钟

这里所有的算法练习均来自codewars上的练习,每周两练

1、实现简易计算器

要求:实现加减乘除,乘除优先级高于加减

这里输入用例中符号与数字之间都有空格

输入用例 输出
'127' 127
'2 + 3' 5
'1 + 2 * 3 + 2 / 1' 9

我的想法

首先对输入的字符串进行判断是否有空格,如果没有空格说明是纯数字可以直接转化;如果有空格,说明有运算符号。
对于有运算符号的字符串首先将字符串转换为数组1,该数组1里包含所有的数字和运算符号。对数组1进行遍历,如果没有碰到乘号和除号,将是数字的元素放到空数组2中,将运算符号的元素放到数组2中。如果碰到乘号或者除号就对该符号前后的数字进行运算,将运算后的数字放到数组2中。

注意两点

  1. 遍历到乘号或者除号时,对两个数进行了运算,所以遍历需要往后+1,因为后面的一个元素已经被运算掉了;
  2. 遍历到乘号或者除号时,对两个数进行了运算,这个符号前面的数之前已经放入了数组2中,所以在运算后,需要将数组2中最后一个元素去掉,再将运算得到的结果放入数组2中

代码

function evaluate (string) {
  let newString
  if (string.match(' ')) {
    newString = string.split(' ');
    let dataArr = []
    let stringArr = []
    for (var i=0; i<newString.length; i++) {
      let newValue = parseInt(newString[i]);
      if (newString[i] !== '*' && newString[i] !== '/') {
        if (isNaN(newValue)) {
          stringArr.push(newString[i])
        } else {
          dataArr.push(newValue)
        }
      } else if (newString[i] === '*') {
        let newData = dataArr.pop() * newString[i+1]
        dataArr.push(newData)
        i++
      } else if (newString[i] === '/') {
        let newData = dataArr.pop() / newString[i+1]
        dataArr.push(newData)
        i++
      }
    }
    stringArr.forEach((value, index, arr) => {
      if (value === '+') {
        let newData = dataArr[0] + dataArr[1];
        dataArr.splice(0, 2, newData)
      }
      if (value === '-') {
        let newData = dataArr[0] - dataArr[1];
        dataArr.splice(0, 2, newData)
      }
    })
    newString = dataArr[0]
  } else {
    newString = string.replace('\'', '')
  }
  return newString
}
console.log(evaluate('1 + 2 * 3 + 2 / 1'))

小小总结

  1. 字符串转换为数字的方法
  2. 怎么判断是否为NaN

2、求最小公倍数

var lst = [ [1, 2], [1, 3], [1, 4]]求的分母最小公倍数,且改变返回的形式

用例

Test.assertEquals(convertFrac(lst), "(6,12)(4,12)(3,12)")

我的思想:

  1. 遍历传入的数组,找到每个分母构成一个新数组newArr,并找到其中最大的数max,其知道最小公倍数一定小于每个分母相乘的值maxNum
  2. 我们可以知道我们求的最小公倍数t肯定大于等于max,小于等于maxNum,在这个范围内循环,对newArr里的每个数与max取余数,如果不为0,max+1
  3. 这里注意跳出循环的条件,我们新建了一个长度和newArr相等的空数组,当数组中每一个值都为1,说明这三个都可以整数,那么结束循环

代码

function convertFrac(lst){
    if (lst.length == 1) {
        return "(" + lst[0][0] + ',' + lst[0][1] + ')'
    } else {
        let maxNum = 1
        let newArr = []
        lst.forEach((value, index, arr) => {
            newArr.push(value[1])
            maxNum *= value[1]
        })
        newArr.sort(function (a, b) {
            return b-a
        })
        let max = newArr[0]
        var arr2 = new Array(lst.length);
        var k = 1
        for (var j = max; j<=maxNum; j++) {
            k = 1
            newArr.forEach((value, index, arr) => {
                if ((max % arr[index]) > 0 ) {
                    max++
                    arr2[index] = 0
                } else {
                    arr2[index] = 1  
                }
                k = k * arr2[index]
            })
            if (k==1) {
                for (var j=0; j<lst.length; j++) {
                    lst[j][0] = max/lst[j][1] * lst[j][0]
                    lst[j][1] = max
                    result += '(' + lst[j][0] + ',' + max +')'
                }
                return result
            }
        } 
    }
  }
  console.log(convertFrac([[1,2]]))
  console.log(convertFrac([[1,7], [1,5], [2, 14]]))
  console.log(convertFrac([[1,2],[1,3],[2,18]]))

欢迎大神们指出我冗余的地方

我的代码有点长了,看到别的大神们写的算得,真的是惭愧的要命。。希望自己可以灵活运用其数组的一些方法和正则表达式。。。

3、求字符串所有排序中,最中间的排序

对任意一个字符串,对其里面的字符进行任意换顺序,对组合按照ascii值排序,求最中间的排序

输入用例 输出用例
'abc' 'bac'
'abcd' 'bdca'
'obcxd' 'dcxob'

我的想法

abc的所有排序有abc acb bac bca cab cba
abcd 的所有排序有:
abcd abdc acbd acdb adbc adcb
bacd badc bcad bcda bdac bdca
cabd cadb cbad cbda cdab cdba
dabc dacb dbac dbca dcab dcba
首先对字符串按照ascii值进行排序,对排序后的字符串按照其首字符不同进行分类排序,观察可以得到对于长度n为偶数的字符串,最中间的数永远是第n/2类字符串的最后一个。对于长度n为奇数的字符串,取出第一个字符串肯定是第Math.floor(n/2)个字符,剩下的字符串还是长度为偶数的,那么可以用递归的方法。

代码

function middlePermutation(s) {
  let length = s.length
  let newArr = s.split('')
  newArr.sort(function (a,b) {return a.charCodeAt(0) - b.charCodeAt(0)})
  if (length > 2) {
    let t = Math.floor(length / 2)
    if (length % 2 === 0) {
      let newData1 = [newArr[t-1]]
      newArr.splice(t-1, 1)
      let newArr2 = newArr.sort(function (a, b) {return b.charCodeAt(0) - a.charCodeAt(0)})
      return newData1.concat(newArr2).join('')
    } else {
      let newData2 = [newArr[t]]
      newArr.splice(t, 1)
      let length2 = newArr.length
      if (length2 > 3) {
        newArr2 = middlePermutation(newArr.join(''))
      } else {
        newArr2 = newArr.sort(function (a, b) {return a.charCodeAt(0) - b.charCodeAt(0)})
      }
      return newData2.concat(newArr2).join('')
    }
  } else {
    return newArr.join('')
  }
}

4、大数乘法计算

由于计算机有长度限制,当数多大时,其计算的结果是1.2345679012345677e+36这样的形式,现在的要求就是展示所有的数字,不可以使用这样的方式。

我的思想

取出两个数都转换为数组,对数组b的每一位分别与数组a的每一位数求乘积,然后保存到新的数组中,这样最后获得一个二维数组(该数组的长度等于数组b的长度),该二维数组里的数组是数组b中每一位与数组a进行运算得到的结果。再将这个二维数组中的每一项进行累加计算。

代码

function multiply(a, b)
{
    let arr1 = arguments[0].split('')
    let arr2 = arguments[1].split('')
    let newArr = []
    let result = (parseInt(arguments[0]) * parseInt(arguments[1])).toString().replace(/^[\s|0]+([0-9])/, '$1')
    if (/e\+/.test(result) || result == Infinity) {
        for (let k = arr1.length-1; k >= 0; k--) {
            newArr[k] = []
            let x = 0
            for (let i = arr2.length-1; i>=0; i--) {
                let t = x + (arr2[i] * arr1[k])
                x = Math.floor(t / 10)
                t = t >= 10 && i!==0 ? t % 10 : t
                if (t >= 10 && i === 0) {
                    newArr[k].unshift(x, t%10)
                } else {
                    newArr[k].unshift(t)
                }
            }
            pushZero(arr1.length-k-1, newArr[k])
        }
        let data = 0
        let arr = newArr[0]
        let result = []
        for (let j=1; j<newArr.length; j++) {
            let y = 0
            for (let i=arr.length-1; i>=0; i--) {
                let dis = arr.length-newArr[j].length
                newArr[j] = pushZero(dis, newArr[j], 1)
                arr[i] = newArr[j][i] + arr[i] + y
                y = Math.floor(arr[i]/10)
                arr[i] = arr[i] >= 10 && i!==0 ? arr[i]%10 : arr[i]
                if (arr[i]>= 10 && i === 0) {
                    result.unshift(y, arr[i]%10)
                } else {
                    result.unshift(arr[i])
                }
            }
            arr = result;
            result = []
        }
        result = arr.join('').replace(/^0+([0-9])/, '$1')
        return result
    } 
    return result 
}

function pushZero (a, arr, k) {
    for (let i=0; i<a; i++) {
        if (k) {
            arr.unshift(0)
        } else {
            arr.push(0)
        }
    }
    return arr
}

5、将0和"0"移到数组的最后

不能使用object.prototype和array.prototype的方法,也不可以使用任何临时数组或对象

我的思想

遍历数组,找到一个0或者"0"(假设为arr[i])的时候,在对这个0和"0"后面的数进行遍历,找到一个非(0或者"0")的数字(假设为arr[j])时将arr[i]和arr[j]替换,注意:为了不影响数组中所有0和"0"的排序,需要确定i和j之间是否还有0或者"0",如果有就将该0和后面的arr[j]置换一下,保证最后出来的数组的0和"0"的顺序与原始数组一致且注意数组中如果出现null,false,不需要将其移动到最后

代码

function removeZeros(array) {
    for (let i=0; i<array.length; i++) {
        if (array[i] === 0 || array[i] === "0") {
            for (let j = i; j< array.length; j++) {
                if (array[j] !== 0 && array[j] !== '0') {
                    [array[i], array[j]] = [array[j], array[i]]
                    if (j-i >1) {
                        for (let k=i+1; k<j; k++) {
                            [array[k], array[j]] = [array[j], array[k]]
                        }
                    }
                    break;
                }
            }
        }
    }
    return array
}
console.log(removeZeros(["0",null,0,"0",false]))
console.log(removeZeros(["0",0, "0",1]))