程序员修仙之路-数据结构之 CXO让我做一个计算器
菜菜呀,個稅最近改革了,我得重新計算你的工資呀,我需要個計算器,你開發一個吧
CEO,CTO,CFO于一身的CXOX總,咱不會買一個嗎?
菜菜那不得花錢嗎,一塊錢也是錢呀··這個計算器支持加減乘除運算就行,很簡單
CEO,CTO,CFO于一身的CXO(尼瑪)那能不能給我漲點工資呀?
菜菜公司現在很困難,你這個計算器關系到公司的存亡,你要注意呀!!
CEO,CTO,CFO于一身的CXO(關于撇開話題佩服的五體投地)好吧X總,我盡快做
菜菜給你一天時間,我這里著急要用
CEO,CTO,CFO于一身的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;
? ? ? ? }
? ? }
2. 鏈表實現棧(鏈式棧):為了應對數組的擴容問題,我們可以用鏈表來實現棧。棧的頂部元素永遠指向鏈表的頭元素即可。具體代碼有興趣的同學可以實現一下。
由以上可以看出,棧其實是基于基礎數據結構之上的一個具體業務形式的封裝。即:先進后出。
????????基于數組的棧我們暫且只討論未發生數組重建的場景下。無論是數組實現還是鏈表實現,我們發現棧的內部其實是有一個指向棧頂元素的指針,不會發生遍歷數組或者鏈表的情形,所以棧的出棧操作時間復雜度為O(1)。
????????至于入棧,如果你看過我以前介紹數組和鏈表的文章,你可以知道,給一個數組下標元素賦值的操作時間復雜度為O(1),在鏈表頭部添加一個元素的操作時間復雜度也是O(1)。所以無論是數組還是鏈表實現棧,入棧操作時間復雜度也是O(1)。并且棧只有入棧出棧兩種操作,比其他數據結構有N個操作方法要簡單很多,也不容易出錯。
????????至于發生數組重建,copy全部數據的過程其實是一個順序棧最壞的時間復雜度,因為和原數組的元素個數n有關,所以時間復雜度為O(n)
????????那一個計算器怎么用棧來實現呢?其實很多計算器就是通過兩個棧來實現的,其中一個棧保存操作的數,另一個棧保存運算符。
????????我們從左到右遍歷表達式,當遇到數字,我們直接壓入操作數棧;當遇到操作符的時候,當前操作符與操作符棧頂的元素比較優先級(先乘除后加減的原則)。如果當前運算符比棧頂運算符優先級高,那說明不需要執行棧頂運算符運算,我們直接將當前運算符也入棧;
????????如果當前運算符比棧頂運算符優先級低,那說明該執行棧頂運算符的運算了。然后出棧運算符棧頂元素,數據棧頂兩個元素,然后進行相關運算,然后把運算結果再次壓入數據棧。
golang版本
特別鳴謝公司朋友亮亮提供golang代碼
?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让我做一个计算器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 程序员修仙之路--设计一个实用的线程池
- 下一篇: 人工智能第六课:如何做研究