2.2 队列

一、定义

  • 先进先出 (First In First Out)

    • 按照到达的顺序来释放元素

    • 所有的插入在表的一端(队首)进行,所有的删除都在表的另一端(队尾)进行

二、实现

1、创建

int S[n+1]; //S为最多能够容纳n个元素的队列
int head=0, tail=0; //head表示队首的位置,tail表示队尾的位置,队列为空时,head和tail位于同一位置;队列满时,(tail + 1)%(n + 1)==head。

2、判断是否为空

bool emp(){
  if (tail==head) return true;
  else return false;
}

3、判断是否为满

bool ful(){
  if ((tail + 1)%(n + 1)==head){
    return true;
  } 
  return false;
}

4、进队

//x进队
bool push(int x){
  if (ful()){
    return false;
  }
  S[tail]=x;
  tail = (tail + 1)%(n + 1);
  return true;
}

5、出队

bool pop(){
  if (emp()) {
    return false;
  } 
  head = (head - 1)%(n + 1);
  return true;
}

三、应用

1、农夫过河

(1)问题内容

“人狼羊菜”乘船过河:只有人能撑船,船只有两个位置(包括人),狼羊、羊菜不能在没有人时共处。

(2)思路

用状态位向量(x_1,x_2,x_3,x_4)表示物体的位置,x_1,x_2,x_3,x_4分别代表人、狼、羊、菜的位置,物体在起始处时,x_i=0;在对岸时,x_i=1​。问题变为从状态0000(整数0)出发,寻找全部由安全状态构成的状态序列,以状态1111(整数15)为最终目标。状态序列中每个状态都可以从前一状态通过农夫(可以带一样东西)划船过河的动作到达。序列中不能出现重复状态。

利用广度优先搜索,采用队列做辅助结构,把下一步有可能达到的状态都放在队列中,然后顺序取出对其分别处理,处理过程中再把下一步的状态放在队列中,……。由于队列的操作按照先进先出原则,因此只有前一步的所有情况都处理完后才能进入下一步。

  • 广度优先搜索:搜索该步的所有可能状态,再进一步考虑后面的各种情况——队列应用

  • 深度优先搜索:沿某一状态走下去,不行再回头——栈应用

(3)代码

a. 确定每个物体的位置
bool farmer(int status)
{ return ((status & 0x08) != 0); }
​
bool wolf(int status)
{ return ((status & 0x04) != 0); }
​
bool sheep(int status)
{ return ((status & 0x02) != 0); }
​
bool cabbage(int status)
{ return ((status & 0x01) != 0); }
b. 安全状态的判断
bool safe(int status) // 返回true:安全,false:不安全
{
  if ((goat(status) == cabbage(status)) &&
    (goat(status) != farmer(status)))
    return(false); // 羊吃白菜
  if ((goat(status) == wolf(status)) &&
    (goat(status) != farmer(status)))
    return(false); // 狼吃羊
    return(true); // 其它状态为安全
}
c.广度优先搜索
  • 定义一个整数队列moveTo,它的每个元素表示一个可以安全到达的中间状态。

  • 还需要定义一个数据结构记录已被访问过的各个状态,以及已被发现的能够到达当前这个状态的路径。用顺序表route 的第i 个元素记录状态i是否已被访问过。

    • 若route[i] 已被访问过,则在这个顺序表元素中记入前驱状态值; -1表示未被访问

    • route 的大小(长度)为16

void solve() {
  int movers, i, location, newlocation;
  vector<int> route(END+1, -1);
  // 记录已考虑的状态路径
  queue<int> moveTo;
  // 准备初值
  moveTo.push(0x00);
  route[0]=0;
  
while (!moveTo.empty() && route[15] == -1) {
  status = moveTo.front();
  moveTo.pop();
  for (movers = 1; movers <= 8; movers <<= 1) {
    // 农夫总是在移动,随农夫移动的也只能是在农夫同侧的东西
  if (farmer(status) == (bool)(status & movers)) {
    // 随农夫移动以后的状态
    newstatus = status ^ (0x08 | movers);
    // 安全的,并且未考虑过的走法
  if (safe(newstatus) && (route[newstatus] == -1)) {
    route[newstatus] = status;
    moveTo.push(newstatus); }
    }
  }
}
​
// 反向打印出路径
if (route[15] != -1) {
  cout << "The reverse path is : “ << endl;
  for (int status = 15; status >= 0; status = route[status]) {
    cout << "The status is : “ << status << endl;
    if (status == 0) break;
    }
  }
  else {
  cout << "No solution.“ << endl;
}

2、双向队列、单调队列

双向队列:一端既可以有元素入队,又有元素出队的队列 单调队列:队列中元素单调递增或递减的队列

给定一个长度为n(n<=10^6)的数组。有一个大小为k的滑动窗口从数组的最左端移动到最右端。你可以看到窗口中的k个数字。窗口每次向右滑动一个数字的距离。

输入 输入包括两行。 第一行包括n和k,分别表示数组的长度和窗口的大小。 第二行包括n个数字。

输出 输出包括两行。 第一行包括窗口从左至右移动的每个位置的最小值。 第二行包括窗口从左至右移动的每个位置的最大值。

思路:维护单调队列

int main(){
  int n,k;
  cin >> n >> k;
  vector<int> max, min;
  int ar[n];
  \\STL库中的双端队列
  deque<int> Q, P;
  \\记录输入的数组
  for (int i=0; i<n; ++i){
    int t;
    cin >> ar[i];
  }
​
  for (int i=0; i <n; ++i){
    if (Q.empty()){
      Q.push_back(i);
    } else {
      \\随着窗口移动,先进入的元素滑出窗口
      if (i - k >= Q.front()){
        Q.pop_front();
      }
      \\维护一个单调递增的队列
      while (!Q.empty() && ar[Q.back()]<=ar[i]){
        Q.pop_back();
      } 
      Q.push_back(i);
    }
    \\在已经形成长度为k的窗口时,记录每次移动时窗口中的最大值
    if (i>=k-1){
      max.push_back(ar[Q.front()]);
    }
  }
  
  for (int i=0; i <n; ++i){
    if (P.empty()){
      P.push_back(i);
    } else {
      if (i - k >= P.front()){
        P.pop_front();
      }
      while (!P.empty() && ar[P.back()]>=ar[i]){
        P.pop_back();
      } 
      P.push_back(i);
    }
    if (i>=k-1){
      min.push_back(ar[P.front()]);
    }
  }
​
  for (int i=0; i<min.size(); ++i){
    if (i+1<min.size()){
      cout << min[i] << " ";
    } else {
      cout << min[i] << endl;
    }
  }
  for (int i=0; i<max.size(); ++i){
    if (i+1<max.size()){
      cout << max[i] << " ";
    } else {
      cout << max[i];
    }
  }
}
  • 12
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值