每周一道算法题002:四则运算
作者:互联网
问题:
求位于1000~9999,满足如下条件的数: 各位数字间加入四则运算符,也可省略,使得按四则运算计算的结果为原数字的各位数逆序排列。
例如:351 → 3×51 = 153
思路:
遍历1000-9999所有的数字,切分并组合运算符,拼成四则运算的算式然后计算。
4位数中最大的数是9999,他可以被拆成999+9=1008,逆排后不可能等于原数,减法和除法不可能计算出比原数更大的数,所以只需要考虑乘法。
php提供了eval函数,可以很方便的计算,但是需要对数字进行处理,因为08开头的数字会被当作8进制,需要清洗一下。
解答:
php
<?php
$ops = array("*", "");
$opsLen = count($ops);
for ($i = 1000; $i < 9999; $i++) {
$num = strval($i);
for ($j = 0; $j < $opsLen; $j++) {
for ($k = 0; $k < $opsLen; $k++) {
for ($l = 0; $l < $opsLen; $l++) {
$str = $num[3] . $ops[$j] . $num[2] . $ops[$k] . $num[1] . $ops[$l] . $num[0];
$len = strlen($str);
if ($len > 4) {
$str = formatNum($str, $len, $ops);
$result = eval('return ' . $str . ';');
if ($i == $result) {
echo $num . "=" . $str . "\n";
}
}
}
}
}
}
// 格式化数字,以08开头的数字会被php当成8进制处理,必须清洗
function formatNum($str, $len, $ops)
{
$stack = array();
$n = '';
for ($i = 0; $i < $len; $i++) {
if (!in_array($str[$i], $ops)) {
$n .= $str[$i];
} else {
$stack[] = intval($n);
$n = '';
$stack[] = $str[$i];
}
// 没有操作符了,就把最后一段数字扔进栈中
if ($i == $len - 1) {
$stack[] = intval($n);
}
}
return join($stack, '');
}
结果为:
1395=5*9*31
golang
golang没有提供eval方法,需要自己实现一个四则运算计算函数。并且需要先将中缀表达式先转为后缀表达式(逆波兰)才能方便程序处理。
参考文档:https://wuyin.io/2018/02/04/calculate-math-statement-by-go-stack/
要实现逆波兰,我们需要用到栈,之前的文章里已经实现了,可以直接拿来用
https://blog.51cto.com/ustb80/2419816
main.go
package main
import (
"./stack"
"fmt"
"strconv"
"strings"
"unicode"
)
func main() {
//s := "1+2*3+(4*5+6)*7"
////s = "22+24*(12-5)*45+16/4"
//s := "899*9"
//r := transCalculation(s)
//fmt.Println(s, "->", r)
//
//result := calculate(r)
//fmt.Println(result)
findNum()
}
func findNum() {
ops := []string{"*", ""}
opsLen := len(ops)
for i := 1000; i < 9999; i++ {
num := strconv.Itoa(i)
for j := 0; j < opsLen; j++ {
for k := 0; k < opsLen; k++ {
for l := 0; l < opsLen; l++ {
str := fmt.Sprintf("%s%s%s%s%s%s%s", string(num[3]), ops[j], string(num[2]), ops[k], string(num[1]), ops[l], string(num[0]))
expLen := len(str)
if expLen > 4 {
postExp := transCalculation(str)
result := calculate(postExp)
if i == result {
fmt.Println(str, "->", postExp, " -> ", result)
}
}
}
}
}
}
}
// 计算后缀表达式的值
// 遇到数字就压栈,遇到运算符就取出两个栈顶元素计算并再压栈
func calculate(exp string) int {
slice := strings.Split(exp, " ")
expLen := len(slice)
s := stack.New()
for i := 0; i < expLen; i++ {
char := slice[i]
if digit, err := strconv.Atoi(char); err == nil {
s.Push(digit)
} else {
s1, _ := strconv.Atoi(fmt.Sprintf("%v", s.Pop()))
s2, _ := strconv.Atoi(fmt.Sprintf("%v", s.Pop()))
switch char {
case "+":
s.Push(strconv.Itoa(s1 + s2))
case "-":
s.Push(strconv.Itoa(s1 - s2))
case "*":
s.Push(strconv.Itoa(s1 * s2))
case "/":
if s2 != 0 {
s.Push(strconv.Itoa(s1 / s2))
}
}
}
//fmt.Println("peek = ",s.Peek())
}
result, _ := strconv.Atoi(fmt.Sprintf("%v", s.Peek()))
return result
}
// 将中缀表达式转为后缀表达式(逆波兰)
func transCalculation(exp string) string {
opStack := stack.New() // 开启一个运算符堆栈
expLen := len(exp) // 表达式长度
result := "" // 存放结果
// 遍历表达式,
// 遇到数字直接输出
// 遇到运算符则判断:
// 1.栈顶运算符优先级更低则入栈,更高或相等则直接输出
// 2.栈为空、栈顶是 ( 直接入栈
// 3.运算符是 ) 则将栈顶运算符全部弹出,直到遇见 (
// 中缀表达式遍历完毕,运算符栈不为空则全部弹出,依次追加到输出
for i := 0; i < expLen; i++ {
char := string(exp[i])
switch char {
case "":
continue
// 数字直接追加至结果
case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9":
// 处理连续数字
j := i
digit := ""
for ; j < expLen && unicode.IsDigit(rune(exp[j])); j++ {
digit += string(exp[j])
}
result += " " + digit
i = j - 1 // 变更i的值,因为j++多加了一次,需要减掉
// 左小括号直接入栈
case "(":
opStack.Push("(")
// 右小括号处理
case ")":
for opStack.Len() > 0 {
preNode := fmt.Sprintf("%v", opStack.Peek())
// 判断栈顶是不是左括号,如果是,就出栈,结束
if preNode == "(" {
opStack.Pop()
break
}
result += " " + preNode
opStack.Pop()
}
default:
for opStack.Len() > 0 {
top := fmt.Sprintf("%v", opStack.Peek())
// 如果当前运算符的优先级高于栈顶,则将当前的入栈
// 否则栈顶出栈,当前入栈
if isOpPrior(char, top) {
break
}
result += " " + top
opStack.Pop()
}
opStack.Push(char)
}
}
for opStack.Len() > 0 {
result += fmt.Sprintf(" %v", opStack.Pop())
}
return result
}
// 判断运符符优先级
// 如果比栈顶的优先级高,返回true
// 如果是左小括号,返回true
func isOpPrior(op string, top string) bool {
switch top {
case "+", "-":
if op == "*" || op == "/" {
return true
}
case "(":
return true
}
return false
}
结果:
5*9*31 -> 5 9 * 31 * -> 1395
标签:opStack,string,fmt,四则运算,strconv,002,算法,result,str 来源: https://blog.51cto.com/ustb80/2420288