1.首先是火车的重排问题:这个问题的大概意思就是给你一个关于n个正整数的排列和k个队列,判断是否可以通过这K个队列完成这n个数的排列,排列的结果是从n到1依次递减并且队列里面的数还需要依次递增。
首先我们可以简单的分析一下这个问题:需要明确的是,使用k个队列(也就是题目中的铁轨)是不一定可以完成数字的重排的。为什么呢?因为我们可以想到,假设数据非常的恶心,最多是n-1个队列。如果这个时候k<n-1,那么显然重排失败。
2.现在我们分析这个过程,其实程序并不是很难,只要把实现过程的每个细节想清楚了,那么就很简单了,之所以感觉无从下手,就是因为细节没有弄明白。我们首先把数据存在一个数组内,然后遍历这个数组。现在,我们将第一个元素取出来,那么这个元素只有两种可能:
(1)这个元素是1,那么可以直接出去了,因为符合条件。
(2)这个元素不是1,那么我们需要把它加入队列。
接着我们来取第二个元素,也是有两种可能
(1):这个元素是2,那么也是可以直接出去了
(2)这个元素不是2,那么我们需要把它加入队列。这个这个时候,问题来了,是加入之前第一个元素的队列,还是新的队列?规则告诉我们,需要做一个比较。如果这个元素比第一个元素大,那么就加入刚才上一个元素的队列,否则,加入新的队列。
依次类推,我们可以抽象出来这样的结果:
我们任取一个元素,会有两种结果
(1)这个元素可以直接出去
(2)这个元素需要加入队列
继续深入分析。
对于(1)我们该如何判断这个元素是否可以出去呢?这是很简单的,我们刚才不是在遍历数组吗?我们其实可以维护一个变量,这个变量的含义就是当前可以出队的元素。比如说,第一个出去的肯定是1,第二个是2,每出去一个我们让这个变量++,那样每次取出来元素我们只需要比较该元素是否等于这个变量当可。
(2)既然元素当前不能直接出去,那么我们就要把它加到队列里。如何加?规则已经告诉我们了。但是我们来思考一个问题,比如说这个数据:3 6 9 2 4 7 1 8 5。按照规则入队之后,第一个队列q1:3 6 9,q2:2 4 7。把这些加进去以后,继续加1,那么1是可以直接出去的,1出去之后呢?继续加8?当然不是,1出去之后肯定是2出去,然后是3,4..一直到不能出队,再继续加入元素。那么问题来了,1出去之后是2出去,但是你怎么知道2这个时候在哪?或者说一个更加本质的问题,你怎么知道2现在可以出去?换句话说也就是,2现在在不在队列里面是未知的。如何解决这个问题呢?其实也不难,我们只需要维护一个变量,这个变量代表了现在所有入队元素中的最小值,如果有这个变量,我们就可以完成这个操作。拿上边的例子,现在1出去之后,我们发现现在所有入队元素中最小值是2,OK,你也出去,更新这个最小值,因为我们在(1)里面维护了一个当前应该出队的变量,我们这个时候只需要比较这两个变量即可,相等就出去,不相等就继续入队。当然了,根据问题的需要我们还要有一个变量来记住当前最小值在哪个队列里面,以便于后边的操作。
其实上边的分析就是细节,只要把这些东西想清楚明白,写起来就很快了。总结起来我们需要维护的变量有三个:当前可以出队的元素,当前入队元素的最小值,以及这个最小值所在的队列的编号。操作我们可以分离成三个:(1)遍历数组判断是否可以出队(2)如果可以出队,将元素出队并更新我们维护的变量(3)不能出队将元素加入队列并更新维护的变量。然后封装即可,下边是代码实现:
一.手写的队列(注意!!不要把模板类的定义和实现写成多文件结构,目前的编译器不支持模板的多文件结构)
#pragma once
#include<iostream>
using namespace std;
template<class T>
struct linknode
{
T data;
linknode<T> *next;
linknode()
{
this->next = NULL;
}
linknode(T d)
{
this->data = d;
this->next = NULL;
}
};
template<class T>
class Queue
{
private:
linknode<T> *tail;
public:
Queue()
{
tail = new linknode<T>();
tail->next = tail;//采用一个循环的队列,维护尾部指针即可
}
bool isempty()
{
if (tail->next ==tail)
{
return true;
}
return false;
}
void push(T d)
{
linknode<T> *p = new linknode<T>(d);
p->next = tail->next;
tail->next = p;
tail = p;
}
T front()
{
if (isempty())
{
cout << "this queue is empty!" << endl;
exit(-1);
}
linknode<T> *p = tail->next;
T data = p->next->data;
return data;
}
T back()
{
if (isempty())
{
cout << "this queue is empty" << endl;
exit(-1);
}
return tail->data;
}
void pop()
{
linknode<T> *p = tail->next;
linknode<T> *q = p->next;
p->next = q->next;
if (q == tail)
tail = p;
delete q;
}
};
//二:重排的实现
#include"pch.h"
#include<iostream>
#include<queue>
#include<fstream>
#include<vector>
#include<string>
#include"Queue.h"
using namespace std;
class RA
{
private:
Queue<int>*data;
int min;//当前铁轨最小的车厢
int k;//铁轨数;
int numOfmin;//min的编号
vector<int>vec;//元素的存放数组
int n;//元素的数量
public:
RA() {};
RA(int k,string path);
int putOneOut();
int reSet(int k);
int cal();
~RA();
};
RA::RA(int k1,string path)
{
ifstream in;
in.open(path);
if (!in)
{
cout << "文件打开失败" << endl;
exit(-1);
}
int tem;
while (!in.eof())
{
in >> tem;
vec.push_back(tem);
}
in.close();
n = vec.size();
min = n + 1;
numOfmin = 0;
this->k = k1;
data = new Queue<int>[k1];
}
RA::~RA()
{
delete[] data;
}
//遍历数组,判断是否可以出轨
int RA::cal()
{
int cur = 1;
for (int i(0); i < n; i++)
{
if (vec[i] == cur)
{
cout << "第"<<vec[i]<<": 节车厢出轨" <<endl;
cur++;
while (min == cur)
{
putOneOut();
cur++;
if (cur == n + 1)
{
return 1;
}
}
}
else
{
if(!reSet(vec[i]))
return false;
}
}
return true;
}
//将元素出轨,并更新维护的变量
int RA::putOneOut()
{
int tem = data[numOfmin].front();
data[numOfmin].pop();
cout << "将第" << tem << " :从第 " << numOfmin + 1<<"个铁轨移出" << endl;
min = n + 1;
for (int i = 0; i < k; i++)
{
if (!data[i].isempty() && (data[i].front()< min))
{
min = data[i].front();
numOfmin = i;
}
}
return 1;
}
//将元素加入队列
int RA::reSet(int c)
{
int Track = k;
int MaxNum = 0;
int x;
for (int i = 0; i < k; i++)
{
if (!data[i].isempty())
{
x = data[i].back();
if (c > x && x > MaxNum)
{
MaxNum = x;
Track = i;
}
}
else
{
if (Track == k)
Track = i;
}
}
if (Track == k)
return false;
data[Track].push(c);
cout << "将第:" << c<<"个车厢移动到第" << Track + 1<<"铁轨" << endl;
if (c < min)
{
min = c;
numOfmin = Track;
}
return true;
}
int main()
{
RA r (3, "D://a.txt");
if (r.cal())
{
cout << "重排成功" << endl;
}
else
{
cout << "重排失败" << endl;
}
return 0;
}
最后是一个双队列模拟栈,其实很简单。不再解释,直接上代码
#include "stdafx.h"
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
/*使用队列来模拟栈:
使用两个队列,一个队列一直是处于空队列的状态。我们具体分析,例如1,2,3,4,5
1:首先q1是空,那么将元素加入q1,
2:之后是2,,过来之后应该是进入另一个队列,然后再将q1的1取出也丢进q2,q1空
3:3进来,进入q1,然后将q2中的所有元素取出加入q1,此时q2空
。。。。。。
以此类推
*/
class imitateStackByQueue
{
private:
queue<int> q1;
queue<int> q2;
public:
void push(int x)
{
if (q1.empty())
{
q1.push(x);
while (!q2.empty())
{
q1.push(q2.front());
q2.pop();
}
}
else
{
q2.push(x);
while (!q1.empty())
{
q2.push(q1.front());
q1.pop();
}
}
}
void pop() {
if (q1.empty())
{
q2.pop();
}
else
{
q1.pop();
}
}
int top()
{
if (q1.empty())
{
return q2.front();
}
else
{
return q1.front();
}
}
bool empty()
{
return (q1.empty() && q2.empty());
}
};
int main()
{
int n;
imitateStackByQueue q;
while (cin>>n&&n)
{
q.push(n);
}
while (!q.empty())
{
cout << q.top() << " ";
q.pop();
}
return 0;
}