栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

程序员修仙之路-数据结构之 CXO让我做一个计算器!!

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

程序员修仙之路-数据结构之 CXO让我做一个计算器!!

CXO的需求果然还在继续,深呼吸,深呼吸 …

有人说数据结构是为算法服务的,我还要在加一句:数据结构和算法都是为业务服务的!!

CXO的需求果然不同凡响,又让菜菜想到了新的数据结构:栈

栈的特性 定义

栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

栈作为一种数据结构,其中有几个特性需要提起大家注意:

  1. 操作受限:何为操作受限?在栈的操作中,一般语言中针对栈的操作只有两种:入栈和出栈。并且操作只发生在栈的顶部。 有的同学会问,我用其他数据结构也一样能实现栈的效果。不错,但是每种数据结构都有自己的使用场景,没有一种绝对无用的数据结构。
  2. 栈在数据结构上属于一种线性表,满足后进先出的原则。这也是栈的最大特性,几乎大部分后进先出的场景都可以使用栈这个容器。比如一个函数的调用过程中,局部变量的存储就是栈原理。当执行一个函数结束的时候,局部变量其实最先释放的是最后的局部变量。

实现

在内存分布上栈是用是实现的呢?既然栈是一种线性结构,也就说可以用线性的内存分布数据结构来实现。

  1. 数组实现栈(顺序栈):数组是在内存分布上连续的一种数据结构。经过以前的学习,我们知道数组的容量是不变的。如果业务上可以知道一个栈的元素的最大数量,我们完全可以用数组来实现。为什么这么说?因为数组的扩容在某些时候性能是比较低的。因为需要开辟新空间,并发生复制过程。
 class MyStack
    {
 //数组容器
 int[] container = new int[100];
 //栈顶元素的索引
 int TopIndex = -1;

 //入栈操作
 public void Push(int newValue)
 {
     if (TopIndex >= 99)
     {
  return ;
     }
     TopIndex++;
     container[TopIndex] = newValue;
 }
 //出栈操作
 public int Pop()
 {
     if (TopIndex < 0)
     {
  return 0;
     }
     var topValue = container[TopIndex];
     TopIndex--;
     return topValue;
 }
    }
  1. 链表实现栈(链式栈):为了应对数组的扩容问题,我们可以用链表来实现栈。栈的顶部元素永远指向链表的头元素即可。具体代码有兴趣的同学可以实现一下。

由以上可以看出,栈其实是基于基础数据结构之上的一个具体业务形式的封装即:先进后出。

性能

基于数组的栈我们暂且只讨论未发生数组重建的场景下。无论是数组实现还是链表实现,我们发现栈的内部其实是有一个指向栈顶元素的指针,不会发生遍历数组或者链表的情形,所以栈的出栈操作时间复杂度为O(1)。至于入栈,如果你看过我以前介绍数组和链表的文章,你可以知道,给一个数组下标元素赋值的操作时间复杂度为O(1),在链表头部添加一个元素的操作时间复杂度也是O(1)。所以无论是数组还是链表实现栈,入栈操作时间复杂度也是O(1)。并且栈只有入栈出栈两种操作,比其他数据结构有N个操作方法要简单很多,也不容易出错。
至于发生数组重建,copy全部数据的过程其实是一个顺序栈最坏的时间复杂度,因为和原数组的元素个数n有关,所以时间复杂度为O(n)

设计要点

那一个计算器怎么用栈来实现呢?其实很多编译器就是通过两个栈来实现的,其中一个栈保存操作的数,另一个栈保存运算符。
我们从左到右遍历表达式,当遇到数字,我们直接压入操作数栈;当遇到操作符的时候,当前操作符与操作符栈顶的元素比较优先级(先乘除后加减的原则)
。如果当前运算符比栈顶运算符优先级高,那说明不需要执行栈顶运算符运算,我们直接将当前运算符也入栈;
如果当前运算符比栈顶运算符优先级低,那说明该执行栈顶运算符的运算了。
然后出栈运算符栈顶元素,数据栈顶两个元素,然后进行相关运算,然后把运算结果再次压入数据栈。

来一发吧

代码写的一般主要是演示栈的应用,特殊情况下计算结果可能有误,有兴趣的同学可以重构一下

c# 版本
 class Program
    {
 static void Main(string[] args)
 {
     List lstAllData = new List();
     //读取输入的表达式,并整理
     string inputStr = Console.ReadLine();
     string tempData = "";
     for (int i = 0; i < inputStr.Length; i++)
     {
  if (inputStr[i] == '+' || inputStr[i] == '-' || inputStr[i] == '*' || inputStr[i] == '/')
  {
      lstAllData.Add(tempData);
      lstAllData.Add(inputStr[i].ToString());
      tempData = "";
  }
  else
  {
      tempData += inputStr[i];
  }
  if(i== inputStr.Length - 1)
  {
      lstAllData.Add(tempData);
  }
     }
     foreach (var item in lstAllData)
     {
  Calculator.Cal(item.ToString());
     }
     var ret = Calculator.GetResult();
     Console.WriteLine(ret);
     Console.Read();
 }

    }
    //计算器
    class Calculator
    {
 //存放计算数据的栈
 static Stack DataStack = new Stack();
 //存放操作符的栈
 static Stack OperatorStack = new Stack();
 public static int Cal(string dataOrOperator)
 {
     int data;
     bool isData = int.TryParse(dataOrOperator, out data);
     if (isData)
     {
  //如果是数据直接入数据栈
  DataStack.Push(data);
     }
     else
     {
  //如果是操作符,和栈顶操作符比较优先级,如果大于栈顶,则直接入栈,否则栈顶元素出栈 进行操作
  if (OperatorStack.Count <= 0)
  {
      OperatorStack.Push(dataOrOperator);
  }
  else
  {
      //当前运算符的优先级
      var currentOpePrecedence = OperatorPrecedence(dataOrOperator);
      //当前运算符栈顶元素的优先级
      var stackTopOpePrecedence = OperatorPrecedence(OperatorStack.Peek());
      if (currentOpePrecedence > stackTopOpePrecedence)
      {
   //如果当前运算符的优先级大于栈顶元素的优先级,则入栈
   OperatorStack.Push(dataOrOperator);
      }
      else
      {
   //运算符栈顶元素出栈,数据栈出栈两个元素,然后进行运算
   var stackOpe = OperatorStack.Pop();
   var data2 = DataStack.Pop();
   var data1 = DataStack.Pop();
   var ret = CalculateData(stackOpe, data1, data2);
   DataStack.Push(ret);
   OperatorStack.Push(dataOrOperator);
      }
  }
     }
     return 0;
 }
 //获取表达式最后的计算结果
 public static int GetResult()
 {
     var ret = 0;
     while (OperatorStack.Count > 0)
     {
  var stackOpe = OperatorStack.Pop();
  var data2 = DataStack.Pop();
  var data1 = DataStack.Pop();
  ret = CalculateData(stackOpe, data1, data2);
  DataStack.Push(ret);
     }
     return ret;
 }
 //根据操作符进行运算,这里可以抽象出接口,请自行实现
 static int CalculateData(string operatorString, int data1, int data2)
 {
     switch (operatorString)
     {
  case "+":
      return data1 + data2;
  case "-":
      return data1 - data2;
  case "*":
      return data1 * data2;
  case "/":
      return data1 + data2;
  default:
      return 0;
     }
 }
 //获取运算符优先级
 public static int OperatorPrecedence(string a)    //操作符优先级
 {
     int i = 0;
     switch (a)
     {
  case "+": i = 1; break;
  case "-": i = 1; break;
  case "*": i = 2; break;
  case "/": i = 2; break;
     }
     return i;

 }
    }

运行结果:

10+20*3+10-10+20-20+60*2
190
golang版本
package stack

import (
	"errors"
	"fmt"
)

type Stack struct {
	Element []interface{} //Element
}

func NewStack() *Stack {
	return &Stack{}
}

func (stack *Stack) Push(value ...interface{}) {
	stack.Element = append(stack.Element, value...)
}

//返回下一个元素
func (stack *Stack) Top() (value interface{}) {
	if stack.Size() > 0 {
		return stack.Element[stack.Size()-1]
	}
	return nil //read empty stack
}

//返回下一个元素,并从Stack移除元素
func (stack *Stack) Pop() (value interface{}) {
	if stack.Size() > 0 {
		d := stack.Element[stack.Size()-1]
		stack.Element = stack.Element[:stack.Size()-1]
		return d
	}
	return nil
}

//交换值
func (stack *Stack) Swap(other *Stack) {
	switch {
	case stack.Size() == 0 && other.Size() == 0:
		return
	case other.Size() == 0:
		other.Element = stack.Element[:stack.Size()]
		stack.Element = nil
	case stack.Size() == 0:
		stack.Element = other.Element
		other.Element = nil
	default:
		stack.Element, other.Element = other.Element, stack.Element
	}
	return
}

//修改指定索引的元素
func (stack *Stack) Set(idx int, value interface{}) (err error) {
	if idx >= 0 && stack.Size() > 0 && stack.Size() > idx {
		stack.Element[idx] = value
		return nil
	}
	return errors.New("Set失败!")
}

//返回指定索引的元素
func (stack *Stack) Get(idx int) (value interface{}) {
	if idx >= 0 && stack.Size() > 0 && stack.Size() > idx {
		return stack.Element[idx]
	}
	return nil //read empty stack
}

//Stack的size
func (stack *Stack) Size() int {
	return len(stack.Element)
}

//是否为空
func (stack *Stack) Empty() bool {
	if stack.Element == nil || stack.Size() == 0 {
		return true
	}
	return false
}

//打印
func (stack *Stack) Print() {
	for i := len(stack.Element) - 1; i >= 0; i-- {
		fmt.Println(i, "=>", stack.Element[i])
	}
}

package calculator

import (
	"calculator/stack"
	"strconv"
)

type Calculator struct{}

var DataStack *stack.Stack
var OperatorStack *stack.Stack

func NewCalculator() *Calculator {
	DataStack = stack.NewStack()
	OperatorStack = stack.NewStack()
	return &Calculator{}
}

func (c *Calculator) Cal(dataOrOperator string) int {

	if data, ok := strconv.ParseInt(dataOrOperator, 10, 64); ok == nil {
		//如果是数据直接入数据栈
		// fmt.Println(dataOrOperator)
		DataStack.Push(data)
	} else {

		//如果是操作符,和栈顶操作符比较优先级,如果大于栈顶,则直接入栈,否则栈顶元素出栈 进行操作
		if OperatorStack.Size() <= 0 {
			OperatorStack.Push(dataOrOperator)
		} else {
			//当前运算符的优先级
			currentOpePrecedence := operatorPrecedence(dataOrOperator)
			//当前运算符栈顶元素的优先级
			stackTopOpePrecedence := operatorPrecedence(OperatorStack.Top().(string))
			if currentOpePrecedence > stackTopOpePrecedence {
				//如果当前运算符的优先级大于栈顶元素的优先级,则入栈
				OperatorStack.Push(dataOrOperator)
			} else {
				//运算符栈顶元素出栈,数据栈出栈两个元素,然后进行运算
				stackOpe := OperatorStack.Pop()
				data2 := DataStack.Pop()
				data1 := DataStack.Pop()

				ret := calculateData(stackOpe.(string), data1.(int64), data2.(int64))
				DataStack.Push(ret)
				OperatorStack.Push(dataOrOperator)
			}
		}
	}
	return 0
}

func (c *Calculator) GetResult() int64 {
	var ret int64
	for {

		if OperatorStack.Size() > 0 {
			stackOpe := OperatorStack.Pop()
			data2 := DataStack.Pop()
			data1 := DataStack.Pop()

			ret = calculateData(stackOpe.(string), data1.(int64), data2.(int64))

			DataStack.Push(ret)
		} else {
			break
		}
	}

	return ret
}

func calculateData(operatorString string, data1, data2 int64) int64 {
	switch operatorString {
	case "+":
		return data1 + data2
	case "-":
		return data1 - data2
	case "*":
		return data1 * data2
	case "/":
		return data1 + data2
	default:
		return 0
	}
}

func operatorPrecedence(a string) int {
	i := 0
	switch a {
	case "+":
		i = 1
	case "-":
		i = 1
	case "*":
		i = 2
	case "/":
		i = 2
	}
	return i
}

package main

import (
	"calculator/calculator"
	"flag"
	"fmt"
)

var (
	inputStr = flag.String("input", "", "请输入...")
)

func main() {
	flag.Parse()

	var lstAllData []string
	var tempData string

	rs := []rune(*inputStr)
	for i := 0; i < len(rs); i++ {
		if string(rs[i]) == "+" || string(rs[i]) == "-" || string(rs[i]) == "*" || string(rs[i]) == "/" {
			lstAllData = append(lstAllData, tempData)
			lstAllData = append(lstAllData, string(rs[i]))
			tempData = ""
		} else {
			tempData += string(rs[i])
		}
		if i == len(rs)-1 {
			lstAllData = append(lstAllData, tempData)
		}
	}

	ca := calculator.NewCalculator()
	for _, v := range lstAllData {
		ca.Cal(v)
	}
	ret := ca.GetResult()
	fmt.Println(ret)
}

运算结果:
go run program.go  -input=1+2-1*3
结果:0

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/230741.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号