棧和隊(duì)列

ArrayList和LinkedList的實(shí)現(xiàn)方式

ArrayList的底層實(shí)現(xiàn)是可以增長(zhǎng)的數(shù)組纳鼎,LinkedList的底層是使用了雙鏈表旨枯。從底層實(shí)現(xiàn)來看漏麦,我們可以知道游添,ArrayList 獲取元素的時(shí)間復(fù)雜度僅為常數(shù),而 插入刪除的時(shí)間復(fù)雜度都為線性時(shí)間復(fù)雜度O(n)秉馏。而LinkedList則剛好相反耙旦,因?yàn)榈讓邮擎湵?所以 插入刪除的時(shí)間復(fù)雜度為常數(shù),而 獲取元素的時(shí)間復(fù)雜度卻是線性時(shí)間復(fù)雜度萝究。

另外免都,ArrayList和LinkedList的contains和remove方法的時(shí)間復(fù)雜度都為線性時(shí)間復(fù)雜度O(n),對(duì)于contains方法來說帆竹,ArrayList和linkedList都需要遍歷操作來實(shí)現(xiàn)contains方法绕娘,所以時(shí)間復(fù)雜度都是線性的;對(duì)于remove操作栽连,ArrayList刪除后需要移動(dòng)元素险领,所以時(shí)間復(fù)雜度是線性的,而LinkedList的remove操作雖然是常數(shù)時(shí)間的秒紧,但是查找到元素的時(shí)間復(fù)雜度是線性的绢陌,所以,對(duì)已LinkedList的remove操作來說也是線性時(shí)間復(fù)雜的熔恢。

棧的運(yùn)用--計(jì)算表達(dá)式的值

本章用Java來是實(shí)現(xiàn)一個(gè)計(jì)算含有+脐湾,-,*叙淌,\和括號(hào)的表達(dá)式的值秤掌,包括的知識(shí)點(diǎn)有:

  • Java語言中棧Stack和Queue的使用
  • 中綴表達(dá)式轉(zhuǎn)后綴表達(dá)式的技巧
  • 計(jì)算后綴表達(dá)式的值

Java提供了棧的實(shí)現(xiàn)Stack對(duì)象愁铺,它繼承了Vector類,底層實(shí)現(xiàn)是數(shù)組闻鉴。Queue是一個(gè)Java隊(duì)列接口茵乱,其中提供了隊(duì)列必要的方法,而LinkedList實(shí)現(xiàn)了Queue接口椒拗,可以使用LinkedList來實(shí)現(xiàn)隊(duì)列操作似将。例如:入隊(duì)操作為addinsert方法,出隊(duì)操作為 poll方法蚀苛,獲取隊(duì)頭的元素為peek方法在验。

中綴表達(dá)式是人們常用的算術(shù)表示方式,其標(biāo)志是操作符處于操作數(shù)之間堵未。而后綴表達(dá)式是操作符處于操作數(shù)之后腋舌,并且暗含的運(yùn)算順序,易于計(jì)算機(jī)進(jìn)行計(jì)算渗蟹。形象點(diǎn)的例子块饺,例如1+2*3+(1+2)是中綴表達(dá)式,將其轉(zhuǎn)換成后綴表達(dá)式為1, 2, 3, * , +, 1, 2, +, +雌芽。其轉(zhuǎn)換過程如下:

輸入:1

操作:將數(shù)字直接加入到結(jié)果隊(duì)列

postfix(結(jié)果隊(duì)列, 從隊(duì)頭到隊(duì)尾):1

opStack(操作符隊(duì)列授艰,從棧底到棧頂,):空

輸入:+

操作:因?yàn)閛pStack棧為空世落,所以將+號(hào)如操作符棧

postfix: 1

opStack: +

輸入:2

操作:將數(shù)字直接加入結(jié)果隊(duì)列

postfix: 1, 2

opStack: +

輸入*

操作:因?yàn)?的計(jì)算優(yōu)先級(jí)大于站定+號(hào)的優(yōu)先級(jí)淮腾,所以入棧

postfix: 1,2

opStack: +,*

輸入3

操作:數(shù)字直接入結(jié)果 隊(duì)列

postfix: 1,2,3

opStack: +,*

輸入+

操作:因?yàn)?號(hào)的優(yōu)先級(jí)不大于棧頂?shù)?,所以*彈棧并且放到結(jié)果隊(duì)列中屉佳;此時(shí)棧頂?shù)娜詾?號(hào)谷朝,但是輸入的+號(hào)的優(yōu)先級(jí)不大于棧頂?shù)?號(hào),所以武花,棧頂?shù)?號(hào)出棧圆凰;此時(shí)棧為空,所以當(dāng)前的+號(hào)入棧

postfix: 1,2,3,*,+

opStack: +

輸入(

操作:對(duì)于( 的輸入來說体箕,直接操作棧即可专钉,也就是說( 在入棧前比任何操作符的優(yōu)先級(jí)都大;而另一中特殊情況是如果棧頂元素是(, 那么只有)的輸入才可以使其出棧干旁,也就是或?qū)τ谄渌僮鞣麃碚f驶沼,它們的優(yōu)先級(jí)又比已經(jīng)入棧的( 的優(yōu)先級(jí)大。

postfix: 1,2,3,*,+

opStack: +,(

輸入1

操作:直接加入結(jié)果隊(duì)列

postfix: 1,2,3,*,+,1

opStack: +,(

輸入+

操作:遇到棧頂為(, 所以直接+號(hào)入操作符棧

postfix: 1,2,3,*,+

opStack: +,(,+

輸入2

操作:直接加入結(jié)果隊(duì)列

postfix: 1,2,3,*,+,1,2

opStack: +,(,+

輸入)

操作:對(duì)于)的輸入争群,應(yīng)該對(duì)操作符棧進(jìn)行彈棧,直至遇到(, 彈棧才結(jié)束大年,這也就是說换薄,)的在入棧前的優(yōu)先級(jí)比其他操作的優(yōu)先級(jí)都低玉雾。在假設(shè)表達(dá)式合法的前提下,不存在)處在棧頂?shù)那闆r轻要,)會(huì)和(一起出棧复旬,但不會(huì)放入結(jié)果隊(duì)列中。這一次冲泥,+號(hào)和(出棧驹碍,+號(hào)加入結(jié)果隊(duì)列。

postfix:1,2,3,*,+,1,2,+

opStack:+

如果操作符棧不為空凡恍,全部彈出加入結(jié)果隊(duì)列

postfix: 1,2,3,*,+,1,2,+,+

opStack: 空

計(jì)算表達(dá)式的值--Java版實(shí)現(xiàn)

接下來志秃,我將要介紹一下我的小型計(jì)算器的實(shí)現(xiàn),表達(dá)式的特征是包含+嚼酝,-浮还,*,/和()闽巩,并且數(shù)字是整數(shù)钧舌,可以是多位的。整個(gè)實(shí)現(xiàn)分為3大部分:

  1. 讀取優(yōu)先級(jí)列表
  2. 中綴表達(dá)式轉(zhuǎn)換成后綴表達(dá)式
  3. 計(jì)算后綴表達(dá)式的值

當(dāng)然了涎跨,最難的部分是第二步洼冻,下面一一介紹。

  • 優(yōu)先級(jí)列表

我是用一個(gè)文件來存儲(chǔ)操作符和對(duì)應(yīng)的優(yōu)先級(jí)隅很,每行存儲(chǔ)一個(gè)撞牢,采用操作符-空格-優(yōu)先級(jí)的方式,如下:+ 1

完整的優(yōu)先級(jí)列表如下

+ 1
- 1
* 2
/ 2

使用HashMap保存優(yōu)先級(jí)列表外构,這里涉及了如何將一個(gè)char轉(zhuǎn)化成一個(gè)int值的過程普泡,我們使用了先將char轉(zhuǎn)化成String,然后將String轉(zhuǎn)化成Integer的方法

        //讀入優(yōu)先級(jí)文件
        Map<Character,Integer> priority=new HashMap<Character,Integer>();
        BufferedReader breader=new BufferedReader(new FileReader("priority.txt"));
        String pline=breader.readLine();
        while(pline!=null) {
            char[] plineChar=pline.toCharArray();
            priority.put(plineChar[0], Integer.parseInt(String.valueOf(plineChar[2])));
            pline=breader.readLine();
        }
        System.out.println("優(yōu)先級(jí):"+priority);
        breader.close();
  • 中綴表達(dá)式轉(zhuǎn)后綴表達(dá)式

因?yàn)閿?shù)字是多位的审编,所以用了一個(gè)棧來numStack來記錄數(shù)撼班,并且使用一個(gè)靜態(tài)方法拼接成一個(gè)整數(shù)。opStack< Character>是用來存儲(chǔ)操作符的垒酬,postfix是用來存儲(chǔ)轉(zhuǎn)換好的后綴表達(dá)式的砰嘁。整個(gè)過程如下:

        //numStack用于存儲(chǔ)存儲(chǔ)操作數(shù)
        Stack<Integer>numStack=new Stack<>();
        //opStack用于存儲(chǔ)操作符
        Stack<Character>opStack=new Stack<>();
        //postfix存儲(chǔ)后綴表達(dá)式的,
        //其中有Integer和Character類型勘究,所以用Object作為泛型參數(shù)
        Queue<Object>postfix=new LinkedList<>();
        Scanner scanner=new Scanner(System.in);
        String myExp=scanner.nextLine();
        while(myExp!=null && !"end".equalsIgnoreCase(myExp)) {
            char[] charExp=myExp.toCharArray();
            //System.out.println(charExp);
            for(char charitem:charExp) {
                switch (charitem) {
                    case '1': case '2': case '3': case '4': case '5':
                    case '6': case '7': case '8': case '9': case '0':
                        numStack.push(Integer.parseInt(String.valueOf(charitem)));
                        break;
                    case '+': case '-': case '*':
                    case '/': case '(':case ')':
                        //在遇到操作符號(hào)之前彈出數(shù)字拼接
                        pushNumber2Result(numStack, postfix);
                        if(opStack.isEmpty()) {
                            opStack.push(charitem);
                        }else {
                            //對(duì)右括號(hào)特殊處理
                            if(charitem==')'&&!opStack.isEmpty()) {
                                while(opStack.peek()!='(') {
                                    postfix.add(opStack.pop());
                                }
                                //彈出左括號(hào)
                                opStack.pop();
                            }else if(charitem=='(') {
                                opStack.push(charitem);
                            }else {
                                //對(duì)非括號(hào)的符號(hào)輸入
                                //如果碰到左括號(hào)操作符號(hào)入棧
                                if(opStack.peek()=='(') {
                                    opStack.push(charitem);
                                }else if(priority.get(charitem)>priority.get(opStack.peek())) {
                                    //如果下一個(gè)操作符的優(yōu)先級(jí)大于棧頂?shù)膬?yōu)先級(jí)矮湘,
                                    //壓棧,此時(shí)棧頂一定有操作符
                                    opStack.push(charitem);
                                }else {
                                    //否則,出棧直至當(dāng)前操作符入棧
                                    //注意當(dāng)棧不空的情況下比較
                                    while(!opStack.isEmpty() && priority.get(charitem)                                          <=priority.get(opStack.peek())) {
                                        postfix.add(opStack.pop());
                                    }
                                    opStack.push(charitem);
                                }
                            }
                        }
                        
                }
            }
            pushNumber2Result(numStack, postfix);
            //注意剩余的操作符可能有多個(gè)
            while(!opStack.isEmpty()) {
                postfix.add(opStack.pop());
            }
            System.out.println(postfix);

其中口糕,pushNumber2Result函數(shù)的定義如下:

public static void pushNumber2Result(Stack<Integer>numStack, Queue<Object>result) {
        int num=0;
        int base=1;
        while(!numStack.isEmpty()) {
            num=num+numStack.pop()*base;
            base=base*10;
        }
        //注意如果num!=0才添加進(jìn)去
        if(num!=0)
            result.add(num);
    }

幾個(gè)值得注意的地方:

  1. 棧的循環(huán)操作注意判空
  2. 操作數(shù)的添加如果是0的話不應(yīng)該添加
  • 利用后綴表達(dá)式 計(jì)算表達(dá)式的值
            numStack.clear();
            while(!postfix.isEmpty()) {
                Object head=postfix.poll();
                if(head instanceof Integer) {
                    numStack.push((Integer)head);
                }else if(head instanceof Character){
                    Character op=(Character) head;
                    int num2=numStack.pop();
                    int num1=numStack.pop();
                    switch(op) {
                    case '+':
                        numStack.push(num1+num2);
                        break;
                    case '-':
                        numStack.push(num1-num2);
                        break;
                    case '*':
                        numStack.push(num1*num2);
                        break;
                    case '/':
                        numStack.push(num1/num2);
                        break;
                    }
                }
            }
            if(numStack.size()==1) {
                System.out.println("result:"+numStack.pop());
            }else {
                System.out.println("計(jì)算出錯(cuò)缅阳!");
            }
  • 測(cè)試結(jié)果
優(yōu)先級(jí):{*=2, +=1, -=1, /=2}
1+2*3+(1*2)
[1, 2, 3, *, +, 1, 2, *, +]
result:9
1+2*4-5/(1+1)
[1, 2, 4, *, +, 5, 1, 1, +, /, -]
result:7

大功告成!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末景描,一起剝皮案震驚了整個(gè)濱河市十办,隨后出現(xiàn)的幾起案子秀撇,更是在濱河造成了極大的恐慌,老刑警劉巖向族,帶你破解...
    沈念sama閱讀 211,348評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呵燕,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡件相,警方通過查閱死者的電腦和手機(jī)再扭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門层坠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來付材,“玉大人,你說我怎么就攤上這事们童『钛” “怎么了敦跌?”我有些...
    開封第一講書人閱讀 156,936評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)逛揩。 經(jīng)常有香客問我柠傍,道長(zhǎng),這世上最難降的妖魔是什么辩稽? 我笑而不...
    開封第一講書人閱讀 56,427評(píng)論 1 283
  • 正文 為了忘掉前任惧笛,我火速辦了婚禮,結(jié)果婚禮上逞泄,老公的妹妹穿的比我還像新娘患整。我一直安慰自己,他們只是感情好喷众,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評(píng)論 6 385
  • 文/花漫 我一把揭開白布各谚。 她就那樣靜靜地躺著,像睡著了一般到千。 火紅的嫁衣襯著肌膚如雪昌渤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,785評(píng)論 1 290
  • 那天憔四,我揣著相機(jī)與錄音膀息,去河邊找鬼。 笑死了赵,一個(gè)胖子當(dāng)著我的面吹牛潜支,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播柿汛,決...
    沈念sama閱讀 38,931評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼冗酿,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起已烤,我...
    開封第一講書人閱讀 37,696評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤鸠窗,失蹤者是張志新(化名)和其女友劉穎妓羊,沒想到半個(gè)月后胯究,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,141評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躁绸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評(píng)論 2 327
  • 正文 我和宋清朗相戀三年裕循,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片净刮。...
    茶點(diǎn)故事閱讀 38,625評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡剥哑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出淹父,到底是詐尸還是另有隱情株婴,我是刑警寧澤,帶...
    沈念sama閱讀 34,291評(píng)論 4 329
  • 正文 年R本政府宣布暑认,位于F島的核電站困介,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蘸际。R本人自食惡果不足惜座哩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望粮彤。 院中可真熱鬧根穷,春花似錦、人聲如沸导坟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惫周。三九已至尘惧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間闯两,已是汗流浹背褥伴。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留漾狼,地道東北人重慢。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像逊躁,于是被迫代替她去往敵國(guó)和親似踱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容