上海交大ACM班C++算法与数据结构——数据结构之队列
1.队列的定义
- 先进先出的线性表
- 基本操作:
- 创建
- 入队
- 出队
- 读取
- 判断是否为空 - 虚构类实现
template <class elemType>
class queue{
public:
virtual bool isEmpty() = 0; //判队空
virtual void enQueue(const elemType &x) = 0; //进队
virtual elemType deQueue() = 0; //出队
virtual elemType getHead() = 0; //读队头元素
virtual ~queue() {} //虚析构函数
};
2.顺序实现
- 循环队列:头指针、尾指针都可以移动,既不会浪费空间(数据固定,出队时头指针移动),也不需要移动大量元素(头指针固定,出队时数据移动)
- 类定义
template <class elemType>
class seqQueue: public queue<elemType> {
private:
elemType *elem;
int maxSize;
// 队头和队尾
int front, rear;
void doubleSpace();
public:
seqQueue(int size = 10);
// 析构函数:收回动态数组
~seqQueue() { delete [] elem ; }
// 判队列是否为空:队头是否等于队尾
bool isEmpty() { return front == rear; }
//进队
void enQueue(const elemType &x);
//出队
elemType deQueue();
// 访问队头元素
elemType getHead() { return elem[(front + 1) % maxSize]; }
};
- 构造函数
template <class elemType>
seqQueue<elemType>::seqQueue(int size) {
elem = new elemType[size];
maxSize = size;
front = rear = 0;
}
- deQueue函数:出队
template <class elemType>
elemType seqQueue<elemType>::deQueue() {
front = (front + 1) % maxSize;
return elem[front];
}
- enQueue函数:入队
template <class elemType>
void seqQueue<elemType>::enQueue(const elemType &x) {
if ((rear + 1) % maxSize == front)
doubleSpace();
rear = (rear + 1) % maxSize;
elem[rear] = x;
}
- doubleSpace函数
template <class elemType>
void seqQueue<elemType>::doubleSpace() {
elemType *tmp =elem;
elem = new elemType[2 * maxSize];
for (int i = 1; i < maxSize; ++i)
elem[i] = tmp[(front + i) % maxSize];//复制到新数组的时候将front调整到数组最前端
front = 0;
rear = maxSize - 1;
maxSize *= 2;
delete [] tmp;
}
-
例题
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1000005
using namespace std;
int n;
int q[N], l, r;
int main() {
int n;
scanf("%d", &n);
l = r = 0;
for (int i = 1; i <= n; ++i) {
int x;
scanf("%d", &x);
q[r++] = x;
}
int flag = 0;
while (l < r) {
int x = q[l++];
if (!flag) printf("%d ", x);
else q[r++] = x;
flag ^= 1;
}
return 0;
}
3.链接队列
- 不带头结点的单链表(同时记录头尾结点的位置)
- 队头:表头,表头插入和删除都方便,表尾不方便删除
队尾:表尾
- 类定义
template <class elemType>
class linkQueue: public queue<elemType> {
private:
struct node {
elemType data;
node *next;
node(const elemType &x, node *N = NULL){data = x; next = N;}
node():next(NULL) {}
~node() {}
};
node *front, *rear;
public:
linkQueue() { front = rear = NULL; }
// 析构函数,和链表的析构函数类似
~linkQueue();
bool isEmpty() { return front == NULL; }
void enQueue(const elemType &x);
elemType deQueue();
elemType getHead() { return front->data; }
};
- enQueue函数:入队(要单独考虑空队时候入队的情况)
template <class elemType>
void linkQueue<elemType>::enQueue(const elemType &x) {
if (rear == NULL)
front = rear = new node(x);
else
rear = rear->next = new node(x);
}
- deQueue函数:出队(要单独考虑出队后队为空的情况)
template <class elemType>
elemType linkQueue<elemType>::deQueue() {
node *tmp = front;
elemType value = front->data;
front = front->next;
delete tmp;
// 判断是否为空
if (front == NULL) rear = NULL;
return value;
}
- 性能分析:所有操作的时间复杂度都是O(1)
4.列车车厢重排问题
// in表示输入轨道中车厢的排列,总共n个车厢,通过k个缓冲轨道
void arrange(int in[], int n, int k) {
linkQueue<int> *buffer = new linkQueue<int>[k];
int last = 0;
// 依次把每节车厢放入缓冲轨道,然后检查能否将缓冲轨道中的车厢移动到输出轨道
for (int i = 0; i < n; ++i) {
// 如果车厢放不进去,表示重排失败,不必再进行下去
if (!putBuffer(buffer, k, in[i])) return;
checkBuffer(buffer, k, last);
}
}
//putBuffer函数:找合适的轨道
//基本要求:轨道上最后一节车厢的编号小于进入的车厢编号,没有满足基本要求的轨道,则启用一条新轨道
//最优要求:多个轨道满足时,选择其中最后一节车厢编号最大的一个
//例如处理车厢8,现有轨道情况
//轨道1: 2、5
//轨道2:3、7
//8应该放入轨道2。如果放入轨道1,接入后面一节车厢是6号,则必须启用一根新的轨道
// 把车厢in,放入缓冲轨道,buffer是存储缓冲轨道队列的数组,size表示缓冲轨道的数量
// 如果车厢能放入合适的缓冲轨道则返回true,否则返回false
bool putBuffer(linkQueue<int> *buffer, int size, int in) {
// 希望把车厢放到编号为avail的缓冲轨道上,max表示所有合适轨道队尾编号最大的车厢
int avail = -1, max = 0;
for (int j = 0 ; j < size; ++j) {
if (buffer[j].isEmpty()) { if (avail == -1) avail = j; }
else if (buffer[j].getTail() < in && buffer[j].getTail() > max) {
avail = j;
max = buffer[j].getTail();
}
}
if (avail != -1) {
buffer[avail].enQueue(in);
cout << in << "移入缓冲区 " << avail << endl;
return true;
} else {
cout << "无可行的方案" << endl;
return false;
}
}
//checkBuffer函数
// 检查能否将缓冲轨道上的车厢移动到输出轨道,last代表输出轨道上最后一列车厢的标号
void checkBuffer( linkQueue<int> *buffer, int size, int &last) {
// 表示需要检查缓冲轨道
bool flag = true;
while (flag) {
flag = false;
for (int j = 0; j < size; ++j) {
if (! buffer[j].isEmpty() && buffer[j].getHead() == last + 1) {
cout << "将" << buffer[j].deQueue() << "从缓冲区" << j << "移到出轨" << endl;
++last;
// 有车厢出轨后再次将flag设置为true
flag = true;
break;
}
}
}
}
- 生成[a,b]间均匀分布的随机数:
rand*(b-a+1)/(RAND_MAX+1)+a
(分成均匀的单位段)
5.例题
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100005
#define M 305
using namespace std;
int n, m, t;
int team[N];
int q[M][N], l[M], r[M];
int main() {
scanf("%d%d", &n, &m);
for (int i = 0; i < n; ++i) scanf("%d", &team[i]);
scanf("%d", &t);
char op[10];
int x;
while (t--) {
scanf("%s", op);
if (op[1] == 'u') {
scanf("%d", &x);
int tx = team[x];
if (l[tx] == r[tx]) q[m][r[m]++] = tx;
q[tx][r[tx]++] = x;
} else {
int tx = q[m][l[m]];
printf("%d\n", q[tx][l[tx]++]);
if (l[tx] == r[tx]) ++l[m];
}
}
return 0;
}
6.块状链表
-
概念:链表结点内顺序存储(每个结点存放字符串中一段连续的字符,而不是单个字符)
-
优点:提高了空间的利用率(加大了数据的比例,减小了指针的比例,每个指针要占用4个字节的空间)
-
缺点:插入和删除时会引起数据的大量移动
-
改进方法:允许节点有一定的空闲空间(空间换时间)
-
应用:字符串处理(C中用固定大小的数组处理字符串,常常会因为超出数组大小出现不可预知的错误,C++ 中用大小可变的空间处理字符串)
-
例题
#include <iostream>
#include <string>
using namespace std;
int main() {
int t, len;
cin >> t;
while (t--) {
string a, b, ans;
cin >> a >> b;
len = a.length();
ans = a.substr(0, len / 2) + b + a.substr(len / 2);
cout << ans << endl;
}
return 0;
}