程序员修仙之路-数据结构之 CXO让我做一个计算器
Posted 互联网修炼之道
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了程序员修仙之路-数据结构之 CXO让我做一个计算器相关的知识,希望对你有一定的参考价值。
菜菜呀,个税最近改革了,我得重新计算你的工资呀,我需要个计算器,你开发一个吧
X总,咱不会买一个吗?
那不得花钱吗,一块钱也是钱呀··这个计算器支持加减乘除运算就行,很简单
(尼玛)那能不能给我涨点工资呀?
公司现在很困难,你这个计算器关系到公司的存亡,你要注意呀!!
(关于撇开话题佩服的五体投地)好吧X总,我尽快做
给你一天时间,我这里着急要用
.........
有人说数据结构是为算法服务的,我还要在加一句:数据结构和算法都是为业务服务的!!
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;
}
}
2. 链表实现栈(链式栈):为了应对数组的扩容问题,我们可以用链表来实现栈。栈的顶部元素永远指向链表的头元素即可。具体代码有兴趣的同学可以实现一下。
由以上可以看出,栈其实是基于基础数据结构之上的一个具体业务形式的封装。即:先进后出。
基于数组的栈我们暂且只讨论未发生数组重建的场景下。无论是数组实现还是链表实现,我们发现栈的内部其实是有一个指向栈顶元素的指针,不会发生遍历数组或者链表的情形,所以栈的出栈操作时间复杂度为O(1)。
至于入栈,如果你看过我以前介绍数组和链表的文章,你可以知道,给一个数组下标元素赋值的操作时间复杂度为O(1),在链表头部添加一个元素的操作时间复杂度也是O(1)。所以无论是数组还是链表实现栈,入栈操作时间复杂度也是O(1)。并且栈只有入栈出栈两种操作,比其他数据结构有N个操作方法要简单很多,也不容易出错。
至于发生数组重建,copy全部数据的过程其实是一个顺序栈最坏的时间复杂度,因为和原数组的元素个数n有关,所以时间复杂度为O(n)
那一个计算器怎么用栈来实现呢?其实很多计算器就是通过两个栈来实现的,其中一个栈保存操作的数,另一个栈保存运算符。
我们从左到右遍历表达式,当遇到数字,我们直接压入操作数栈;当遇到操作符的时候,当前操作符与操作符栈顶的元素比较优先级(先乘除后加减的原则)。如果当前运算符比栈顶运算符优先级高,那说明不需要执行栈顶运算符运算,我们直接将当前运算符也入栈;
如果当前运算符比栈顶运算符优先级低,那说明该执行栈顶运算符的运算了。然后出栈运算符栈顶元素,数据栈顶两个元素,然后进行相关运算,然后把运算结果再次压入数据栈。
golang版本
特别鸣谢公司朋友亮亮提供golang代码
1package stack
2
3import (
4 "errors"
5 "fmt"
6)
7
8type Stack struct {
9 Element []interface{} //Element
10}
11
12func NewStack() *Stack {
13 return &Stack{}
14}
15
16func (stack *Stack) Push(value ...interface{}) {
17 stack.Element = append(stack.Element, value...)
18}
19
20//返回下一个元素
21func (stack *Stack) Top() (value interface{}) {
22 if stack.Size() > 0 {
23 return stack.Element[stack.Size()-1]
24 }
25 return nil //read empty stack
26}
27
28//返回下一个元素,并从Stack移除元素
29func (stack *Stack) Pop() (value interface{}) {
30 if stack.Size() > 0 {
31 d := stack.Element[stack.Size()-1]
32 stack.Element = stack.Element[:stack.Size()-1]
33 return d
34 }
35 return nil
36}
37
38//交换值
39func (stack *Stack) Swap(other *Stack) {
40 switch {
41 case stack.Size() == 0 && other.Size() == 0:
42 return
43 case other.Size() == 0:
44 other.Element = stack.Element[:stack.Size()]
45 stack.Element = nil
46 case stack.Size() == 0:
47 stack.Element = other.Element
48 other.Element = nil
49 default:
50 stack.Element, other.Element = other.Element, stack.Element
51 }
52 return
53}
54
55//修改指定索引的元素
56func (stack *Stack) Set(idx int, value interface{}) (err error) {
57 if idx >= 0 && stack.Size() > 0 && stack.Size() > idx {
58 stack.Element[idx] = value
59 return nil
60 }
61 return errors.New("Set失败!")
62}
63
64//返回指定索引的元素
65func (stack *Stack) Get(idx int) (value interface{}) {
66 if idx >= 0 && stack.Size() > 0 && stack.Size() > idx {
67 return stack.Element[idx]
68 }
69 return nil //read empty stack
70}
71
72//Stack的size
73func (stack *Stack) Size() int {
74 return len(stack.Element)
75}
76
77//是否为空
78func (stack *Stack) Empty() bool {
79 if stack.Element == nil || stack.Size() == 0 {
80 return true
81 }
82 return false
83}
84
85//打印
86func (stack *Stack) Print() {
87 for i := len(stack.Element) - 1; i >= 0; i-- {
88 fmt.Println(i, "=>", stack.Element[i])
89 }
90}
91//========================分割线==============================//
92package calculator
93
94import (
95 "calculator/stack"
96 "strconv"
97)
98
99type Calculator struct{}
100
101var DataStack *stack.Stack
102var OperatorStack *stack.Stack
103
104func NewCalculator() *Calculator {
105 DataStack = stack.NewStack()
106 OperatorStack = stack.NewStack()
107 return &Calculator{}
108}
109
110func (c *Calculator) Cal(dataOrOperator string) int {
111
112 if data, ok := strconv.ParseInt(dataOrOperator, 10, 64); ok == nil {
113 //如果是数据直接入数据栈
114 // fmt.Println(dataOrOperator)
115 DataStack.Push(data)
116 } else {
117
118 //如果是操作符,和栈顶操作符比较优先级,如果大于栈顶,则直接入栈,否则栈顶元素出栈 进行操作
119 if OperatorStack.Size() <= 0 {
120 OperatorStack.Push(dataOrOperator)
121 } else {
122 //当前运算符的优先级
123 currentOpePrecedence := operatorPrecedence(dataOrOperator)
124 //当前运算符栈顶元素的优先级
125 stackTopOpePrecedence := operatorPrecedence(OperatorStack.Top().(string))
126 if currentOpePrecedence > stackTopOpePrecedence {
127 //如果当前运算符的优先级大于栈顶元素的优先级,则入栈
128 OperatorStack.Push(dataOrOperator)
129 } else {
130 //运算符栈顶元素出栈,数据栈出栈两个元素,然后进行运算
131 stackOpe := OperatorStack.Pop()
132 data2 := DataStack.Pop()
133 data1 := DataStack.Pop()
134
135 ret := calculateData(stackOpe.(string), data1.(int64), data2.(int64))
136 DataStack.Push(ret)
137 OperatorStack.Push(dataOrOperator)
138 }
139 }
140 }
141 return 0
142}
143
144func (c *Calculator) GetResult() int64 {
145 var ret int64
146 for {
147
148 if OperatorStack.Size() > 0 {
149 stackOpe := OperatorStack.Pop()
150 data2 := DataStack.Pop()
151 data1 := DataStack.Pop()
152
153 ret = calculateData(stackOpe.(string), data1.(int64), data2.(int64))
154
155 DataStack.Push(ret)
156 } else {
157 break
158 }
159 }
160
161 return ret
162}
163
164func calculateData(operatorString string, data1, data2 int64) int64 {
165 switch operatorString {
166 case "+":
167 return data1 + data2
168 case "-":
169 return data1 - data2
170 case "*":
171 return data1 * data2
172 case "/":
173 return data1 + data2
174 default:
175 return 0
176 }
177}
178
179func operatorPrecedence(a string) int {
180 i := 0
181 switch a {
182 case "+":
183 i = 1
184 case "-":
185 i = 1
186 case "*":
187 i = 2
188 case "/":
189 i = 2
190 }
191 return i
192}
193//========================分割线==============================//
194package main
195
196import (
197 "calculator/calculator"
198 "flag"
199 "fmt"
200)
201
202var (
203 inputStr = flag.String("input", "", "请输入...")
204)
205
206func main() {
207 flag.Parse()
208
209 var lstAllData []string
210 var tempData string
211
212 rs := []rune(*inputStr)
213 for i := 0; i < len(rs); i++ {
214 if string(rs[i]) == "+" || string(rs[i]) == "-" || string(rs[i]) == "*" || string(rs[i]) == "/" {
215 lstAllData = append(lstAllData, tempData)
216 lstAllData = append(lstAllData, string(rs[i]))
217 tempData = ""
218 } else {
219 tempData += string(rs[i])
220 }
221 if i == len(rs)-1 {
222 lstAllData = append(lstAllData, tempData)
223 }
224 }
225
226 ca := calculator.NewCalculator()
227 for _, v := range lstAllData {
228 ca.Cal(v)
229 }
230 ret := ca.GetResult()
231 fmt.Println(ret)
232}
c#版本
1class Program
2 {
3 static void Main(string[] args)
4 {
5 List<string> lstAllData = new List<string>();
6 //读取输入的表达式,并整理
7 string inputStr = Console.ReadLine();
8 string tempData = "";
9 for (int i = 0; i < inputStr.Length; i++)
10 {
11 if (inputStr[i] == '+' || inputStr[i] == '-' || inputStr[i] == '*' || inputStr[i] == '/')
12 {
13 lstAllData.Add(tempData);
14 lstAllData.Add(inputStr[i].ToString());
15 tempData = "";
16 }
17 else
18 {
19 tempData += inputStr[i];
20 }
21 if(i== inputStr.Length - 1)
22 {
23 lstAllData.Add(tempData);
24 }
25 }
26 foreach (var item in lstAllData)
27 {
28 Calculator.Cal(item.ToString());
29 }
30 var ret = Calculator.GetResult();
31 Console.WriteLine(ret);
32 Console.Read();
33 }
34
35 }
36 //计算器
37 class Calculator
38 {
39 //存放计算数据的栈
40 static Stack<int> DataStack = new Stack<int>();
41 //存放操作符的栈
42 static Stack<string> OperatorStack = new Stack<string>();
43 public static int Cal(string dataOrOperator)
44 {
45 int data;
46 bool isData = int.TryParse(dataOrOperator, out data);
47 if (isData)
48 {
49 //如果是数据直接入数据栈
50 DataStack.Push(data);
51 }
52 else
53 {
54 //如果是操作符,和栈顶操作符比较优先级,如果大于栈顶,则直接入栈,否则栈顶元素出栈 进行操作
55 if (OperatorStack.Count <= 0)
56 {
57 OperatorStack.Push(dataOrOperator);
58 }
59 else
60 {
61 //当前运算符的优先级
62 var currentOpePrecedence = OperatorPrecedence(dataOrOperator);
63 //当前运算符栈顶元素的优先级
64 var stackTopOpePrecedence = OperatorPrecedence(OperatorStack.Peek());
65 if (currentOpePrecedence > stackTopOpePrecedence)
66 {
67 //如果当前运算符的优先级大于栈顶元素的优先级,则入栈
68 OperatorStack.Push(dataOrOperator);
69 }
70 else
71 {
72 //运算符栈顶元素出栈,数据栈出栈两个元素,然后进行运算
73 var stackOpe = OperatorStack.Pop();
74 var data2 = DataStack.Pop();
75 var data1 = DataStack.Pop();
76 var ret = CalculateData(stackOpe, data1, data2);
77 DataStack.Push(ret);
78 OperatorStack.Push(dataOrOperator);
79 }
80 }
81 }
82 return 0;
83 }
84 //获取表达式最后的计算结果
85 public static int GetResult()
86 {
87 var ret = 0;
88 while (OperatorStack.Count > 0)
89 {
90 var stackOpe = OperatorStack.Pop();
91 var data2 = DataStack.Pop();
92 var data1 = DataStack.Pop();
93 ret = CalculateData(stackOpe, data1, data2);
94 DataStack.Push(ret);
95 }
96 return ret;
97 }
98 //根据操作符进行运算,这里可以抽象出接口,请自行实现
99 static int CalculateData(string operatorString, int data1, int data2)
100 {
101 switch (operatorString)
102 {
103 case "+":
104 return data1 + data2;
105 case "-":
106 return data1 - data2;
107 case "*":
108 return data1 * data2;
109 case "/":
110 return data1 + data2;
111 default:
112 return 0;
113 }
114 }
115 //获取运算符优先级
116 public static int OperatorPrecedence(string a) //操作符优先级
117 {
118 int i = 0;
119 switch (a)
120 {
121 case "+": i = 1; break;
122 case "-": i = 1; break;
123 case "*": i = 2; break;
124 case "/": i = 2; break;
125 }
126 return i;
127
128 }
129 }
●!
●!
●!
以上是关于程序员修仙之路-数据结构之 CXO让我做一个计算器的主要内容,如果未能解决你的问题,请参考以下文章