【力扣】队列(先入先出)&& 栈(后入先出)

队列(先入先出)

按顺序处理数据
在这里插入图片描述

设计循环队列

在这里插入图片描述

#include <vector>
class MyCircularQueue {
public:
    MyCircularQueue(int k) {
        Queue.reserve(k);
        size=0;
        length=k;
        head=-1;
        tail=-1;
    }
    
    bool enQueue(int value) {
        if(!isFull()){
            size++;
            if(isEmpty()){
                tail++;
                head++;
            }
            else tail+=1;
            if(tail>=length) tail=0;
            Queue[tail]=value;
            return true;
        }else return false;
    }
    
    bool deQueue() {
        if(!isEmpty()){
            size--;
            if(head==tail){
                head=-1;
                tail=-1;
                return true;
            }
            head+=1;
            if(head>=length) head=0;
            return true;
        }else return false;
    }
    
    int Front() {
        if(isEmpty()) return -1;
        else return Queue[head];
    }
    
    int Rear() {
        if(isEmpty()) return -1;
        else return Queue[tail];
    }
    
    bool isEmpty() {
        if(head==-1&&tail==-1) return true;
        else return false;
    }
    
    bool isFull() {
        if(size==length) return true;
        else return false;
    }
private:
    vector<int> Queue;
    int size;
    int head;
    int tail;
    int length;
};

/**
 * Your MyCircularQueue object will be instantiated and called as such:
 * MyCircularQueue* obj = new MyCircularQueue(k);
 * bool param_1 = obj->enQueue(value);
 * bool param_2 = obj->deQueue();
 * int param_3 = obj->Front();
 * int param_4 = obj->Rear();
 * bool param_5 = obj->isEmpty();
 * bool param_6 = obj->isFull();
 */

广度优先搜索(Breadth First Search)

  1. 结点的处理顺序是什么?

在第一轮中,我们处理根结点。在第二轮中,我们处理根结点旁边的结点;在第三轮中,我们处理距根结点两步的结点;等等等等。

与树的层序遍历类似,越是接近根结点的结点将越早地遍历。

如果在第 k 轮中将结点 X 添加到队列中,则根结点与 X 之间的最短路径的长度恰好是 k。也就是说,第一次找到目标结点时,你已经处于最短路径中。

  1. 队列的入队和出队顺序是什么?

如上面的动画所示,我们首先将根结点排入队列。然后在每一轮中,我们逐个处理已经在队列中的结点,并将所有邻居添加到队列中。值得注意的是,新添加的节点不会立即遍历,而是在下一轮中处理。

结点的处理顺序与它们添加到队列的顺序是完全相同的顺序,即先进先出(FIFO)。这就是我们在 BFS 中使用队列的原因。

模板 I(Java)

/**
 * Return the length of the shortest path between root and target node.
 */
int BFS(Node root, Node target) {
    Queue<Node> queue;  // store all nodes which are waiting to be processed
    int step = 0;       // number of steps neeeded from root to current node
    // initialize
    add root to queue;
    // BFS
    while (queue is not empty) {
        step = step + 1;
        // iterate the nodes which are already in the queue
        int size = queue.size();
        for (int i = 0; i < size; ++i) {
            Node cur = the first node in queue;
            return step if cur is target;
            for (Node next : the neighbors of cur) {
                add next to queue;
            }
            remove the first node from queue;
        }
    }
    return -1;          // there is no path from root to target
}

模板 II(Java)

有时,确保我们永远不会访问一个结点两次很重要。否则,我们可能陷入无限循环。如果是这样,我们可以在上面的代码中添加一个哈希集来解决这个问题。这是修改后的伪代码:

/**
 * Return the length of the shortest path between root and target node.
 */
int BFS(Node root, Node target) {
    Queue<Node> queue;  // store all nodes which are waiting to be processed
    Set<Node> used;     // store all the used nodes
    int step = 0;       // number of steps neeeded from root to current node
    // initialize
    add root to queue;
    add root to used;
    // BFS
    while (queue is not empty) {
        step = step + 1;
        // iterate the nodes which are already in the queue
        int size = queue.size();
        for (int i = 0; i < size; ++i) {
            Node cur = the first node in queue;
            return step if cur is target;
            for (Node next : the neighbors of cur) {
                if (next is not in used) {
                    add next to queue;
                    add next to used;
                }
            }
            remove the first node from queue;
        }
    }
    return -1;          // there is no path from root to target
}

有两种情况你不需要使用哈希集:

  1. 你完全确定没有循环,例如,在树遍历中;
  2. 你确实希望多次将结点添加到队列中。

栈(后入先出)

模板

#include <iostream>

class MyStack {
    private:
        vector<int> data;               // store elements
    public:
        /** Insert an element into the stack. */
        void push(int x) {
            data.push_back(x);
        }
        /** Checks whether the queue is empty or not. */
        bool isEmpty() {
            return data.empty();
        }
        /** Get the top item from the queue. */
        int top() {
            return data.back();
        }
        /** Delete an element from the queue. Return true if the operation is successful. */
        bool pop() {
            if (isEmpty()) {
                return false;
            }
            data.pop_back();
            return true;
        }
};

int main() {
    MyStack s;
    s.push(1);
    s.push(2);
    s.push(3);
    for (int i = 0; i < 4; ++i) {
        if (!s.isEmpty()) {
            cout << s.top() << endl;
        }
        cout << (s.pop() ? "true" : "false") << endl;
    }
}

最小栈

在这里插入图片描述

解法一:用vector模拟栈

#include<vector>
#include<climits>
class MinStack {
public:
    MinStack() {
        min=INT_MAX;
    }
    
    void push(int val) {
        mystack.push_back(val);
    }
    
    void pop() {
        if(!mystack.empty()) mystack.pop_back();
    }
    
    int top() {
        if(!mystack.empty()) return mystack.back();
        else return 0;
    }
    
    int getMin() {
        if(!mystack.empty()){
            min=mystack[0];
            for(int i=0;i<mystack.size();i++){
                if(mystack[i]<min) min=mystack[i];
            }
        }
        return min;
    }
private:
    vector<int> mystack;
    int min;
};

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack* obj = new MinStack();
 * obj->push(val);
 * obj->pop();
 * int param_3 = obj->top();
 * int param_4 = obj->getMin();
 */

不足:效率很低!

解法二:双栈解决问题

#include<stack>
#include<climits>
class MinStack {
public:
    MinStack() {
    }
    
    void push(int val) {
        mystack.push(val);
        if(stack2.empty()||val<=stack2.top()) stack2.push(val);
    }
    
    void pop() {
        if(!stack2.empty()&&mystack.top()==stack2.top()){
            stack2.pop();
        } 
        if(!mystack.empty()) mystack.pop();
    }
    
    int top() {
        if(!mystack.empty()) return mystack.top();
        else return 0;
    }
    int getMin() {
        if(stack2.size()) return stack2.top();
        else return 0;
    }
private:
    stack<int> mystack;
    stack<int> stack2;
};

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack* obj = new MinStack();
 * obj->push(val);
 * obj->pop();
 * int param_3 = obj->top();
 * int param_4 = obj->getMin();
 */

另一个栈储存最小值

括号对称

在这里插入图片描述

#include<stack>
#include<string>
class Solution {
public:
    bool isValid(string s) {
        if(s.length()%2) return false;
        for(int i=0;i<s.length();i++){
            if(s[i]=='{')stack1.push('}');
            else if(s[i]=='[') stack1.push(']');
            else if(s[i]=='(') stack1.push(')');
            else if(stack1.empty()||s[i]!=stack1.top()){
                return false;
            }else stack1.pop();
        }
        if(!stack1.empty()) return false;
        else return true;
    }
private:
    stack<char>stack1;
};

每日温度

在这里插入图片描述

暴力解法效率极低,易超时

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        vector<int> answer;
        answer.resize(temperatures.size());//resize可以调整capacity
        //cout<<answer.capacity()<<endl;
        int k=0;
        for(int i=0;i<temperatures.size();i++){
            int cnt=0;
            for(int j=i+1;j<temperatures.size();j++){
                if(temperatures[j]>temperatures[i]) 
                    {answer[k]=j-i;k++;cnt=1;break;}
            }
            if(cnt==0){answer[k]=0;k++;}
        }
        answer[temperatures.size()-1]=0;
        return answer;
    }
};

用单栈解决:

#include<stack>
#include<vector>
class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& T) {
        stack<int> mystack;
        vector<int> answer(T.size());
        for(int i=0;i<T.size();i++){
            if(mystack.empty()||T[i]<=T[mystack.top()]){
                mystack.push(i);
            }else{
                while(!mystack.empty()){
                    if(T[mystack.top()]>=T[i]) {mystack.push(i);break;}
                    answer[mystack.top()]=i-mystack.top();
                    mystack.pop();
                }
                mystack.push(i);
            }
        }

        return answer;
    }
};

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

逆波兰表达式

在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<long long> Stack;
        for(int i=0;i<tokens.size();i++){
            if(tokens[i]=="+"||tokens[i]=="-"||tokens[i]=="/"||tokens[i]=="*"){
                long long a=Stack.top();
                Stack.pop();
                long long b=Stack.top();
                Stack.pop();
                if(tokens[i]=="+") Stack.push(a+b);
                else if(tokens[i]=="-") Stack.push(b-a);
                else if(tokens[i]=="/") Stack.push(b/a);
                else if(tokens[i]=="*") Stack.push(b*a);
            }else{
                int a=stoi(tokens[i]);
                Stack.push(a);
            }
        }
        return Stack.top();
    }
};

DFS(深度优先搜索)

DFS - 模板 I

正如我们在本章的描述中提到的,在大多数情况下,我们在能使用 BFS 时也可以使用 DFS。但是有一个重要的区别:遍历顺序。
与 BFS 不同,更早访问的结点可能不是更靠近根结点的结点。因此,你在 DFS 中找到的第一条路径可能不是最短路径。

/*
 * Return true if there is a path from cur to target.
 */
boolean DFS(Node cur, Node target, Set<Node> visited) {
    return true if cur is target;
    for (next : each neighbor of cur) {
        if (next is not in visited) {
            add next to visted;
            return true if DFS(next, target, visited) == true;
        }
    }
    return false;
}

DFS - 模板 II

递归解决方案的优点是它更容易实现。 但是,存在一个很大的缺点:如果递归的深度太高,你将遭受堆栈溢出。 在这种情况下,您可能会希望使用 BFS,或使用显式栈实现 DFS。

/*
 * Return true if there is a path from cur to target.
 */
boolean DFS(int root, int target) {
    Set<Node> visited;
    Stack<Node> s;
    add root to s;
    while (s is not empty) {
        Node cur = the top element in s;
        return true if cur is target;
        for (Node next : the neighbors of cur) {
            if (next is not in visited) {
                add next to s;
                add next to visited;
            }
        }
        remove cur from s;
    }
    return false;
}

小结

小结
在前面的章节中,我们介绍了两个数据结构:队列和栈

  1. 队列
    队列是一种 FIFO 式的数据结构:第一个元素将被首先处理。有两个重要操作:入队和出队。我们可以使用带有两个指针的动态数组来实现队列。
    我们可以使用广度优先搜索(BFS)。队列还有一些重要的扩展,例如:
    - 双端队列
    - 优先队列


  2. 栈是一种 LIFO 式的数据结构:最后一个元素将被首先处理。有两个重要操作:push 和 pop。栈的实现非常简单,使用动态数组就足以实现栈。
    当满足 LIFO 原则时,我们使用栈。深度优先搜索(DFS)是栈的一个重要应用。

  3. 总结
    理解和比较以下几组概念:
    - FIFO 和 LIFO;
    - 队列 和 栈;
    - BFS 和 DFS。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是力扣LeetCode)上的经典问题:两数之和(Two Sum)的 C 语言解法。 ```c #include <stdio.h> #include <stdlib.h> int* twoSum(int* nums, int numsSize, int target, int* returnSize) { int i, j; int* result = (int*)malloc(sizeof(int) * 2); *returnSize = 2; for (i = 0; i < numsSize - 1; i++) { for (j = i + 1; j < numsSize; j++) { if (nums[i] + nums[j] == target) { result[0] = i; result[1] = j; return result; } } } return NULL; } int main() { int nums[] = {2, 7, 11, 15}; int target = 9; int returnSize; int* result = twoSum(nums, sizeof(nums) / sizeof(nums[0]), target, &returnSize); if (result != NULL) { printf("[%d, %d]\n", result[0], result[1]); free(result); } return 0; } ``` 这个程序首先定义了一个函数 `twoSum()`,它接受一个整数数组 `nums`,数组的长度 `numsSize`,目标值 `target` 和一个指向整数变量的指针 `returnSize`。这个函数的目的是在给定数组中找到两个数,它们的和等于目标值,并返回这两个数的下标。 函数内部使用了两个嵌套的循环来枚举数组中的所有可能的组合,并检查它们的和是否等于目标值。如果找到了符合条件的组合,就把它们的下标存储在一个动态分配的整数数组中,并返回这个数组的指针。如果没有找到符合条件的组合,就返回空指针。 在 `main()` 函数中,我们定义了一个测试用例,即整数数组 `{2, 7, 11, 15}` 和目标值 `9`。然后我们调用 `twoSum()` 函数,传入这个测试用例,并打印出返回的结果。 值得注意的是,我们在 `twoSum()` 函数内部动态分配了一个整数数组 `result`,然后在 `main()` 函数中使用 `free()` 函数释放了它的内存,以避免内存泄漏。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值