第九章 模板与群体数据
栈
栈类模板
栈是只能从一端访问的线性群体, 是一种后进先出的数据结构;
不同的程序对栈底和栈顶的策略有可能不同;
栈的基本状态
栈空, 栈满, 一般状态;(三种)
栈的基本操作
初始化
入栈
出栈
清空栈
访问栈顶元素
检测栈的状态(满, 空)
栈类模板实现示例
//Stack.h
#ifndef STACK_H
#define STACK_H
#include <cassert>
template <class T, int SIZE = 50>
class Stack {
private:
T list[SIZE];
int top;
public:
Stack();
void push(const T &item);
T pop();
void clear();
const T &peek() const;
bool isEmpty() const;
bool isFull() const;
};
//模板的实现
template <class T, int SIZE>
Stack<T, SIZE>::Stack() : top(-1) { }
template <class T, int SIZE>
void Stack<T, SIZE>::push(const T &item) {
assert(!isFull());
list[++top] = item;
}
template <class T, int SIZE>
T Stack<T, SIZE>::pop() {
assert(!isEmpty());
return list[top--];
}
template <class T, int SIZE>
const T &Stack<T, SIZE>::peek() const {
assert(!isEmpty());
return list[top]; //返回栈顶元素
}
template <class T, int SIZE>
bool Stack<T, SIZE>::isEmpty() const {
return top == -1;
}
template <class T, int SIZE>
bool Stack<T, SIZE>::isFull() const {
return top == SIZE - 1;
}
template <class T, int SIZE>
void Stack<T, SIZE>::clear() {
top = -1;
}
#endif //STACK_H
栈的应用举例
计算器(采用后缀输入法)
//Calculator.h
#ifndef CALCULATOR_H
#define CALCULATOR_H
#include "Stack.h" // 包含栈类模板定义文件
class Calculator { //计算器类
private:
Stack<double> s; // 操作数栈
void enter(double num); //将操作数num压入栈
//连续将两个操作数弹出栈,放在opnd1和opnd2中
bool getTwoOperands(double &opnd1, double &opnd2);
void compute(char op); //执行由操作符op指定的运算
public:
void run(); //运行计算器程序
void clear(); //清空操作数栈
};
#endif //CALCULATOR_H
//Calculator.cpp
#include "Calculator.h"
#include <iostream>
#include <sstream>
#include <cmath>
using namespace std;
//工具函数,用于将字符串转换为实数
inline double stringToDouble(const string &str) {
istringstream stream(str); //字符串输入流
double result;
stream >> result;
return result;
}
void Calculator::enter(double num) { //将操作数num压入栈
s.push(num);
}
bool Calculator::getTwoOperands(double &opnd1, double &opnd2) {
if (s.isEmpty()) { //检查栈是否空
cerr << "Missing operand!" << endl;
return false;
}
opnd1 = s.pop(); //将右操作数弹出栈
if (s.isEmpty()) { //检查栈是否空
cerr << "Missing operand!" << endl;
return false;
}
opnd2 = s.pop(); //将左操作数弹出栈
return true;
}
void Calculator::compute(char op) { //执行运算
double operand1, operand2;
bool result = getTwoOperands(operand1, operand2);
if (result) { //如果成功,执行运算并将运算结果压入栈
switch(op) {
case '+': s.push(operand2 + operand1); break;
case '-': s.push(operand2 - operand1); break;
case '*': s.push(operand2 * operand1); break;
case '/': if (operand1 == 0) { //检查除数是否为0
cerr << "Divided by 0!" << endl;
s.clear(); //除数为0时清空栈
} else
s.push(operand2 / operand1);
break;
case '^': s.push(pow(operand2, operand1)); break;
default: cerr << "Unrecognized operator!" << endl;
break;
}
cout << "= " << s.peek() << " "; //输出本次运算结果
} else
s.clear(); //操作数不够,清空栈
}
void Calculator::run() { //读入并处理后缀表达式
string str;
while (cin >> str, str != "q") {
switch(str[0]) {
case 'c': s.clear(); break;
case '-': //遇'-'需判断是减号还是负号
if (str.size() > 1)
enter(stringToDouble(str));
else
compute(str[0]);
break;
case '+': //遇到其它操作符时
case '*':
case '/':
case '^':
compute(str[0]); break;
default: //若读入的是操作数,转换为整型后压入栈
enter(stringToDouble(str)); break;
}
}
}
void Calculator::clear() { //清空操作数栈
s.clear();
}
//9_9.cpp
#include "Calculator.h"
int main() {
Calculator c;
c.run();
return 0;
}
队列
队列是只能向一端添加元素,
从另一端删除元素的线性群体;
是一种先进先出的结构;
插入端叫队尾
队列基本状态
队空, 队满, 一般状态;
循环队列
在想象中将数组弯曲成环形, 元素出队时,
后继元素不移动, 每当队尾达到数组最后一个元素时,
便再回到数组开头;
队列类模板代码示例
//Queue.h
#ifndef QUEUE_H
#define QUEUE_H
#include <cassert>
//类模板的定义
template <class T, int SIZE = 50>
class Queue {
private:
int front, rear, count; //队头指针、队尾指针、元素个数
T list[SIZE]; //队列元素数组
public:
Queue(); //构造函数,初始化队头指针、队尾指针、元素个数
void insert(const T &item); //新元素入队
T remove(); //元素出队
void clear(); //清空队列
const T &getFront() const; //访问队首元素
//测试队列状态
int getLength() const;//求队列长度
bool isEmpty() const;//判断队列空否
bool isFull() const;//判断队列满否
};
//构造函数,初始化队头指针、队尾指针、元素个数
template <class T, int SIZE>
Queue<T, SIZE>::Queue() : front(0), rear(0), count(0) { }
template <class T, int SIZE>
void Queue<T, SIZE>::insert (const T& item) {//向队尾插入元素
assert(count != SIZE);
count++; //元素个数增1
list[rear] = item; //向队尾插入元素
rear = (rear + 1) % SIZE; //队尾指针增1,用取余运算实现循环队列
}
template <class T, int SIZE> T Queue<T, SIZE>::remove() {
assert(count != 0);
int temp = front; //记录下原先的队首指针
count--; //元素个数自减
front = (front + 1) % SIZE;//队首指针增1。取余以实现循环队列
return list[temp]; //返回首元素值
}
template <class T, int SIZE>
const T &Queue<T, SIZE>::getFront() const {
return list[front];
}
template <class T, int SIZE>
int Queue<T, SIZE>::getLength() const { //返回队列元素个数
return count;
}
template <class T, int SIZE>
bool Queue<T, SIZE>::isEmpty() const { //测试队空否
return count == 0;
}
template <class T, int SIZE>
bool Queue<T, SIZE>::isFull() const { //测试队满否
return count == SIZE;
}
template <class T, int SIZE>
void Queue<T, SIZE>::clear() { //清空队列
count = 0;
front = 0;
rear = 0;
}
#endif //QUEUE_H
排序
排序概述
将元素的任意序列, 重新排列成一个按关键字有序的序列;
数据元素: 数据的基本单位; 在计算机中通常作为一个整体进行考虑;
一个数据元素可由若干数据项组成;
关键字: 数据元素中某个数据项的值, 用它可以标识(识别)一个数据元素;
在排序过程中需要完成两种基本操作:
比较两个数的大小
调整元素在序列中的位置
内部排序与外部排序
内部排序: 待排序的数据元素存放在计算机内存中进行的排序过程;
外部排序: 待排序的数据元素数量很大,
以致内存中一次不能容纳全部数据,
在排序过程中尚需对外存进行访问的排序过程;
几种简单的内部排序方法
插入排序
选择排序
交换排序
插入排序
详细在数据结构中
插入排序的基本思想
每一步将一个待排序元素按其关键字值的大小插入到已排序序列的适当位置上,
知道待排序元素插入完为止;
选择排序
交换排序
查找
查找
详细见数据结构与算法