Tag Archives: 队列

剑指 Offer 09. 用两个栈实现队列

剑指 Offer 09. 用两个栈实现队列

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

示例 1:

输入:
[“CQueue”,”appendTail”,”deleteHead”,”deleteHead”]
[[],[3],[],[]]
输出:[null,null,3,-1]
示例 2:

输入:
[“CQueue”,”deleteHead”,”appendTail”,”appendTail”,”deleteHead”,”deleteHead”]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]
提示:

1 <= values <= 10000
最多会对 appendTail、deleteHead 进行 10000 次调用

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


用两个栈实现一个队列。栈是后进先出,队列是先进先出。设置两个栈stack_in_和stack_out_,分别负责数据的push和pop,如果stack_out_没数据,则需要从stack_in_中把数据捣腾到stack_out_中。完整代码如下:

class CQueue {
private:
    stack<int> stk_in_, stk_out_;
public:
    CQueue() {

    }
    
    void appendTail(int value) {
        stk_in_.push(value);
    }
    
    int deleteHead() {
        if(!stk_out_.empty()) {
            int ans = stk_out_.top();
            stk_out_.pop();
            return ans;
        } else if(!stk_in_.empty()) {
            while(stk_in_.size() != 1) {
                int tmp = stk_in_.top();
                stk_in_.pop();
                stk_out_.push(tmp);
            }
            int tmp = stk_in_.top();
            stk_in_.pop();
            return tmp;
        } else {
            return -1;
        }
    }
};

本代码提交AC,用时696MS。

LeetCode Minimum Cost to Make at Least One Valid Path in a Grid

1368. Minimum Cost to Make at Least One Valid Path in a Grid

Given a m x ngrid. Each cell of the grid has a sign pointing to the next cell you should visit if you are currently in this cell. The sign of grid[i][j] can be:

  • 1 which means go to the cell to the right. (i.e go from grid[i][j] to grid[i][j + 1])
  • 2 which means go to the cell to the left. (i.e go from grid[i][j] to grid[i][j - 1])
  • 3 which means go to the lower cell. (i.e go from grid[i][j] to grid[i + 1][j])
  • 4 which means go to the upper cell. (i.e go from grid[i][j] to grid[i - 1][j])

Notice that there could be some invalid signs on the cells of the grid which points outside the grid.

You will initially start at the upper left cell (0,0). A valid path in the grid is a path which starts from the upper left cell (0,0) and ends at the bottom-right cell (m - 1, n - 1) following the signs on the grid. The valid path doesn’t have to be the shortest.

You can modify the sign on a cell with cost = 1. You can modify the sign on a cell one time only.

Return the minimum cost to make the grid have at least one valid path.

Example 1:

Input: grid = [[1,1,1,1],[2,2,2,2],[1,1,1,1],[2,2,2,2]]
Output: 3
Explanation: You will start at point (0, 0).
The path to (3, 3) is as follows. (0, 0) --> (0, 1) --> (0, 2) --> (0, 3) change the arrow to down with cost = 1 --> (1, 3) --> (1, 2) --> (1, 1) --> (1, 0) change the arrow to down with cost = 1 --> (2, 0) --> (2, 1) --> (2, 2) --> (2, 3) change the arrow to down with cost = 1 --> (3, 3)
The total cost = 3.

Example 2:

Input: grid = [[1,1,3],[3,2,2],[1,1,4]]
Output: 0
Explanation: You can follow the path from (0, 0) to (2, 2).

Example 3:

Input: grid = [[1,2],[4,3]]
Output: 1

Example 4:

Input: grid = [[2,2,2],[2,2,2]]
Output: 3

Example 5:

Input: grid = [[4]]
Output: 0

Constraints:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 100

这个题目路有点难,我想了一下午,不过想明白之后也就不难了。

给定一个网格地图,每个网格标明了从该位置可以到达的下一个位置。现在要从地图的左上角走到右下角,有可能根据图标不能到达,问最少改变几个图标可以到达。每个图标仅可改变一次,可将其改为上下左右任意一个方向。

解法1。事实上,对于网格中的每个点,它都可以走到其上下左右的4个点,只不过按图标走费用为0,不按图标走费用为1。所以可以构造一个长和宽都是m*n的图,总元素即为(m*n)*(m*n),新的图中的值就是老图中相邻两个点之间的费用。然后在新的图中执行Dijkstra算法,寻找最短路径,即为原图的最小消费。

解法1太复杂了,忘记怎么写Dijkstra了,放弃。

解法2。不转换为新的图,直接在原图中执行算法。设置一个和原图同样大小的cost二维数组,cost[i][j]表示从左上角走到该位置的最小费用。对原图从左上角开始执行BFS,如果从当前点走到下一个节点的累加费用小于下一个节点的当前费用,则更新下一个节点的费用,并且BFS走到下一个节点。最终右下角的费用就是最小费用。

完整代码如下:

const int MAXCOST = 1000000;
struct Node {
	int x, y;
	Node() :x(0), y(0) {};
	Node(int x_, int y_) :x(x_), y(y_){};
};
class Solution {
public:

	int minCost(vector<vector<int>>& grid) {
		vector<vector<int>> dirs = { {0, 1}, {0, -1},{1, 0},{-1, 0} };//右左下上
		int m = grid.size(), n = grid[0].size();
		vector<vector<int>> cost(m, vector<int>(n, MAXCOST));
		cost[0][0] = 0;
		queue<Node> q;
		q.push(Node());
		while (!q.empty()) {
			Node cur = q.front();
			q.pop();
			for (int i = 0; i < dirs.size(); ++i) {
				int x = cur.x + dirs[i][0], y = cur.y + dirs[i][1];
				if (x < 0 || x >= m || y < 0 || y >= n)continue;
				if (i + 1 == grid[cur.x][cur.y] && cost[cur.x][cur.y] < cost[x][y]) {
					cost[x][y] = cost[cur.x][cur.y];
					q.push(Node(x, y));
				}
				else if (i + 1 != grid[cur.x][cur.y] && (cost[cur.x][cur.y] + 1 < cost[x][y])) {
					cost[x][y] = cost[cur.x][cur.y] + 1;
					q.push(Node(x, y));
				}
			}
		}
		return cost[m - 1][n - 1];
	}
};

本代码提交AC,用时76MS。

该解法没有设置visited数组标记走过的节点,因为没必要。一方面,以题目中的Example 2的图为例(下标从1开始),其(2,1)位置有可能从(1,1)直接下来,也有可能(1,1)先走右边下来再走左边到达(2,1),而第二条路径的费用低于第一条路径,如果设置visited数组的话,第二条路径就没法走了。另一方面,也不用担心不设置visited数组会导致程序来回来反复跑而无法终止,因为只有当走到该点的费用小于该点的当前费用时,才会将其加入队列,如果来回跑的话,费用肯定大于改点当前费用,也就不会加入队列了。所以无需设置visited数组,程序也能正常结束并得到正确结果。

解法3。参考讨论区题解,和解法2类似,但比解法2效率更高。BFS的时候,使用双端队列存储需要访问的节点,而且每次插入队列时,先插入满足原图方向标识的节点,而且是插入到队列开头,因为满足方向标识的路径费用为0,插入到队列开头之后,下次BFS时也先从队列开头取节点。对于不满足方向标识的节点,插入到队列尾部。这个策略相当于优先走费用为0的路径,只有当这些路径无法到达时,才会走费用为1的路径。

完整代码如下:

struct Node {
	int x, y, cost;
	Node() :x(0), y(0), cost(0) {};
	Node(int x_, int y_, int cost_) :x(x_), y(y_), cost(cost_) {};
};
class Solution {
public:

	int minCost(vector<vector<int>>& grid) {
		vector<vector<int>> dirs = { {0, 1}, {0, -1},{1, 0},{-1, 0} };//右左下上
		int m = grid.size(), n = grid[0].size();
		vector<vector<int>> flag(m, vector<int>(n, 0));
		deque<Node> q;
		q.push_back(Node());
		int ans = 0;
		while (!q.empty()) {
			Node cur = q.front();
			q.pop_front();
			flag[cur.x][cur.y] = 1;
			if (cur.x == m - 1 && cur.y == n - 1) {
				ans = cur.cost;
				break;
			}
			for (int i = 0; i < dirs.size(); ++i) {
				int x = cur.x + dirs[i][0], y = cur.y + dirs[i][1];
				if (x < 0 || x >= m || y < 0 || y >= n)continue;
				if (flag[x][y] == 1)continue;
				if (i + 1 == grid[cur.x][cur.y]) {
					q.push_front(Node(x, y, cur.cost));
				}
				else {
					q.push_back(Node(x, y, cur.cost + 1));
				}
			}
		}
		return ans;
	}
};

本代码提交AC,用时36MS,比解法2效率更高。

LeetCode Implement Stack using Queues

225. Implement Stack using Queues

Implement the following operations of a stack using queues.

  • push(x) — Push element x onto stack.
  • pop() — Removes the element on top of the stack.
  • top() — Get the top element.
  • empty() — Return whether the stack is empty.

Example:

MyStack stack = new MyStack();

stack.push(1);
stack.push(2);  
stack.top();   // returns 2
stack.pop();   // returns 2
stack.empty(); // returns false

Notes:

  • You must use only standard operations of a queue — which means only push to backpeek/pop from frontsize, and is empty operations are valid.
  • Depending on your language, queue may not be supported natively. You may simulate a queue by using a list or deque (double-ended queue), as long as you use only standard operations of a queue.
  • You may assume that all operations are valid (for example, no pop or top operations will be called on an empty stack). 225. Implement Stack using Queues

LeetCode Implement Queue using Stacks正好相反,本题要用队列实现堆栈的效果。 队列是先进先出的,而堆栈是后进先出。如果用队列q保存数据,则要实现堆栈的pop效果时,需要获取到q的队尾元素,可以不断把q的数据从队首弹出,同时压回队尾,重复n-1次,此时队首元素就是原来的队尾元素了。堆栈的top效果和push的实现方法是类似的。 这种题目就是数据不断的倒腾。。。 完整代码如下:

class MyStack {
private:
    queue<int> m_q;
public: /** Initialize your data structure here. */
    MyStack() {} /** Push element x onto stack. */
    void push(int x) { m_q.push(x); } /** Removes the element on top of the stack and returns that element. */
    int pop()
    {
        int sz = m_q.size();
        for (int i = 0; i < sz – 1; ++i) {
            m_q.push(m_q.front());
            m_q.pop();
        }
        int front = m_q.front();
        m_q.pop();
        return front;
    } /** Get the top element. */
    int top()
    {
        int sz = m_q.size(), front = m_q.front();
        for (int i = 0; i < sz; ++i) {
            front = m_q.front();
            m_q.push(front);
            m_q.pop();
        }
        return front;
    } /** Returns whether the stack is empty. */
    bool empty() { return m_q.empty(); }
};

本代码提交AC,用时3MS。

二刷。用两个queue来模拟stack,不如上面的解法好看:

class MyStack {
private:
	vector<queue<int>> vq_;
	int data_queue_id_;
public:
	/** Initialize your data structure here. */
	MyStack() {
		vq_.push_back(queue<int>());
		vq_.push_back(queue<int>());
		data_queue_id_ = 0;
	}

	/** Push element x onto stack. */
	void push(int x) {
		vq_[data_queue_id_].push(x);
	}

	/** Removes the element on top of the stack and returns that element. */
	int pop() {
		int another_id = 1 - data_queue_id_;
		int last_data = 0;
		while (!vq_[data_queue_id_].empty()) {
			last_data = vq_[data_queue_id_].front();
			vq_[data_queue_id_].pop();
			if (!vq_[data_queue_id_].empty()) {
				vq_[another_id].push(last_data);
			}
		}
		swap(another_id, data_queue_id_);
		return last_data;
	}

	/** Get the top element. */
	int top() {
		int another_id = 1 - data_queue_id_;
		int last_data = 0;
		while (!vq_[data_queue_id_].empty()) {
			last_data = vq_[data_queue_id_].front();
			vq_[data_queue_id_].pop();
			vq_[another_id].push(last_data);
		}
		swap(another_id, data_queue_id_);
		return last_data;
	}

	/** Returns whether the stack is empty. */
	bool empty() {
		return vq_[data_queue_id_].empty();
	}
};

本代码提交AC,用时0MS。

LeetCode Implement Queue using Stacks

232. Implement Queue using Stacks 232. Implement Queue using Stacks

Implement the following operations of a queue using stacks.

  • push(x) — Push element x to the back of queue.
  • pop() — Removes the element from in front of queue.
  • peek() — Get the front element.
  • empty() — Return whether the queue is empty.

Example:

MyQueue queue = new MyQueue();

queue.push(1);
queue.push(2);  
queue.peek();  // returns 1
queue.pop();   // returns 1
queue.empty(); // returns false

Notes:

  • You must use only standard operations of a stack — which means only push to toppeek/pop from topsize, and is empty operations are valid.
  • Depending on your language, stack may not be supported natively. You may simulate a stack by using a list or deque (double-ended queue), as long as you use only standard operations of a stack.
  • You may assume that all operations are valid (for example, no pop or peek operations will be called on an empty queue). 232. Implement Queue using Stacks

本题要求用堆栈来实现队列的功能。因为堆栈是后进先出,而队列是先进先出,顺序正好相反。比如进入队列的顺序是1,2,3,但是进到堆栈stk1之后,从栈顶到栈底的顺序就变成了3,2,1,此时如果要实现队列出队的效果,就必须借助另一个堆栈stk2,把stk1的内容压入到stk2,这样从栈顶到栈底的顺序就和队列一样了,是1,2,3。 所以本题借助两个堆栈stk1和stk2,stk1就是常规的数据来了就压栈,当需要实现队列的pop效果时,就借助stk2倒腾一下,如果又要实现队列的push效果时,又要把stk2的数据捣腾回stk1。 总的来说,我们使用懒人规则,每次直到调用某个操作,且当前stk*不能满足要求时,才捣腾stk1和stk2。 完整代码如下:

class MyQueue {
private:
    stack<int> stk1, stk2; // 输入来源1,2,3,stk1从栈底到栈顶的顺序是1,2,3,stk2从栈底到栈顶的顺序是3,2,1
public: /** Initialize your data structure here. */
    MyQueue() {} /** Push element x to the back of queue. */
    void push(int x)
    {
        while (!stk2.empty()) {
            stk1.push(stk2.top());
            stk2.pop();
        }
        stk1.push(x);
    } /** Removes the element from in front of queue and returns that element. */
    int pop()
    {
        while (!stk1.empty()) {
            stk2.push(stk1.top());
            stk1.pop();
        }
        int top = stk2.top();
        stk2.pop();
        return top;
    } /** Get the front element. */
    int peek()
    {
        while (!stk1.empty()) {
            stk2.push(stk1.top());
            stk1.pop();
        }
        return stk2.top();
    } /** Returns whether the queue is empty. */
    bool empty() { return stk1.empty() && stk2.empty(); }
};

本代码提交AC,用时3MS。
二刷。其实push的时候可以不需要把stk2的数据倒腾回stk1,stk2的数据始终是队列头的,stk1的数据始终是队列尾的,代码如下:

class MyQueue {
private:
    stack<int> stk1, stk2;
    void adjust()
    {
        while (!stk1.empty()) {
            stk2.push(stk1.top());
            stk1.pop();
        }
    }
public: /** Initialize your data structure here. */
    MyQueue() {} /** Push element x to the back of queue. */
    void push(int x) { stk1.push(x); } /** Removes the element from in front of queue and returns that element. */
    int pop()
    {
        if (stk2.empty())
            adjust();
        int ans = stk2.top();
        stk2.pop();
        return ans;
    } /** Get the front element. */
    int peek()
    {
        if (stk2.empty())
            adjust();
        int ans = stk2.top();
        return ans;
    } /** Returns whether the queue is empty. */
    bool empty() { return stk1.empty() && stk2.empty(); }
};

本代码提交AC,用时3MS。