データ構造演習のスタックとキュー:算術式から後置式への変換(C言語の実装)

コンテンツ

I.はじめに

2.問題分析

3.アプリケーション例-一般的な算術式は接尾辞式に変換されます

(1)スタックの基本的な操作機能の定義

(2)主な機能

(3)運転結果

 (4)、完全なコード

(5)まとめ


I.はじめに:

       この関数をコードで実装するのに1日かかりました。プロセスは非常に苦痛で、多くのエラーがありました。多分私は比較的良かったので、デバッグするのに1日かかりました。ことわざにあるように、間違いを犯すことは自己改善の始まりです。エラーがなければ問題は見つかりません。問題を表示し、分析し、解決するだけで、何かを得て改善することができます。プロセスは困難ですが、問題が解決した瞬間、宝くじに当選するようなものです。 。算術式を接尾辞式に変換することは、スタックの古典的なアプリケーションです。アイデアは同じで、このアイデアをマスターし、このアイデアを使用してすべての算術式をサフィックス式に変換でき、コードはユニバーサルです。

転載の際は、この記事のリンクをご記入ください!

第二に、問題分析:

       算術式とは:a * b +(cd / e)*fのような私たちが通常行う数学的問題の式です。

       接尾辞式とは:オペランドの後に演算子を置きます。上記の式に対応する接尾辞式は次のとおりです。ab* cde / -f * +、前の演算子が最初に実行されます。それ以外の場合、オペランドの相対的な順序は変更されず、括弧は接尾辞式に含まれません。

        コンピューターが私たちの考えやコンピューターに何をしてほしいかを理解できるように、条件を設定する方法を分析する必要があります。一目でいくつかのことを見ることができますが、コンピューターは私たちに従う前に指示を伝える必要があります。 。働くためのアイデア。

        スタックの場合、スタックがfirst-in-last-out配列であり、スタックの最上位で許可されるのはポップおよびプッシュ操作のみであることを最初に知る必要があります。配列にはサイズが必要です。最初に、入力算術式を格納するのに十分な大きさの配列を定義できます。次に、スタック構造を定義します。スタックS1は、最終的な接尾辞式を格納するために使用されます。スタックS1の位置は、接尾辞式の逆の順序です。配列を使用して、出力時にこのスタックS1の要素を取得できます。 from最後のビットを前方に出力できます。配列の代わりにスタックS1を使用してサフィックス式を格納するのはなぜですか?スタックは操作が非常に便利なので、スタックのポップやプッシュなどの関数を呼び出すだけで済みます。もちろん、アレイ直接ストレージを使用することもできます。別のスタックS2は、演算子を算術式に格納するために使用され、スタックを最初にポップする演算子が最初に演算を実行します。したがって、S2のオペレーターがいつスタックからポップされるか、および現在スキャンされているオペレーターがいつスタックにプッシュされるかを決定するために、いくつかの条件を設定する必要があります。 

        現在の算術式をスキャンして、現在の文字Cを取得します。スタックにポップするための次のルールがあります。

(1)左角かっこc ='('の場合、後置式のスタックS2に直接格納します。

        //如果是左括号,直接进栈
        if(c=='(')
        {
            Push(&S2,c);
        }

(2)c =')'の閉じ括弧の場合、現在のストレージ演算子のスタックS2の一番上の要素を取得します。左の括弧'('と等しくない場合、スタックS2のスタックの一番上の要素はに進みます。 S1、次にS2はポップアウト操作を実行し、次にスタックS2の最上位要素を再度取得し、ステップ(2)を実行し続け、ループを実行します。'('左括弧が初めて検出されるまで、ループが停止し、'('がS2から削除されます。スタックからポップして、算術式の次の文字のスキャンを続行します。

        else if(c==')')
        {
            char b=Get(S2);//获取存操作符的栈顶元素
            while(b!='(')//遇见左括号之前一直循环出栈S2
            {
                Push(&S1,b);//存入到栈S1
                Pop(&S2);    //进行出栈S2
                b=Get(S2);//再获取栈顶S2的元素
                //printf("--------++%c\n",b);
            }
            Pop(&S2);

(3)c!='+'およびc!='-'およびc!='*'およびc!='/'およびc!=')'の場合。次に、それを後置式のスタックS1に直接格納します。ここで、cは数値であるか、cは一連の未知の変数xであることが理解できます。算術式の次の文字のスキャンを続行します。

        else if(c!='+'&&c!='-'&&c!='*'&&c!='/'&&c!=')')//根据题目变换
        {
            Push(&S1,c);
        }

(4)c =='+'またはc=='-'の場合、つまり、加算と減算の2つの演算子に遭遇した場合、最初にスタックS2が空のスタックであるかどうかを判断します。スタック、次に、現在の演算子cをスタックS2に直接入れることができます。空でない場合は、スタックS2の最上位要素を取得します。左角かっこ'('の場合、現在の演算子cをスタックS2に直接入れることができます。左角かっこ'('でない場合は、スタックS2の一番上の要素を最初にS1にスタックします。次に、S2にポップ操作を実行させてから、スタックS2の最上位要素を再度取得し、このステップ(4)をループで実行して、'('左括弧が初めて検出されるか、スタックが空の場合、ループは停止し、'(' S2からスタックをポップして、算術式の次の文字をスキャンし続けます。左角かっこが見つからない場合は、スタックが空になるまでスタックをポップし続けます。次に、cをスタックS2にプッシュします。引き続き、数式の次の文字の算術式をスキャンします。

        else if(c=='+'||c=='-')
        {
            if(S2.top==-1)         //如果操作符栈为空,直接进栈
            {
                Push(&S2,c);
            }
            else
            {
                b=Get(S2);
                while(b!='(')         //只要不为左括号或者是栈不为空,那就一直出栈
                {
                    Push(&S1,b);         //运算符存入到栈S1中
                    Pop(&S2);
                    if(S2.top!=-1)            //栈不为空,执行一次出栈
                    {
                        b=Get(S2);           //再获取栈顶
                    }
                    else
                    {
                        break;
                    }
                }
                Push(&S2,c);
            }

        }

(5)c =='*' || c =='/'の場合、つまり、乗算と除算の2つの演算子に遭遇した場合、最初にスタックS2が空のスタックであるかどうかを判断します。スタックの場合、現在の演算子cをスタックS2に入れるだけです。空でない場合、スタックS2の最上位要素を取得します。b!='('('and b!=' +'and b!='-')の場合、ここで使用され、使用されません。問題。これら3つが当てはまらない場合にのみ、同時に真になります。現在のS2の最上位要素をスタックS1にプッシュし、スタックS2がポップ操作を実行してから、スタックS2を取得します。 S2を再度スタックします。スタックが空になるか、スタックの一番上の要素が上記の3つのうちのいずれかに等しくなるまで、一番上の要素を繰り返します(5)。ループを停止し、現在の演算子cをスタックS2に入れます。続行します。次の文字の算術式をスキャンします。

    else if(c=='*'||c=='/')
        {
            if(S2.top==-1)
            {
               Push(&S2,c);
            }
            else
            {
                b=Get(S2);
                while(b!='('&&b!='+'&&b!='-')
                {
                    Push(&S1,b);          //运算符存入到栈S1中
                    Pop(&S2);
                    if(S2.top!=-1)             //执行一次出栈
                    {
                       b=Get(S2);
                    }
                    else
                    {
                        break;
                    }
                }
                Push(&S2,c);
            }
        }

(6)算術式の配列をスキャンすると、スタックS2に格納されている演算子がスタックからポップされ、スタックS1に順番に入力されます。このとき、スタックS1の順序は、接尾辞式の逆の順序になります。この逆の順序を配列に格納し、この配列の出力を逆にすると、必要なサフィックス式になります。

    while(S2.top!=-1)      //将S2中剩余的操作符存入到栈S1中
    {
        char c=Get(S2);
        Push(&S1,c);
        Pop(&S2);
    }
    int a=S1.top;           //记录后缀表达式的长度
    for(int i=0;S1.data[S1.top]!=-1;i++)   //将栈S1中的元素存入到数组中
    {
        data1[i]=S1.data[S1.top];
        S1.top--;
    }
    for(int i=a;i>=0;i--)             //反向输出数组即可
    {
        printf("%c",data1[i]);
    }

次の図は、算術式を接尾辞式に変換するためのLi Chunbaoの本に記載されている手順です。私の理解するか、次の図を理解することができます。

3.アプリケーション例-一般的な算術式は接尾辞式に変換されます

説明

二項演算子に基づく算術式の場合は、対応する接尾辞式に変換して出力します。

入力

'#'文字で終了する算術式を入力します。

出力

式を変換して得られた接尾式を出力します。

サンプル

入力 

a * b +(cd / e)* f#

出力 

ab * cde / -f * +

(1)スタックの基本的な操作機能の定義

//定义一个栈结构体
typedef struct
{
    char data[Maxsize];    //存储元素
    int top;                //栈顶标记位
}Stack;
//初始化
void Init(Stack *L)
{
    L->top=-1;
}
//进栈操作
void Push(Stack *L,char x)
{
    if(L->top>=Maxsize)//判断是否栈满
    {
        return;
    }
    L->top++;
    L->data[L->top]=x;      //栈没满就将x入栈
}
//出栈操作
void Pop(Stack *L)
{
    if(L->top==-1)//判断是否为空
    {
        return;
    }
    L->top--;        //不空就将栈顶标记位减一
}
//获取栈顶元素
char Get(Stack L)
{
    if(L.top==-1)
    {
        return 0;
    }
    else
    {
        return L.data[L.top];
    }
}

(2)主な機能

main関数のコードブロックの意味は、問題分析の2番目の部分で詳細に紹介されています。このコードを収集してこれらの単語を理解でき、ロジックは非常に単純です。

int main()
{
    Stack S1;//存放最终的表达式
    Stack S2;//存放运算符
    Init(&S1);
    Init(&S2);
    char data[Maxsize];//存放用户输入的表达式
    scanf("%s",data);
    char data1[Maxsize];
    char b;
    for(int i=0;data[i]!='#';i++)
    {
        char c=data[i];
        if(c=='(')
        {
            Push(&S2,c);
        }
        else if(c==')')
        {
            char b=Get(S2);//获取存操作符的栈顶元素
            while(b!='(')//遇见左括号之前一直循环出栈S2
            {
                Push(&S1,b);//存入到栈S1
                Pop(&S2);    //进行出栈S2
                b=Get(S2);//再获取栈顶S2的元素
            }
            Pop(&S2);
        }
        else if(c!='+'&&c!='-'&&c!='*'&&c!='/'&&c!=')')//根据题目变换
        {
            Push(&S1,c);
        }
        else if(c=='+'||c=='-')
        {
            if(S2.top==-1)         //如果操作符栈为空,直接进栈
            {
                Push(&S2,c);
            }
            else
            {
                b=Get(S2);
                while(b!='(')         //只要不为左括号或者是栈不为空,那就一直出栈
                {
                    Push(&S1,b);         //运算符存入到栈S1中
                    Pop(&S2);
                    if(S2.top!=-1)            //栈不为空,执行一次出栈
                    {
                        b=Get(S2);           //再获取栈顶
                    }
                    else
                    {
                        break;
                    }
                }
                Push(&S2,c);
            }

        }
        else if(c=='*'||c=='/')
        {
            if(S2.top==-1)
            {
               Push(&S2,c);
            }
            else
            {
                b=Get(S2);
                while(b!='('&&b!='+'&&b!='-')
                {
                    Push(&S1,b);          //运算符存入到栈S1中
                    Pop(&S2);
                    if(S2.top!=-1)             //执行一次出栈
                    {
                       b=Get(S2);
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }
    }
    while(S2.top!=-1)
    {
        char c=Get(S2);
        Push(&S1,c);
        Pop(&S2);
    }
    int a=S1.top;
    for(int i=0;S1.data[S1.top]!=-1;i++)
    {
        data1[i]=S1.data[S1.top];
        S1.top--;
    }
    for(int i=a;i>=0;i--)
    {
        printf("%c",data1[i]);
    }
    return 0;
}

(3)運転結果

 (4)、完全なコード

#include <stdio.h>
#include <stdlib.h>
#define Maxsize 1000
//定义一个栈结构体
typedef struct
{
    char data[Maxsize];    //存储元素
    int top;                //栈顶标记位
}Stack;
//初始化
void Init(Stack *L)
{
    L->top=-1;
}
//进栈操作
void Push(Stack *L,char x)
{
    if(L->top>=Maxsize)//判断是否栈满
    {
        return;
    }
    L->top++;
    L->data[L->top]=x;      //栈没满就将x入栈
}
//出栈操作
void Pop(Stack *L)
{
    if(L->top==-1)//判断是否为空
    {
        return;
    }
    L->top--;        //不空就将栈顶标记位减一
}
//获取栈顶元素
char Get(Stack L)
{
    if(L.top==-1)
    {
        return 0;
    }
    else
    {
        return L.data[L.top];
    }
}
int main()
{
    Stack S1;//存放最终的表达式
    Stack S2;//存放运算符
    Init(&S1);
    Init(&S2);
    char data[Maxsize];//存放用户输入的表达式
    scanf("%s",data);
    char data1[Maxsize];
    char b;
    for(int i=0;data[i]!='#';i++)
    {
        char c=data[i];
        if(c=='(')
        {
            Push(&S2,c);
        }
        else if(c==')')
        {
            char b=Get(S2);//获取存操作符的栈顶元素
            while(b!='(')//遇见左括号之前一直循环出栈S2
            {
                Push(&S1,b);//存入到栈S1
                Pop(&S2);    //进行出栈S2
                b=Get(S2);//再获取栈顶S2的元素
            }
            Pop(&S2);
        }
        else if(c!='+'&&c!='-'&&c!='*'&&c!='/'&&c!=')')//根据题目变换
        {
            Push(&S1,c);
        }
        else if(c=='+'||c=='-')
        {
            if(S2.top==-1)         //如果操作符栈为空,直接进栈
            {
                Push(&S2,c);
            }
            else
            {
                b=Get(S2);
                while(b!='(')         //只要不为左括号或者是栈不为空,那就一直出栈
                {
                    Push(&S1,b);         //运算符存入到栈S1中
                    Pop(&S2);
                    if(S2.top!=-1)            //栈不为空,执行一次出栈
                    {
                        b=Get(S2);           //再获取栈顶
                    }
                    else
                    {
                        break;
                    }
                }
                Push(&S2,c);
            }

        }
        else if(c=='*'||c=='/')
        {
            if(S2.top==-1)
            {
               Push(&S2,c);
            }
            else
            {
                b=Get(S2);
                while(b!='('&&b!='+'&&b!='-')
                {
                    Push(&S1,b);          //运算符存入到栈S1中
                    Pop(&S2);
                    if(S2.top!=-1)             //执行一次出栈
                    {
                       b=Get(S2);
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }
    }
    while(S2.top!=-1)
    {
        char c=Get(S2);
        Push(&S1,c);
        Pop(&S2);
    }
    int a=S1.top;
    for(int i=0;S1.data[S1.top]!=-1;i++)
    {
        data1[i]=S1.data[S1.top];
        S1.top--;
    }
    for(int i=a;i>=0;i--)
    {
        printf("%c",data1[i]);
    }
    return 0;
}

(5)まとめ

        この質問には1日かかりました。実際に少しずつデバッグしました。各条件にprintステートメントを追加して、どの条件が間違っているかを確認し、最終的に、この判断で問題が発生したと判断しました。これをスキャンするには、このb!='(' && b!='+' && b!='-'条件を定義する必要があります。b!='(' || b!='+' || b!='- '、&&はすべてtrueがtrueであることを意味し、||は、1つがtrueである限り、trueであり、条件の条件が実行されることを意味します。ループ本体。たとえば、a +( b * c + d)#、閉じ括弧までスキャンする場合)、b!='(' || b!='+' || b!='-'の条件を使用する場合、 *記号の場合、演算子スタックの一番上にある左括弧b =(に到達し、b!='(' || b!='+' | | b!='-'をaとして使用する場合条件、この条件はtrueです。これは、この時点でbが+、-と等しくないためです。したがって、内部のコンテンツが実行されます。ただし、b =(の場合、ポップではなく*をスタックに直接プッシュします。それ。

        今日はこの質問をしましたが、本当に頑張って何かを得ることができました。判断条件を設定するときは、プラス面とマイナス面の両方を考慮する必要があります。条件が正しく設定されている場合にのみ、プログラムを希望どおりに実行できます。実行する方向です。来て!

おすすめ

転載: blog.csdn.net/BaoITcore/article/details/121456313