資料結構學習(C++)——棧應用(表示式求值) (轉)

worldblog發表於2008-01-21
資料結構學習(C++)——棧應用(表示式求值) (轉)[@more@]

棧的應用很廣泛,原書只講解了求值,那我也就只寫這些。其實,棧的最大的用途是解決回溯問題,這也包含了消解遞迴;而當你用棧解決回溯問題成了習慣的時候,你就很少想到用遞迴了,比如迷宮求解。另外,人的習慣也是先入為主的,比如樹的遍歷,從學的那天開始,就是遞迴演算法,雖然書上也教了用棧實現的方法,但應用的時候,你首先想到的還是遞迴;當然了,如果語言本身不支援遞迴(如BASIC),那棧就是唯一的選擇了——好像現在的高階語言都是支援遞迴的。

如下是表示式類的定義和實現,表示式可以是中綴表示也可以是字尾表示,用頭節點資料域裡的type區分,這裡有一點說明的是,由於單連結串列的賦值,我原來寫的時候沒有複製頭節點的內容,所以,要是在兩個表示式之間賦值,頭節點裡存的資訊就丟了。你可以改寫單連結串列的賦值函式來解決這個隱患,或者你根本不不在兩個表示式之間賦值也行。

#ifndef Expression_H:namespace prefix = o ns = "urn:schemas--com::office" />

#define Expression_H

 

#include "List.h"

#include "Stack.h"

 

#define INFIX 0

#define POSTFIX 1

#define OPND 4

#define OPTR 8

 

template class ExpNode

{

public:

  int type;

  union { Type opnd; char optr;};

  ExpNode() : type(INFIX), optr('=') {}

  ExpNode(Type opnd) : type(OPND), opnd(opnd) {}

  ExpNode(char optr) : type(OPTR), optr(optr) {}

};

 

template class Expression : List >

{

public:

  void Input()

  {

    MakeEmpty(); Get()->type =INFIX;

  cout << endl << "輸入表示式,以=結束輸入" << endl;

  Type opnd; char optr = ' ';

  while (optr != '=')

  {

    cin >> opnd;

  if (opnd != 0)

  {

    ExpNode newopnd(opnd);

    LastInsert(newopnd);

  }

    cin >> optr;

    ExpNode newoptr(optr);

    LastInsert(newoptr);

 

  }

  }

 

  void Print()

  {

    First();

  cout << endl;

  for (ExpNode *p = Next(); p != NULL; p = Next() )

  {

    switch (p->type)

  {

    case OPND:

    cout << p->opnd; break;

    case OPTR:

    cout << p->optr; break;

    default: break;

  }

    cout << ' ';

  }

  cout << endl;

  }

 

  Expression & Postfix() //將中綴表示式轉變為字尾表示式

  {

    First();

  if (Get()->type == POSTFIX) return *this;

    Stack s; s.Push('=');

    Expression temp;

    ExpNode *p = Next();

  while (p != NULL)

  {

    switch (p->type)

  {

    case OPND:

    temp.LastInsert(*p); p = Next(); break;

    case OPTR:

    while (isp(s.GetTop()) > icp(p->optr) )

    {

      ExpNode newoptr(s.Pop());

      temp.LastInsert(newoptr);

    }

    if (isp(s.GetTop()) == icp(p->optr) )

    {

      s.Pop(); p =Next(); break;

    }

    s.Push(p->optr); p = Next(); break;

    default: break;

  }

  }

  *this = temp;

    pGetFirst()->data.type = POSTFIX;

  return *this;

  }

 

  Type Calculate()

  {

    Expression temp = *this;

  if (pGetFirst()->data.type != POSTFIX) temp.Postfix();

    Stack s; Type left, right;

  for (ExpNode *p = temp.Next(); p != NULL; p = temp.Next())

  {

    switch (p->type)

  {

    case OPND:

    s.Push(p->opnd); break;

    case OPTR:

    right = s.Pop(); left = s.Pop();

    switch (p->optr)

    {

    case '+': s.Push(left + right); break;

    case '-': s.Push(left - right); break;

    case '*': s.Push(left * right); break;

    case '/': if (right != 0) s.Push(left/right); else return 0; break;

//    case '%': if (right != 0) s.Push(left%right); else return 0; break;

//    case '^': s.Push(Power(left, right)); break;

    default: break;

    }

    default: break;

  }

  }

  return s.Pop();

  }

 

private:

  int isp(char optr)

  {

  switch (optr)

  {

  case '=': return 0;

  case '(': return 1;

  case '^': return 7;

  case '*': return 5;

  case '/': return 5;

  case '%': return 5;

  case '+': return 3;

  case '-': return 3;

  case ')': return 8;

    default: return 0;

  }

  }

 

  int icp(char optr)

  {

  switch (optr)

  {

  case '=': return 0;

  case '(': return 8;

  case '^': return 6;

  case '*': return 4;

  case '/': return 4;

  case '%': return 4;

  case '+': return 2;

  case '-': return 2;

  case ')': return 1;

    default: return 0;

  }

  }

 

};

 

#endif

幾點說明

l  表示式用單連結串列儲存,你可以看到這個連結串列中既有運算元又有運算子,如果你看過我的《如何在一個連結串列中鏈入不同型別的》,這裡的方法也是對那篇文章的補充。

l  輸入表示式時,會將原來的內容清空,並且必須按照中綴表示輸入。如果你細看一下中綴表示式,你就會發現,除了括號,表示式的結構是“運算元”、“運算子”、“運算元”、……“運算子(=)”,為了統一這個規律,同時也為了使輸入函式簡單一點,規定括號必須這樣輸入“0(”、“)0”;這樣一來,“0”就不能作為運算元出現在表示式中了。因為我沒有在輸入函式中增加容錯的語句,所以一旦輸錯了,那就“死”了。

l  表示式求值的過程是,先變成字尾表示,然後用字尾表示求值。因為原書講解的是這兩個演算法,並且用這兩個演算法就能完成中綴表示式的求值,所以我就沒寫中綴表示式的直接求值演算法。具體演算法說明參見原書,我就不廢話了。

l  Calculate()註釋掉的兩行,“%”是因為只對整型表示式合法,“^”的Power()函式沒有完成。

l  isp(),icp()的返回值,原書說的不細,我來多說兩句。‘=’(表示式開始和結束標誌)的棧內棧外優先順序都是最低。‘(’棧外最高,棧內次最低。‘)’棧外次最低,不進棧。‘^’棧內次最高,棧外比棧內低。‘×÷%’棧內比‘^’棧外低,棧外比棧內低。‘+-’棧內比‘×’棧外低,棧外比棧內低。這樣,綜合起來,就有9個優先順序,於是就得出了書上的那個表。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-997979/,如需轉載,請註明出處,否則將追究法律責任。

相關文章