程序員修仙之路-數據結構之 CXO讓我作一個計算器!!

image

CXO的需求果真還在繼續,深呼吸,深呼吸 .......golang

有人說數據結構是爲算法服務的,我還要在加一句:數據結構和算法都是爲業務服務的!!算法

CXO的需求果真不一樣凡響,又讓菜菜想到了新的數據結構:棧c#

棧的特性

定義

棧(stack)又名堆棧,它是一種運算受限的線性表。其限制是僅容許在表的一端進行插入和刪除運算。這一端被稱爲棧頂,相對地,把另外一端稱爲棧底。向一個棧插入新元素又稱做進棧、入棧或壓棧,它是把新元素放到棧頂元素的上面,使之成爲新的棧頂元素;從一個棧刪除元素又稱做出棧或退棧,它是把棧頂元素刪除掉,使其相鄰的元素成爲新的棧頂元素。數組

棧做爲一種數據結構,其中有幾個特性須要提起你們注意:bash

  1. 操做受限:何爲操做受限?在棧的操做中,通常語言中針對棧的操做只有兩種:入棧和出棧。而且操做只發生在棧的頂部。 有的同窗會問,我用其餘數據結構也同樣能實現棧的效果。不錯,可是每種數據結構都有本身的使用場景,沒有一種絕對無用的數據結構。
  2. 棧在數據結構上屬於一種線性表,知足後進先出的原則。這也是棧的最大特性,幾乎大部分後進先出的場景均可以使用棧這個容器。好比一個函數的調用過程當中,局部變量的存儲就是棧原理。當執行一個函數結束的時候,局部變量其實最早釋放的是最後的局部變量。

image

實現

在內存分佈上棧是用是實現的呢?既然棧是一種線性結構,也就說能夠用線性的內存分佈數據結構來實現。數據結構

  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)app

設計要點

那一個計算器怎麼用棧來實現呢?其實不少編譯器就是經過兩個棧來實現的,其中一個棧保存操做的數,另外一個棧保存運算符。 咱們從左到右遍歷表達式,當遇到數字,咱們直接壓入操做數棧;當遇到操做符的時候,當前操做符與操做符棧頂的元素比較優先級(先乘除後加減的原則) 。若是當前運算符比棧頂運算符優先級高,那說明不須要執行棧頂運算符運算,咱們直接將當前運算符也入棧; 若是當前運算符比棧頂運算符優先級低,那說明該執行棧頂運算符的運算了。 而後出棧運算符棧頂元素,數據棧頂兩個元素,而後進行相關運算,而後把運算結果再次壓入數據棧。數據結構和算法

來一發吧

代碼寫的通常主要是演示棧的應用,特殊狀況下計算結果可能有誤,有興趣的同窗能夠重構一下函數

c# 版本
class Program
    {
        static void Main(string[] args)
        {
            List<string> lstAllData = new List<string>();
            //讀取輸入的表達式,並整理
            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<int> DataStack = new Stack<int>();
        //存放操做符的棧
        static Stack<string> OperatorStack = new Stack<string>();
        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
複製代碼

添加關注,查看更精美版本,收穫更多精彩

image
相關文章
相關標籤/搜索