队列及其应用

202 阅读10分钟

数组实现

插入

时间复杂度:

O(1)O(1)
template<class T>
void Queue<T>::push(const T& theElement) {
    if (full()) {
        // 如果需要扩容
        changeLength();
    }
    // 把元素 theElement 加入队列
    // 元素只能插入到 1~MaxSize 范围内
    rear = (rear + 1) % MaxSize;
    queue[rear] = theElement;
}

删除

时间复杂度: image.png

O(n)O(n)

n 为删除后队列中剩余元素个数 image.png 最好的情况下(即未满不移动):

O(1)O(1)

最坏的情况下(满了整体前移):

O(n)O(n)

做成环后时间复杂度变为 O(1)O(1)
此时下标变成 loc=(front+i)%arrayLengthloc=(front+i) \% arrayLength

image.png 为防止队列空和队列满无法判断, 因此队列需要空出一位

image.png

template<class T>
void Queue<T>::pop() {
    if (front == rear) {
        throw "当前队列为空, 无法删除";
    }
    // 循环队列队首索引增加 1 再删除
    // 因为 front 表示队首前一位的位置
    front = (front + 1) % MaxSize;
    queue[front].~T();
}

插入

当队列满的时候, 需要增大数组

image.png

// 循环队列数组扩容
template<class T>
void Queue<T>::changeLength() {
    cout << "扩容中.." << endl;
    T* newQueue = new T[2 * MaxSize]; // 分配新的数组空间

    int start = (front + 1) % MaxSize; // 起始位置

    if (start < 2) {
        // 没有形成环, 也就是 front=0, 一直没有删除元素的情况
        copy(queue + start, queue + start + MaxSize - 1, newQueue);
    }
    else {
        // 队列形成环, 分成两段进行复制
        // 上图的 A~B
        copy(queue + start, queue + MaxSize, newQueue);
        // 上图的 C~G
        copy(queue, queue + rear + 1, newQueue + MaxSize - start);
    }
    // 设置新队列的首和尾的元素位置
    front = 2 * MaxSize - 1;
    rear = MaxSize - 2;
    MaxSize *= 2; // 队列长度扩大两倍
    delete[] queue;
    queue = newQueue;
};

// 插入元素
template<class T>
void Queue<T>::push(const T& theElement) {
    if (full()) {
        // 如果需要扩容
        changeLength();
    }
    // 把元素 theElement 加入队列
    // 元素只能插入到 1~MaxSize 范围内
    rear = (rear + 1) % MaxSize;
    queue[rear] = theElement;
}
完整代码
#include<iostream>
using namespace std;

template<class T>
class Queue {
public:
    Queue(int MaxQueueSize = 10);
    ~Queue() {
        delete[] queue; 
    }
    bool empty() const {
        return front == rear; 
    }
    bool full() const {
        // 比如 MaxSize=10, 数组下标范围为 0~9
        // front=0, rear=9 就表示队列满
        return (rear + 1) % MaxSize == front;
    }
    T first() const; // 返回头元素
    T end() const; // 返回尾元素
    void push(const T& theElement); // 把元素加入队尾
    void pop(); // 删除首元素
    void changeLength(); // 队列扩容

private:
    int front;   // 队首
    int rear;    // 队尾
    int MaxSize; // 队列容量
    T* queue;    // element array
};

// 构造函数
template<class T>
Queue<T>::Queue(int MaxQueueSize) {
    MaxSize = MaxQueueSize + 1; // 数组留出一定空间
    queue = new T[MaxSize];
    front = rear = 0;
}

// 返回队首元素
template<class T>
T Queue<T>::first() const {
    if (empty()) {
        throw "当前队列为空";
    }
    return queue[(front + 1) % MaxSize];
}

// 返回队尾元素
template<class T>
T Queue<T>::end() const {
    if (empty()) {
        throw "当前队列为空";
    }
    return queue[rear];
}

// 循环队列数组扩容
template<class T>
void Queue<T>::changeLength() {
    cout << "扩容中.." << endl;
    T* newQueue = new T[2 * MaxSize]; // 分配新的数组空间

    int start = (front + 1) % MaxSize; // 起始位置

    if (start < 2) {
        // 没有形成环
        copy(queue + start, queue + start + MaxSize - 1, newQueue);
    }
    else {
        // 队列形成环
        copy(queue + start, queue + MaxSize, newQueue);
        copy(queue, queue + rear + 1, newQueue + MaxSize - start);
    }
    // 设置新队列的首和尾的元素位置
    front = 2 * MaxSize - 1;
    rear = MaxSize - 2;
    MaxSize *= 2; // 队列长度扩大两倍
    delete[] queue;
    queue = newQueue;
};

// 插入元素
template<class T>
void Queue<T>::push(const T& theElement) {
    if (full()) {
        // 如果需要扩容
        changeLength();
    }
    // 把元素 theElement 加入队列
    // 元素只能插入到 1~MaxSize 范围内
    rear = (rear + 1) % MaxSize;
    queue[rear] = theElement;
}

// 删除元素
template<class T>
void Queue<T>::pop() {
    if (empty()) {
        throw "当前队列为空, 无法删除";
    }
    // 循环队列队首索引增加 1 再删除
    // 因为 front 表示队首前一位的位置
    front = (front + 1) % MaxSize;
    queue[front].~T();
}

int main() {
    Queue<int> q(3);
    q.push(10);
    q.push(12);

    cout << "队首元素" << q.first() << endl; // 10
    cout << "队尾元素" << q.end() << endl; // 13

    q.pop();
    q.push(13);
    q.push(15);
    q.push(16);
    cout << "队首元素" << q.first() << endl; // 12
    cout << "队尾元素" << q.end() << endl; // 13

    return 0;
}

链表实现

两种链接方向

image.png

方案(a) 更适合删除

插入

image.png

时间复杂度:

O(1)O(1)
template<class T>
void linkedQueue<T>::push(const T& theElement) {
    chainNode<T>* p = new chainNode<T>(theElement, NULL);
    if (front) {
        // 队列不为空时
        rear->next = p;
        rear = p;
    }
    else {
        // 队列为空时
        rear = front = p;
    }
}

删除

image.png

时间复杂度:

方案(a)

O(1)O(1)
template<class T>
void linkedQueue<T>::pop() {
    if (empty()) {
        throw "当前队列为空, 无法删除";
    }

    chainNode<T>* next = front->next; // 备份下一个节点
    delete front; // 释放原队首的空间
    front = next; // 确定新的队首
}

方案(b)

O(n)O(n)

析构函数

时间复杂度:

O(n)O(n)
template<class T>
linkedQueue<T>::~linkedQueue() {
    chainNode<T>* next;
    // 遍历整个链表释放信息
    while (front) {
        next = front->next;
        delete front;
        front = next;
    }
}
完整代码
#include<iostream>
using namespace std;

template <class T>
struct chainNode {
    // 数据成员
    T element;
    chainNode<T>* next;

    // 方法
    chainNode() { }
    chainNode(const T& element) {
        this->element = element;
    }
    chainNode(const T& element, chainNode<T>* next) {
        this->element = element;
        this->next = next;
    }
};

template<class T>
class linkedQueue {
public:
    linkedQueue() {
        // 初始化时指向 NULL
        front = rear = NULL;
    }
    ~linkedQueue();
    bool empty() const {
        // 判断队首是不是指向 NULL
        return front ? false : true;
    }
    T first() const; // 返回头元素
    T end() const; // 返回尾元素
    void push(const T& theElement); // 把元素加入队尾
    void pop(); // 删除首元素

private:
    chainNode<T>* front;
    chainNode<T>* rear;
};


// 析构函数
template<class T>
linkedQueue<T>::~linkedQueue() {
    chainNode<T>* next;
    // 遍历整个链表释放信息
    while (front) {
        next = front->next;
        delete front;
        front = next;
    }
}

// 返回队首元素
template<class T>
T linkedQueue<T>::first() const {
    if (empty()) {
        throw "当前队列为空";
    }
    return front->element;
}

// 返回队尾元素
template<class T>
T linkedQueue<T>::end() const {
    if (empty()) {
        throw "当前队列为空";
    }
    return rear->element;
}

// 插入元素
template<class T>
void linkedQueue<T>::push(const T& theElement) {
    chainNode<T>* p = new chainNode<T>(theElement, NULL);
    if (front) {
        // 队列不为空时
        rear->next = p;
        rear = p;
    }
    else {
        // 队列为空时
        rear = front = p;
    }
}

// 删除元素
template<class T>
void linkedQueue<T>::pop() {
    if (empty()) {
        throw "当前队列为空, 无法删除";
    }

    chainNode<T>* next = front->next; // 备份下一个节点
    delete front; // 释放原队首的空间
    front = next; // 确定新的队首
}

int main() {
    linkedQueue<int> q;
    q.push(10);
    q.push(12);

    cout << "队首元素" << q.first() << endl; // 10
    cout << "队尾元素" << q.end() << endl; // 13

    q.pop();
    q.push(13);
    q.push(15);
    q.push(16);
    cout << "队首元素" << q.first() << endl; // 12
    cout << "队尾元素" << q.end() << endl; // 13

    return 0;
}

车厢重排

栈实现刚好相反, 车厢号数大的可以入队, 车厢号数小的只能去其他队列

image.png

  1. 3, 6, 9 依次入队

image.png

  1. 2 比 9 小, 入另一个队

image.png

  1. 1 符合条件, 直接出轨, 符合条件的车厢变成 2 号, 2 号出队, 符合条件的车厢变成 3 号, 如此反复...

代码实现

#include<iostream>
#include<queue>
#include<vector>
using namespace std;


class Rearrange {
public:
    Rearrange(int numberOfTracks) {
        this->numberOfTracks = numberOfTracks;
        track = new queue<int>[numberOfTracks + 1];
        smallestCar = INT_MAX; // 表示一个不存在车厢
        itsTrack = -1;
    }
    bool putInHoldingTrack(int);
    void outputFromHoldingTrack(vector<int>*);
    vector<int> railroad(vector<int>);
private:
    int numberOfTracks; // 缓冲轨道数

    // 队列中最小车厢号
    // 初始值是一个不存在的车厢号, 表示轨道中没有车
    int smallestCar;

    int itsTrack; // 最小车厢号所在队列号
    queue<int>* track; // 车厢队列
};


// 为车厢 c 寻找最适合的缓冲轨道
bool Rearrange::putInHoldingTrack(int c) {
    int bestTrack = 0, // 目前最合适的缓冲轨道
        bestLast = 0; // 队尾车厢最接近 c 的车厢号

    // 扫描缓冲轨道
    for (int i = 1; i <= numberOfTracks;i++) {
        if (!track[i].empty()) {
            // 缓冲轨道 i 不空
            int lastCar = track[i].back(); // 获取队尾元素

            // 取最接近 c 的最大车厢号所在队列作为最合适缓冲轨道
            if (c > lastCar && lastCar > bestLast) {
                // 缓冲轨道 i 的尾部具有编号更大的车厢
                bestLast = lastCar;
                bestTrack = i;
            }
        }
        else {
            // 缓冲轨道 i 为空, 则最佳轨道就是 i
            if (bestTrack == 0) bestTrack = i;
        }
    }

    if (bestTrack == 0) return false; // 没有可用的缓冲轨道

    // 把车厢 c 移到最佳缓冲轨道 bestTrack
    track[bestTrack].push(c);
    // 更新 smallestCar 和 itsTrack
    if (c < smallestCar) {
        smallestCar = c;
        itsTrack = bestTrack;
    }

    return true;
}


void Rearrange::outputFromHoldingTrack(vector<int>* r) {
    track[itsTrack].pop(); // 从队列删除编号最小的车厢

    r->push_back(smallestCar); // 输出轨道

    // 更新最小车厢号数和队列号
    smallestCar = INT_MAX;
    for (int i = 1; i <= numberOfTracks; i++) {
        if (!track[i].empty() && track[i].front() < smallestCar) {
            smallestCar = track[i].front();
            itsTrack = i;
        }
    }
}

vector<int> Rearrange::railroad(vector<int> cars) {
    int nextCarToOutput = 1; // 符合出轨条件的车厢号(即第一个是 1 号车厢)
    int numberOfCars = cars.size(); // 车厢数

    vector<int> res;

    for (int i = 0; i < numberOfCars; i++) { // 遍历所有车厢
        if (cars[i] == nextCarToOutput) {
            // 符合出轨条件, 车厢出轨
            res.push_back(cars[i]);
            nextCarToOutput++; // 符合条件的车厢+1(车厢号数只会是连续的)

            while (smallestCar == nextCarToOutput) {
                // 一旦有车厢出轨, 则从缓冲轨道中陆续取出车厢
                // 只有缓冲轨道中最小车厢号 smallestCar 符合出轨条件才能 output
                outputFromHoldingTrack(&res);
                nextCarToOutput++;
            }
        }
        else {
            // 如果找不到合适的缓冲轨道, 说明排序失败
            if (!putInHoldingTrack(cars[i])) {
                    throw "排序失败";
            }
        }
    }

    return res;
}

int main() {
    vector<int> cars = { 3, 6, 9, 2, 4, 7, 1, 8, 5 };
    Rearrange r(3);
    cars = r.railroad(cars);
    for (int i = 0; i < cars.size(); i++) {
            cout << cars[i] << " ";
    }

    return 0;
}

电路布线

即寻找两点间最短路径问题

image.png

从 a 开始, 其相邻格子标记为 1, 1 的相邻格子标记为 2, 以此类推, 直到 b 被标记, 其编号就是最短距离

步骤

  1. 从 start 开始遍历, 给其周围的 4 个可达节点标记为 1, 标记的同时入队
  2. 如果存在终点, 则结束, 不存在终点, 将标记为 1 的节点出队, 然后访问其 4 个可达节点, 以此类推
  3. 如果访问到的可达结点为终点, 退出循环
  4. 队列为空且最终未达终点, 则终点不可达

代码实现

广度优先搜索, 回溯法

#include<iostream>
#include<queue>
#include<vector>
using namespace std;

// 坐标类
class Point {
public:
    int x;
    int y;
    Point(int x, int y) {
        this->x = x;
        this->y = y;
    }
    Point() {} // 什么都不做的构造函数
};

vector<Point> findPath(Point start, Point end, vector<vector<int>> grid) {
    vector<Point> path; // 最短路径数组
    int pathLength; // 最短路径长度

    // 寻找从始点到终点的最短路径
    if ((start.x == end.x) && (start.y == end.y)) { // 始点 = 终点
        pathLength = 0;
        return path;
    }
	
    // 初始化偏移量
    Point offset[4] = {
        Point(0, 1), // 右
        Point(1, 0), // 下
        Point(0, -1), // 左
        Point(-1, 0), // 上
    };

    int numOfNbrs = 4; // 一个方格最多 4 个相邻位置
    queue<Point> q; // 节点队列
    Point nbr; // 一个临时节点

    Point cur = start; // 表示当前方块
    // start 周围 4 个方格会先入队
    // 然后先出队去找 4 个节点的可达节点
    do {
        // 给相邻位置做标记
        for (int i = 0; i < numOfNbrs; i++) {
            // 检查相邻位置
            nbr.x = cur.x + offset[i].x;
            nbr.y = cur.y + offset[i].y;

            if (nbr.x < 0 || nbr.x > 6 || nbr.y < 0 || nbr.y > 6) {
                continue;
            }

            if (grid[nbr.x][nbr.y] == 0) { // 可标记

                grid[nbr.x][nbr.y] = grid[cur.x][cur.y] + 1; // 相邻位置的编号多 1

                if (nbr.x == end.x && nbr.y == end.y) break; // 标记完成

                // 把相邻位置入队
                q.push(nbr);
            }
        }

        // 是否到达终点
        if ((nbr.x == end.x) && (nbr.y == end.y)) break; // 到达

        // 没到终点, 判断队列是否为空
        if (q.empty()) return path; // 路径不存在

        cur = q.front(); // 取下一个位置
        q.pop();
    } while (true);
	
    // 构造路径, 路径长度为终点编号 - 3(即去掉起始和终点, 且起始点起始编号为 2)
    pathLength = grid[end.x][end.y] - 3;

    cout << "终点编号" << grid[end.x][end.y] << endl;
    cout << "最短路径长度: " << pathLength << endl;

    path.resize(pathLength);

    // 从终点回溯
    cur = end;
    for(int j = pathLength - 1; j >= 0; j--) {
        for (int i = 0; i < numOfNbrs; i++) {
            nbr.x = cur.x + offset[i].x;
            nbr.y = cur.y + offset[i].y;

            if (nbr.x < 0 || nbr.x > 6 || nbr.y < 0 || nbr.y > 6) continue;

            // 编号小一位就找到了祖先节点
            if (grid[nbr.x][nbr.y] == j + 3) break;
        }
        cur = nbr; // 移向祖先
        path[j] = cur;  // 寻找祖先位置
    }
    return path;
}

int main() {

    // 起始位置标记为 2
    vector<vector<int>> grid = {{0, 0, 1, 0, 0, 0, 0 },
                            {0, 0, 1, 1, 0, 0, 0 },
                            {0, 2, 0, 0, 1, 0, 0 },
                            {0, 0, 0, 1, 1, 0, 0 },
                            {1, 0, 0, 0, 1, 0, 0 },
                            {1, 1, 1, 0, 0, 0, 0 },
                            {1, 1, 1, 0, 0, 0, 0 }};

    Point start(2, 1);
    Point end(3, 5);

    vector<Point> p = findPath(start, end, grid);

    cout << "路径为: (" << start.x << "," << start.y << ")-";
    for (int i = 0; i < p.size(); i++) {
        cout << "(" << p[i].x << "," << p[i].y << ")-";
    }
    cout << "(" << end.x << "," << end.y << ")" << endl;

    return 0;
}

杨辉三角

杨辉三角与组合数

image.png

由上图可知, 杨辉三角第 n 行的其实是 (a+b)n(a+b)^n 的系数, 下方的值是上方左右两个值相加(边界的 1 可以理解为+0)

#include<iostream>
#include<queue>
using namespace std;

void YANGVI(int n) {
    queue<int> yang; // 队列初始化

    cout << 1 << endl; // 第一行

    // 第二行的两个数据数据
    yang.push(1);
    yang.push(1);
    int s = 0;
    for (int i = 1; i < n; i++) {
        yang.push(0); // 0 作为一行结束
        for (int j = 1; j <= i + 2; j++) {
            int t = yang.front(); // 读取上方右边的值
            yang.pop();
            // s 代表上方左边的值
            yang.push(s + t); // 计算下一行系数, 并进队列		
            s = t;

            // 一行要执行 i+2 次循环
            // 第 i+2 次是取出末尾的 0, 加上前面的 1 作为下一行结束的 1
            // 并且 0 还可以在下一次循环时加上第一个 1 作为开头的 1
            if (j != i + 2) cout << s << " ";
        }
        cout << endl;
    }
}

int main() {
    YANGVI(10);
    return 0;
}

上面的队列还保留着 n+1 行的数据(这可以用于计算最终多项式的表达式)

非队列方案
vector<vector<int>> generate(int numRows) {
    vector<vector<int>> arr = {{1}}; // 第一个是 1

    for(int i = 1; i < numRows; i++) {
        int j = 0;
        vector<int> list;
        while(j <= i) {
            if(j - 1 < 0 || j == i) {
                // 第一个和最后一个都是 1
                list.push_back(1);
            } else {
                list.push_back(arr[i - 1][j - 1] + arr[i - 1][j]);
            }
            j++;
        }
        arr.push_back(list);
    }
    return arr;
}

动态规划法

#include<vector>
#include<iostream>
using namespace std;

vector<int> getRow(int rowIndex) {
    vector<int> ans(rowIndex + 1, 1);

    // 第 0 层和第 1 层都是 1, 因此从第 2 层开始
    for (int i = 2; i < ans.size(); i++) {
        for (int j = i - 1; j > 0; j--) {
            // 从后往前修改, 这样不会影响下一次计算
            ans[j] = ans[j] + ans[j - 1];
        }
    }

    return ans;
};

vector<vector<int>> getYangVi(int num) {
    vector<vector<int>> ans;
    for (int i = 0; i < num; i++) {
        ans.push_back(getRow(i));
    }

    return ans;
}

int main() {
    vector<vector<int>> res = getYangVi(6);

    for (int i = 0; i < res.size(); i++) {
        for (int j = 0; j < res[i].size(); j++) {
            cout << res[i][j] << " ";
        }

        cout << endl;
    }
    return 0;
}

类杨辉三角与多项式

image.png

由上图可知, 下方的值等于上方“左×b的系数+右×a的系数”

#include <iostream>
#include<string>
#include <queue>
using namespace std;

// (na+mb)^exp
string polynomial(int n, int m, int exp) {
    queue <int> q;

    q.push(n);
    q.push(m);

    // 类似杨辉三角
    int s = 0, t;
    for (int i = 1; i < exp; i++) {
        q.push(0);
        for (int j = 1; j <= i + 2; j++) {
            t = q.front();
            q.pop();
            // b 的系数 × 左上 + a 的系数 × 右上
            q.push(n * t + m * s);
            s = t;
        }
    }

    int a = exp, b = 0; // 这是 a 和 b 在表达式中的指数
    bool first = true; // 这代表当前是否为第一项
    string expr; // 表达式
    while (!q.empty()) {
        int coefficient = q.front(); // 系数
        q.pop();

        if (coefficient == 0) {
            a--;
            b++;
            continue;
        };

        if (!first && coefficient > 0) {
            // 第一个项, 不需要加号
            // 系数大于 0 才需要 + 号
            // 因为 - 号会跟着系数带上
            expr += "+";
        }

        if (coefficient != 1 && coefficient != -1) {
            expr += to_string(coefficient);
        }
        else if (coefficient == -1) {
            expr += "-";
        }

        if (a != 0 && a != 1) {
            expr += ("x^" + to_string(a));
        }
        else if (a == 1) {
            expr += "x";
        }

        if (b != 0 && b != 1) {
            expr += ("y^" + to_string(b));
        }
        else if (b == 1) {
            expr += "y";
        }

        a--;
        b++;
        first = false;
    }

    return expr;
}

int main() {
    int n, m, e;
    cin >> n >> m >> e;
    cout << polynomial(n, m, e);

    // 2 3 2
    // 4x^2+12xy+9y^2

    return 0;
}

注意系数如果太大的话, int 队列就不行了

大整数

如果 a 和 b 的系数很大(-1000~1000), 指数 exp 也很大(2~20), 那么 long long 根本不够用!

思路: 转成字符串计算

完整代码
#include<iostream>
#include<queue>
#include<string>
using namespace std;


class Solution {
public:
    // 获取绝对值
    string absolute(string num) {
        if (num[0] == '-') {
            return num.substr(1);
        }
        return num;
    }

    // 字符串乘法
    string multiply(string num1, string num2) {
        bool negative; // 是否是负数
        if (num1[0] == '-' && num2[0] == '-') {
            // 两个负数相乘, 等于正数
            negative = false;
        }
        else if (num1[0] == '-' || num2[0] == '-') {
            // 只要有一个为负数, 结果就为负数
            negative = true;
        }
        else {
            // 两个都是正数的情况
            negative = false;
        }

        // 数字字符串取绝对值运算
        num1 = absolute(num1);
        num2 = absolute(num2);

        int n1 = num1.size(),
        n2 = num2.size();

        // 初始化一个全 0 字符串
        // 两个数相乘, 其结果的位数不会超过两者位数之和
        string res(n1 + n2, '0');

        int length, start;
        length = start = n1 + n2 - 1;

        // 从后往前遍历
        for (int j = n2 - 1; j >= 0; j--) {
            int k = start;
            int carry = 0; // 进位
            for (int i = n1 - 1; i >= 0; i--) {
                // 两数相乘 + 进位 + 上一轮此位置结果
                int sum = (num2[j] - '0') * (num1[i] - '0') + carry + (res[k] - '0');
                carry = sum / 10;
                res[k] = (sum % 10 + '0');
                k--;
            }
            if (carry) { 
                /* 最终进位
                *  该位置一定没有被占用过
                *              1 2 5
                *            ×   8 9
                * -------------------
                *     (进位)  9 18 45
                * (进位) 8   16 40
                */
                res[k] = carry + '0';
            }
            start--;
        }

        // 获取第一个不为 0 的下标
        int k = 0;
        while (k <= length && res[k] == '0') {
            k++;
        }

        if (k == length + 1) return "0"; // 结果为 0

        // 去除字符串 res 前面的 0
        res = res.substr(k);

        if (negative) {
            // 负数
            return '-' + res;
        }

        return res;
    }

    // 字符串加法(仅支持非负数)
    string add(string num1, string num2) {
        int n1 = num1.size(),
        n2 = num2.size();

        // 两数相加, 最多不超过最大数位数 + 1
        int len = n1 > n2 ? n1 + 1: n2 + 1;
        string res(len, '0');

        // 数字字符串补 0
        num1 = string(len - n1, '0') + num1;
        num2 = string(len - n2, '0') + num2;

        int carry = 0; // 进位
        int k = len - 1; // 开始的位置
        for (int i = len - 1; i > 0; i--) {
            int sum = (num2[i] - '0') + (num1[i] - '0') + carry;
            carry = sum / 10;
            res[k] = (sum % 10 + '0');
            k--;
        }

        if (carry) {
            res[k] = carry + '0';
        }

        // 去掉前面的 0
        if (res[0] == '0') {
            return res.substr(1);
        }

        return res;
    }

    /*
    * 判断两个数谁大(仅支持非负数)
    * 返回 true, 当且仅当 num1 大
    */
    bool less(string num1, string num2) {
        // 字符串长度大的表示数大
        int n1 = num1.size(),
        n2 = num2.size();

        // 长度一样, 直接比较字符串
        if (n1 == n2) return num1 > num2;

        // 长度大的数大
        return n1 > n2;
    }

    // 字符串减法(仅支持非负数)
    string sub(string num1, string num2) {
        int n1 = num1.size(),
        n2 = num2.size();

        string res;
        if (less(num1, num2)) {
            // num1 更大, 可以直接做减法
            res = subtract(num1, num2);
        }
        else {
            // num1 小, 用 num2 - num1 后加上负号
            res = '-' + subtract(num2, num1);
        }

        return res;
    }

    // 减法, 要求 num1 > num2(仅支持非负数)
    string subtract(string num1, string num2) {
            int n1 = num1.size(), 
            n2 = num2.size();

        // 两数相减, 结果的位数不会超过最大位
        int len = n1 > n2 ? n1: n2;
        string res(len, '0');

        // 数字字符串补 0
        num1 = string(len - n1, '0') + num1;
        num2 = string(len - n2, '0') + num2;

        int borrow = 0; // 借位
        for (int k = len - 1; k >= 0; k--) {
            int z = (num1[k] - borrow - num2[k] + 10) % 10;
            res[k] = z + '0';
            borrow = num1[k] - borrow - num2[k] < 0 ? 1 : 0;
        }

        int pos = 0;
        while (pos <= len && res[pos] == '0') {
            pos++;
        }

        return res.substr(pos);
    }
};

string polynomial(int n, int m, int exp) {
	
    queue<string> q;
    Solution sol;

    // 系数转字符串
    string a = to_string(n);
    string b = to_string(m);

    q.push(a);
    q.push(b);

    string num1, num2;
    string ans;

    string s = "0", t;

    // 这一步和普通写法相似, 只不过换成字符串加减法
    for (int i = 1; i < exp; i++) {
        q.push("0");	
        for (int j = 1; j <= i + 2; j++) {
            t = q.front();
            q.pop();

            num1 = sol.multiply(t, a);
            num2 = sol.multiply(s, b);

            if (num1[0] == '-' && num2[0] == '-') {
                // 两个都是负数, 取绝对值后相加, 再加个负号
                ans = '-' + sol.add(sol.absolute(num1), sol.absolute(num2));
            }
            else if (num1[0] == '-') {
                // num1 是负数, 则 num2 肯定不是
                // num2 - |num1|
                ans = sol.sub(num2, sol.absolute(num1));
            }
            else if (num2[0] == '-') {
                // 能判断到这里, 说明 num1 不是负数, num2 是负数
                ans = sol.sub(num1, sol.absolute(num2));
            }
            else {
                // 都不是负数, 直接相加
                ans = sol.add(num1, num2);
            }

            q.push(ans);
            s = t;
        }
    }

    int ac = exp, bc = 0; // 这是 a 和 b 在表达式中的指数
    bool first = true; // 这代表当前是否为第一项
    string expr; // 表达式
    while (!q.empty()) {
        string coefficient = q.front(); // 系数
        q.pop();

        if (coefficient == "0") {
            ac--;
            bc++;
            continue;
        };

        if (!first && coefficient[0] != '-') {
            // 第一个项, 不需要加号
            // 系数大于 0 才需要 + 号
            // 因为 - 号会跟着系数带上
            expr += "+";
        }

        if (coefficient != "1" && coefficient != "-1") {
            expr += coefficient;
        }
        else if (coefficient == "-1") {
            expr += "-";
        }

        if (ac != 0 && ac != 1) {
            expr += ("x^" + to_string(ac));
        }
        else if (ac == 1) {
            expr += "x";
        }

        if (bc != 0 && bc != 1) {
            expr += ("y^" + to_string(bc));
        }
        else if (bc == 1) {
            expr += "y";
        }

        ac--;
        bc++;
        first = false;
    }

    return expr;
}

int main() {
    int n, m , e;
    cin >> n >> m >> e;

    cout << polynomial(n, m, e);

    return 0;
}