简体   繁体   English

使用 C/C++ 中缀前缀(不使用堆栈)

[英]infix to prefix using C/C++ (without using stack)

In compiler designing they gave us a project to write an "infix to prefix" program using C/C++ but we are not allowed to use the stack method.在编译器设计中,他们给了我们一个项目来使用 C/C++ 编写“中缀前缀”程序,但我们不允许使用堆栈方法。 i know it is possible to reverse the postfix to get prefix but the problem is that the program that is written in the book to convert infix to postfix doesn't work.我知道可以反转后缀以获取前缀,但问题是书中编写的将中缀转换为后缀的程序不起作用。 this it the "infix to postfix" in the book这是书中的“后缀中缀”

#include <stdio.h>
#include <ctype.h>
#define NUM 256
#define NONE -1
int lineno = 1;
int tokenval = NONE;
int lexan()
{
    int t,
    while (1)
    {
        t = getchar();
        if (t = = 0)
            | | (t = = '\t');
        else if (t = = '\n')
            lineno = lineno + 1;
        else if (isdigit(t)) {
            tokenval = t - '0';
            t = getchar();
            while (isdigit(t)) {
                tokenval = tokenval * 10 + t - '0';
                t = getchar();
            }
            ungetc(t, stdin) return NUM;
        }
        else {
            tokenval = NONE;
            return t;
        }
    }
} 

So, no build in stack is allowed.因此,不允许内置堆栈。

Then let us build a different container.然后让我们构建一个不同的容器。 I will show you a dynamic array, with some special functions, to我将向您展示一个具有一些特殊功能的动态数组

  • check, if there is something in the container检查容器中是否有东西
  • add data add the end of the container添加数据添加容器的末尾
  • read data from the end of the container从容器末端读取数据
  • remove the last element from the container从容器中删除最后一个元素
  • and a combination of read and remove以及读取和删除的组合

Because obviously you are not allowed to use a C++ standard container, I created this dynamic container, with dynamic memory handling in it.因为显然不允许使用 C++ 标准容器,所以我创建了这个动态容器,其中包含动态 memory 处理。 In normal life, you should never use such a thing, but C++ elements.在正常的生活中,你不应该使用这样的东西,而是 C++ 元素。

It could look like:它可能看起来像:

// This is the thing that we need for our operation
struct TheOtherThing {
    char* data{};
    unsigned int numberOfElements{};
    unsigned int capacity{ 2 };

    ~TheOtherThing() { delete[] data; }
    TheOtherThing() { data = new char[capacity]; }

    void atAtEnd(const char c) {
        // First check, if this element still fits on the TheOtherThing
        if (numberOfElements >= capacity) {

            // Oh no, TheOtherThing capacity is exhausted. Allocate new memory. Now with double the capacity than before
            capacity = capacity * 2;

            // Allocate new temporary, but bigger memory
            char* temp = new char[capacity];

            // Copy all data from old memory to new temporary memory
            for (unsigned int k = 0; k < numberOfElements; ++k) {
                temp[k] = data[k];
            }
            // Release memory from old data
            delete[] data;

            // And assign new temporary data to old pointer
            data = temp;
        }
        // And now, after having done all this memory handling, store the value at the end of the dynamic array.
        data[numberOfElements] = c;

        // Remember that we have one more element on the TheOtherThing
        ++numberOfElements;
    }
    // Typical Needed functions to deal with TheOtherThing
    bool hasData() const { return numberOfElements != 0; }
    char lastElement() const { return (hasData() ?  data[numberOfElements - 1] : '\0'); }
    void removeFromEnd() { if (hasData()) --numberOfElements; }
    char getAndRemoveFromEnd() { char result = '\0'; if (hasData()) { --numberOfElements; result = data[numberOfElements]; } return result; }
};

The attentive reader will notice that this is nothing but a stack.细心的读者会注意到,这不过是一个堆栈。

Infix to postfix is a rather simple operation.后缀中缀是一个相当简单的操作。

  • All digits will be taken and output as is.所有数字都将被取走,output 原样。
  • operators will be put on stack and taken out in the crrect order, depending on operator precedence.运算符将被放入堆栈并以正确的顺序取出,具体取决于运算符的优先级。

Totally simple.完全简单。

Code could then look like this:代码可能如下所示:

#include <string>
#include <iostream>

// This is the thing that we need for our operation
struct TheOtherThing {
    char* data{};
    unsigned int numberOfElements{};
    unsigned int capacity{ 2 };

    ~TheOtherThing() { delete[] data; }
    TheOtherThing() { data = new char[capacity]; }

    void atAtEnd(const char c) {
        // First check, if this element still fits on the TheOtherThing
        if (numberOfElements >= capacity) {

            // Oh no, TheOtherThing capacity is exhausted. Allocate new memory. Now with double the capacity than before
            capacity = capacity * 2;

            // Allocate new temporary, but bigger memory
            char* temp = new char[capacity];

            // Copy all data from old memory to new temporary memory
            for (unsigned int k = 0; k < numberOfElements; ++k) {
                temp[k] = data[k];
            }
            // Release memory from old data
            delete[] data;

            // And assign new temporary data to old pointer
            data = temp;
        }
        // And now, after having done all this memory handling, store the value at the end of the dynamic array.
        data[numberOfElements] = c;

        // Remember that we have one more element on the TheOtherThing
        ++numberOfElements;
    }
    // Typical Needed functions to deal with TheOtherThing
    bool hasData() const { return numberOfElements != 0; }
    char lastElement() const { return (hasData() ?  data[numberOfElements - 1] : '\0'); }
    void removeFromEnd() { if (hasData()) --numberOfElements; }
    char getAndRemoveFromEnd() { char result = '\0'; if (hasData()) { --numberOfElements; result = data[numberOfElements]; } return result; }
};

std::string infixToPostfix(std::string& infix) {

    // Here we will store the resulting postfix expession
    std::string postfix{};

    // This we will use as a helper
    TheOtherThing tot{};

    // Check all characters of the infix expression in a loop
    for (const char c : infix) {
        // Handle all kind of characters

        if (c >= '0' and c <= '9') postfix += c;                // Digit, take as is
        else if (c == '+' or c == '-' or c == '*' or c == '/' or c == '^') {    // Check for operator

            // If this charcter (and as long) has a lower precedence then the operator on the top of the thing
            while (tot.hasData() and ( (c == '+' or c == '-') or ((c == '*' or c == '/') and (tot.lastElement() == '^'))))
                // Then add it to the resulting string
                postfix += tot.getAndRemoveFromEnd();
            // Put new operator on the thing
            tot.atAtEnd(c);
        }
        else std::cerr << "Error: Invalid character '" << c << "' found. Ignoring . . .\n";
    }
    // If there is still something on the thing
    while (tot.hasData())
        postfix += tot.getAndRemoveFromEnd();

    // And return resulting postfix expression
    return postfix;
}

int main() {
    std::string infix{ "1+2-3*4/5^6" };
    std::cout << "\n\nInfix:  '" << infix << "'   --> postfix:  '" << infixToPostfix(infix) << "'\n\n";
}

But, even a simple string has the functionality of a stack.但是,即使是简单的字符串也具有堆栈的功能。 It has all necessary functions.它具有所有必要的功能。

So, we could also write:所以,我们也可以这样写:

#include <string>
#include <iostream>

std::string infixToPostfix(std::string& infix) {

    // Here we will store the resulting postfix expession
    std::string postfix{};

    // This we will use as a helper
    std::string tot{};

    // Check all characters of the infix expression in a loop
    for (const char c : infix) {
        // Handle all kind of characters

        if (c >= '0' and c <= '9') postfix += c;                               // Digit, take as is
        else if (c == '+' or c == '-' or c == '*' or c == '/' or c == '^') {   // Check for operator

            // If this charcter (and as long) has a lower precedence then the operator on the top of the thing
            while (not tot.empty() and ((c == '+' or c == '-') or ((c == '*' or c == '/') and (tot.back() == '^')))) {
                // Then add it to the resulting string
                postfix += tot.back();
                tot.pop_back();
            }
            // Put new operator on the thing
            tot.push_back(c);
        }
        else std::cerr << "Error: Invalid character '" << c << "' found. Ignoring . . .\n";
    }
    // If there is still something on the thing
        while (not tot.empty()) {
            postfix += tot.back();
            tot.pop_back();
        }

    // And return resulting postfix expression
    return postfix;
}

int main() {
    std::string infix{ "1+2-3*4/5^6" };
    std::cout << "\n\nInfix:  '" << infix << "'   --> postfix:  '" << infixToPostfix(infix) << "'\n\n";
}

But, oh shock.但是,哦,震惊。 Since we are still on C and not on C++, we do not even have a std::string .由于我们仍在 C 而不是 C++ 上,我们甚至没有std::string But, luckily, we have our other thing, which is also a dynamic string.但是,幸运的是,我们还有另一个东西,它也是一个动态字符串。

With that and without any C++ container, we can have:有了它并且没有任何 C++ 容器,我们可以拥有:

#include <iostream>

// This is the thing that we need for our operation
struct TheOtherThing {
    char* data{};
    unsigned int numberOfElements{};
    unsigned int capacity{ 2 };

    ~TheOtherThing() { delete[] data; }
    TheOtherThing() { data = new char[capacity]; }

    void atAtEnd(const char c) {
        // First check, if this element still fits on the TheOtherThing
        if (numberOfElements >= capacity) {

            // Oh no, TheOtherThing capacity is exhausted. Allocate new memory. Now with double the capacity than before
            capacity = capacity * 2;

            // Allocate new temporary, but bigger memory
            char* temp = new char[capacity];

            // Copy all data from old memory to new temporary memory
            for (unsigned int k = 0; k < numberOfElements; ++k) {
                temp[k] = data[k];
            }
            // Release memory from old data
            delete[] data;

            // And assign new temporary data to old pointer
            data = temp;
        }
        // And now, after having done all this memory handling, store the value at the end of the dynamic array.
        data[numberOfElements] = c;

        // Remember that we have one more element on the TheOtherThing
        ++numberOfElements;
    }
    // Typical Needed functions to deal with TheOtherThing
    bool hasData() const { return numberOfElements != 0; }
    char lastElement() const { return (hasData() ? data[numberOfElements - 1] : '\0'); }
    void removeFromEnd() { if (hasData()) --numberOfElements; }
    char getAndRemoveFromEnd() { char result = '\0'; if (hasData()) { --numberOfElements; result = data[numberOfElements]; } return result; }
    void operator += (const char c) { 
        atAtEnd(c); }
};

void infixToPostfix(char* infix, TheOtherThing *postfix) {

    // This we will use as a helper
    TheOtherThing tot{};
    char c;

    // Check all characters of the infix expression in a loop
    while (c = *infix++) {   // Be very carefule here. That is dirty with side effect

        // Handle all kind of characters
        if (c >= '0' and c <= '9') *postfix += c;                                // Digit, take as is
        else if (c == '+' or c == '-' or c == '*' or c == '/' or c == '^') {    // Check for operator

            // If this charcter (and as long) has a lower precedence then the operator on the top of the thing
            while (tot.hasData() and ((c == '+' or c == '-') or ((c == '*' or c == '/') and (tot.lastElement() == '^'))))
                // Then add it to the resulting string
                *postfix += tot.getAndRemoveFromEnd();
            // Put new operator on the thing
            tot.atAtEnd(c);
        }
        else std::cerr << "Error: Invalid character '" << c << "' found. Ignoring . . .\n";
    }
    // If there is still something on the thing
    while (tot.hasData())
        *postfix += tot.getAndRemoveFromEnd();
    *postfix += '\0';
}

int main() {
    char infix[]="1+2-3*4/5^6";
    TheOtherThing postfix{};
    infixToPostfix(infix, &postfix);
    std::cout << "\n\nInfix:  '" << infix << "'   --> postfix:  '" << postfix.data << "'\n\n";
}

Nice?好的? Isn't it?不是吗?

The approach for converting an infix to a prefix expression is:将中缀转换为前缀表达式的方法是:

  1. Reverse the input string反转输入字符串
  2. Convert the reversed input string to a postfix expression将反转的输入字符串转换为后缀表达式
  3. Reverse the postfix expression to get the prefix expression反转后缀表达式得到前缀表达式

If you would be allowed to use a std::stack , then things would be easy.如果允许您使用std::stack ,那么事情会很容易。 Converting the infix to postfix is a well know algorithm.将中缀转换为后缀是一种众所周知的算法。

Using a stack you would:使用堆栈,您将:

  • ignore spaces忽略空格
  • output digits and letters as is output 数字和字母原样
  • push opening brackets on the stack推动堆栈上的开口支架
  • If a closing bracket comes, pop from the stack, add to output, unitl matching bracket comes如果来了右括号,则从堆栈中弹出,添加到 output,unitl 匹配括号来
  • for operators, pop from stack accoding to their precedence, then push new operator on stack对于运算符,根据其优先级从堆栈中弹出,然后将新运算符推入堆栈

Rather simple.相当简单。

But, you have no stack.但是,你没有堆栈。

So, we need to use a different algorithm.所以,我们需要使用不同的算法。 Basically, we can use any array.基本上,我们可以使用任何数组。 But since we do not know in advance, how many elements are in the array, we will use something dynamic.但是由于我们事先不知道数组中有多少元素,我们将使用动态的东西。 Normally you could use a std::vector or a std::deque or similar.通常你可以使用std::vectorstd::deque或类似的。 Even a `std::string has´ all the functionality that you need.即使是 `std::string` 也有你需要的所有功能。

You always need the same functionality fromsuch a container您总是需要来自此类容器的相同功能

  • Put something at the end of the container (push)在容器的末端放一些东西(推)
  • Read from the end (top)从最后阅读(顶部)
  • Remove from the end (pop)从末端移除(pop)
  • Get the information about the number of elements in the container, or, if it is empty获取有关容器中元素数量的信息,或者,如果它为空
  • There may be additional convenience functions可能有额外的便利功能

We can easily create a small class that fulfils all of the above requirements.我们可以轻松创建满足上述所有要求的小型 class。 It can be used to store dynamic length strings and also other data.它可用于存储动态长度字符串以及其他数据。 The mechanism is the same.机制是一样的。

Basic functionality:基本功能:

  • The container has a capacity (meaning, it could store that many elements, although tere may be less, even 0 elements in it)容器有容量(意思是,它可以存储那么多元素,虽然 tere 可能更少,甚至是 0 个元素)
  • There is a pointer to the data有一个指向数据的指针
  • If memory is needed, the we allocate new memory via new .如果需要 memory,我们通过new分配新的 memory。 (And we double the capacity) (我们将容量翻倍)

Putting things together, one of many possible solutions could be like the below把事情放在一起,许多可能的解决方案之一可能如下所示

#include <iostream>

// This is a dynamic array that we need for our operation
class DynamicArray {

private:
    // This is a pointer to the dynmic allocated char array
    char* data{};

    // This is the number of Elements that are currently stored in our dynamic array
    unsigned int numberOfElements = 0;

    // This is the capacity of our dyanmic array. So many elements can be strored without any reallocation
    unsigned int capacity = 8;

    // Some helpwer functions for swapping 2 data
    void swap(char& c1, char& c2) { char temp = c1; c1 = c2; c2 = temp; }

public:

    // --------------------------------------------------------------------------------------------------------
    // Constructor / destructor 
    // 
    // Default constructor
    DynamicArray() { data = new char[capacity]; }
    // Copy contructor. Copy data from other dynamic array
    DynamicArray(const DynamicArray& da) { data = new char[capacity]; for (unsigned int i = 0; i < da.numberOfElements; ++i) *this += da.data[i]; };

    // Special constructor to copy data from a C-style string
    DynamicArray(const char* str) {data = new char[capacity]; for (unsigned int i = 0; str[i] != '\0'; ++i) *this += str[i]; };

    // The destructor will release the dynamically allocated memory
    ~DynamicArray() { delete[] data; }

    // --------------------------------------------------------------------------------------------------------
    // Typical needed functions to deal with DynamicArray

    // We will use this function to add data to our dynamic array. Here dynamic memory management will be done
    void addAtEnd(const char c);

    // Check, if we have data stored at allo in our dynmic array
    bool hasData() const { return numberOfElements != 0; }

    // Get a copy of the last element in the dynamic array
    char lastElement() const { return (hasData() ? data[numberOfElements - 1] : '\0'); }

    // Remove the last element from the dynamic array. In reality we will remove nothing, but just decrease the number of elements
    void removeFromEnd() { if (hasData()) --numberOfElements; }
    
    // Make a copy of the last element and then remove the last element
    char getAndRemoveFromEnd() { char result = '\0'; if (hasData()) { --numberOfElements; result = data[numberOfElements]; } return result; }

    // Reverse the data in the internal respresentation of or dynamic memory
    void reverse() { for (unsigned int k = 0; k < numberOfElements / 2; ++k) swap(data[k], data[numberOfElements-k-1]); }

    // Indicates, how many elements are currently stored in our dynamic array
    unsigned int length() const { return numberOfElements; } 

    // --------------------------------------------------------------------------------------------------------
    // Overwrite some operators for easier handling

    // Index operator. Access element at position k. Attention: No range check
    char& operator [](unsigned int k) { return data[k]; }

    // Assignment operator for a whole dynamic array
    DynamicArray& operator = (DynamicArray& t) {this->numberOfElements = 0; for (unsigned int k = 0; k < t.numberOfElements; ++k) *this += t.data[k]; return*this;  }

    // operator to add element at end. Convinience function
    void operator += (const char c) { addAtEnd(c); }

    // Output the data to a stream
    friend std::ostream& operator << (std::ostream& os, const DynamicArray& t) { for (unsigned int k = 0; k < t.numberOfElements; ++k) std::cout << t.data[k]; return os; }
};

void DynamicArray::addAtEnd(char c) {
    // First check, if this element still fits on the DynamicArray
    if (numberOfElements >= capacity) {

        // Oh no, DynamicArray capacity is exhausted. Allocate new memory. Now with double the capacity than before
        capacity = capacity * 2;

        // Allocate new temporary, but bigger memory
        char* temp = new char[capacity];

        // Copy all data from old memory to new temporary memory
        for (unsigned int k = 0; k < numberOfElements; ++k) {
            temp[k] = data[k];
        }
        // Release memory from old data
        delete[] data;

        // And assign new temporary data to old pointer
        data = temp;
    }
    // And now, after having done all this memory handling, store the value at the end of the dynamic array.
    data[numberOfElements] = c;

    // Remember that we have one more element on the DynamicArray
    ++numberOfElements;
}

// --------------------------------------------------------------------------------
// Main conversion function

void infixToPrefix(DynamicArray& infix, DynamicArray *prefix) {

    // This we will use as a helper
    DynamicArray da{};
    char c;

    // ---------------------------------------------------------------------------
    // 1. We want toreverse the string given as input
    DynamicArray infixReversed( infix );
    infixReversed.reverse();

    // Invert brackets: Exchange '(' and ')', becuase we reversed the string
    for (unsigned int k = 0; k < infixReversed.length(); ++k) {
        if (infixReversed[k] == '(')  infixReversed[k] = ')';
        else if (infixReversed[k] == ')')  infixReversed[k] = '(';
    }
    
    // ---------------------------------------------------------------------------
    // 2. Convert to postfix
    // Here we will store the the resultr of post fix expression
    DynamicArray postfix;

    // Check all characters of the reveresed infix expression in a loop
    for (unsigned int k = 0; k < infixReversed.length(); ++k) {

        // Get current character
        c = infixReversed[k];

        // Handle all kind of characters
        if (c == ' ') continue;                                              // Ignore spaces
                                                                             // Literal, take as is
        if ((c >= '0' and c <= '9') or (c>='A' and c <= 'Z') or (c >= 'a' and c <= 'z')) postfix += c;  
        else if (c == '(') da.addAtEnd(c);                                   // Opening bracket, store
        else if (c == ')') {                                                 // Find matching opening bracket
            while (da.hasData() and (da.lastElement() != '('))
                postfix += da.getAndRemoveFromEnd();
            da.removeFromEnd();
        }
        else if (c == '+' or c == '-' or c == '*' or c == '/' or c == '^') { // Check for operator

            // If this character (and as long as) has a lower precedence then the last operator strored in our dynamic array
            while (da.hasData() and (da.lastElement() != '(') and ((c == '+' or c == '-') or ((c == '*' or c == '/') and (da.lastElement() == '^'))))

                // Then add it to the resulting string
                postfix += da.getAndRemoveFromEnd();

            // Store new operator at the end of the dynamic array
            da.addAtEnd(c);
        }   // Ignore all invalid characters
        else std::cerr << "Error: Invalid character '" << c << "' found. Ignoring . . .\n";
    }
    // If there is still data in our dynamic array, then remove it and add to output
    while (da.hasData())
        postfix += da.getAndRemoveFromEnd();

    // ---------------------------------------------------------------------------
    // 3. Reverse the postfix expression to get the prefix expression

    // Reverse the data
    postfix.reverse();
    
    // And copy to output parameter
    *prefix = postfix;
}

// Test
int main() {
    
    DynamicArray infix ("1 + ( 2 - 3) * 4 / 5 ^ 6");
    DynamicArray prefix{};
    infixToPrefix(infix, &prefix);
    std::cout << "\n\nInfix:  '" << infix << "'   --> prefix:  '" << prefix << "'\n\n";
}

Of course this is for a 1 digit (letter) length only.当然,这仅适用于 1 位(字母)长度。 But can be easily adapted.但可以很容易地适应。

Have fun.玩得开心。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM