2022年最后一天,复习了栈及栈的应用,转自邓俊辉的数据结构。
栈的实现
列表实现,插入和删除的成本低,但每个节点都要储存前一个节点和下一个节点的值。
template<typename T>
class Stack :public List<T> {
public:
void push(T const& e) { List<T>::insertAsLast(e); }
T pop() { return List<T>::remove(List<T>::last()); }
T& top() { return List<T>::last()->data; }
};
附列表节点和列表的实现
#define Rank int
template<typename T>
struct ListNode;
template<typename T>
using ListNodePosi = ListNode<T>*;
template<typename T>
struct ListNode
{
//成员
T data;
ListNodePosi<T> pred;
ListNodePosi<T> succ;
//构造函数
ListNode() {};
ListNode(T e, ListNodePosi<T> p = nullptr, ListNodePosi<T> s = nullptr) :
data(e), pred(p), succ(s) {};
//操作接口
ListNodePosi<T> insertAsPred(T const& e);//紧靠当前节点之前插入新节点
ListNodePosi<T> insertAsSucc(T const& e);//紧靠当前节点之后插入新节点
};
template<typename T>
ListNodePosi<T> ListNode<T>::insertAsPred(T const& e) {
ListNodePosi<T> newNode = new ListNode(e, this->pred, this);
this->pred->succ = newNode;
this->pred = newNode;
return newNode;
};
template<typename T>
ListNodePosi<T> ListNode<T>::insertAsSucc(T const& e) {
ListNodePosi<T> newNode = new ListNode(e, this, this->succ);
this->succ->pred = newNode;
this->succ=newNode;
return newNode;
};
列表成员及操作
#pragma once
#include"ListNode.h"
#include<iostream>
using namespace std;
template<typename T>
class List {
private:
int _size;
ListNodePosi<T> header, trailer;
protected:
void init();//列表创建时的初始化
int clear();//清除所有节点
void copyNode(ListNodePosi<T> p, int a);//复制列表中自位置p起的n项
public:
//构造函数
List() { init(); }//默认
List(T* A, int n);//给数据,构造列表
List(List<T> const& L);//复制构造
List(List<T> const& L, Rank r, int n);//复制列表L中自第r项起的第n项
List(ListNodePosi<T> p, int n);//复制列表中自位置p起的n项
//析构函数
~List();//清除所有节点,包括header和trailer
//只读访问接口
Rank size() const { return _size; }//规模
bool empty() const { return _size <= 0; }//判空
T& operator[](Rank r) const;//重载,支持寻秩访问,效率低
ListNodePosi<T> first() const { return header->succ; }//首节点
ListNodePosi<T> last() const { return trailer->pred; }//末节点
bool valid(ListNodePosi<T> p) //判断节点p是否对外合法
{
return p && (trailer != p) && (header != p);
};
ListNodePosi<T> find(T const&e) const//无序查找
{
return find(e, _size, trailer);
}
ListNodePosi<T> find(T const& e, int n, ListNodePosi<T> p) const;
ListNodePosi<T> search(T const&e) const
{
return search(e, _size, trailer);
}
ListNodePosi<T> search(T const& e, int n, ListNodePosi<T> p) const;
ListNodePosi<T> selectMax(ListNodePosi<T> p, int n);//在p及其n-1个后继中选出最大者
ListNodePosi<T> selectMax() { return selectMax(header->succ, _size); }
//可写访问接口
ListNodePosi<T> insertAsFrist(T const& e) {
//将e当作首节点插入
return insert(header, e);
};
ListNodePosi<T> insertAsLast(T const& e) {
//将e当作末节点插入
return insert(e, trailer);
};
ListNodePosi<T> insert(ListNodePosi<T> p, T const& e)
{ //将e当作p的后继插入
_size++; return p->insertAsSucc(e);
};
ListNodePosi<T> insert(T const& e, ListNodePosi<T> p) {
//将e当作p的前继插入
_size++; return p->insertAsPred(e);
};
T remove(ListNodePosi<T> p);//删除合法位置p的节点,返回被删除的节点数据
//遍历
void getInfo() {
cout << "节点个数 " << _size << endl;
ListNodePosi<T> temp = first();
while (temp != trailer) {
cout << temp->data << " ";
temp = temp->succ;
}
cout << endl;
}
};
template<typename T>
void List<T>::init() {
header = new ListNode<T>;
trailer = new ListNode<T>;
header->succ = trailer; header->pred = nullptr;
trailer->pred = header; trailer->succ = nullptr;
_size = 0;
}
template<typename T>
int List<T>::clear() {
int oldSize = _size;
while (0 < _size) remove(header->succ);
return oldSize;
};
template<typename T>
T List<T>::remove(ListNodePosi<T> p) {
T tempData = p->data;
p->succ->pred = p->pred;
p->pred->succ = p->succ;
delete p;
_size--;
return tempData;
};
template<typename T>
List<T>::~List() {
clear();
delete header; delete trailer;
};
template<typename T>
void List<T>::copyNode(ListNodePosi<T> p, int a) {
init();
while (a--) { insertAsLast(p->data); p = p->succ; }
};
template<typename T>
List<T>::List(List<T> const& L, Rank r, int n) {
ListNodePosi<T> p = L.first();
while (0 < r--) p = p->succ;
copyNode(p, n);
};
template<typename T>
List<T>::List(List<T> const& L) {
copyNode(L.first(), L._size);
};
template<typename T>
List<T>::List(ListNodePosi<T> p, int n) {
//复制列表中自位置p起的n项
copyNode(p, n);
};
template<typename T>
List<T>::List(T* A, int n) {
init();
for (int i = 0; i < n; i++)
insertAsLast(A[i]);
};
栈应用
进制转换
没啥说的,就是digit数组比较巧妙。避免了一个个转换的问题。
//n非负数,十进制转换为base进制,base<16
void convert(Stack<char>& s, __int64 n, int base) {
char digit[] = {'0','1','2','3','4','5','6','7','8','9'
,'A','B','C','D','E'};
if (n == 0) {
s.push(digit[0]); return;
}
while (n > 0) {
s.push(digit[n % base]);
n /= base;
}
}
括号匹配
用于多个对应的括号交叉混乱,看他合不合法。html的<p> <\p>之类的也是用这个方法。
//括号匹配,{ [ (
bool paren(const char exp[], int lo, int hi) {
Stack<char> S;
for (; lo < hi; lo++) {
if ((exp[lo] == '(') || (exp[lo] == '[') || (exp[lo] == '{'))
S.push(exp[lo]);
else if (S.empty())
return false;
else if (exp[lo] == ')')
{
if (S.top() == '(')
S.pop();
else
return false;
}
else if (exp[lo] == ']')
{
if (S.top() != '[')
return false;
else
S.pop();
}
else if (exp[lo] == '}')
{
if (S.top() != '{')
return false;
else
S.pop();
}
}
return S.empty();
}
中缀表达式求值
运算的时候,最后的两个运算数,要注意运算顺序,谁除谁,后出来的是被减数,先出来的是减数字。
精华还是在符号优先级的表。
#define N_OPTR 9
typedef enum
{
ADD, SUB, MUL, DIV, POW, FAC, L_P, R_P, EOE,
} Operator; //运算符集合
const char pri[N_OPTR][N_OPTR] =
{ //运算符优先等级 [栈顶] [当前]
/* |-------------------- 当 前 运 算 符 --------------------| */
/* + - * / ^ ! ( ) \0 */
/* -- + */ '>', '>', '<', '<', '<', '<', '<', '>', '>',
/* | - */ '>', '>', '<', '<', '<', '<', '<', '>', '>',
/* 栈 * */ '>', '>', '>', '>', '<', '<', '<', '>', '>',
/* 顶 / */ '>', '>', '>', '>', '<', '<', '<', '>', '>',
/* 运 ^ */ '>', '>', '>', '>', '>', '<', '<', '>', '>',
/* 算 ! */ '>', '>', '>', '>', '>', '>', ' ', '>', '>',
/* 符 ( */ '<', '<', '<', '<', '<', '<', '<', '=', ' ',
/* | ) */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
/* -- \0 */ '<', '<', '<', '<', '<', '<', '<', ' ', '='
};
Operator optr2rank(char op)
{ //由运算符转译出编号
switch (op)
{
case '+':
{
return ADD; //加
}
case '-':
{
return SUB; //减
}
case '*':
{
return MUL; //乘
}
case '/':
{
return DIV; //除
}
case '^':
{
return POW; //乘方
}
case '!':
{
return FAC; //阶乘
}
case '(':
{
return L_P; //左括号
}
case ')':
{
return R_P; //右括号
}
case '\0':
{
return EOE; //起始符与终止符
}
default:
{
exit(-1); //未知运算符
}
}
}
//中缀表达式求值
//读数
void readNumber(Stack<double>& optn, char*& p) {
bool isNegitive = false;
if (p[0] == '-') {
p++; isNegitive = true;
}
double result = 0;
//整数部分
while (isdigit(*p)) {
result = result * 10 + (int)(*(p++) - '0');
}
//小数部分
double wei = 0.1;
if ((*p) == '.') {
p++;
while (isdigit(*p)) {
result +=(*(p++) - '0')*wei;
wei *= 0.1;
}
}
if (isNegitive)
result *= -1;
optn.push(result);
}
//阶乘
int fac(int n) {
int res = 1;
while (n--)
res *= (n + 1);
return res;
}
//乘方
int fac(int a, int b) {
int res = 1;
while (b--) {
res *= a;
}
return res;
}
//运算符优先级
char getRank(char a,char b) {
return pri[optr2rank(a)][optr2rank(b)];
}
//计算
double calcu(double num1, char optr, double num2) {
if (optr == '+') return num1 + num2;
else if (optr == '-') return num1 - num2;
else if (optr == '*') return num1 * num2;
else if (optr == '/') return num1 / num2;
else if (optr == '^') return fac(num1, num2);
else return 0;
}
//运算主函数
double calcu(char *p) {
Stack<char> optr;
Stack<double> optn;
optr.push('\0');
while (!optr.empty()) {
if (isdigit(*p))
readNumber(optn, p);
else {
switch (getRank(optr.top(),*p))
{
case '<':
{
optr.push(*p); p++;
break;
}
case '=':
{
optr.pop(); p++; //脱括号并接收下一个字符
break;
}
case '>':{
char opt = optr.pop();
if (opt == '!') {
double nums1 = optn.pop();
optn.push(fac(nums1));
}
else {
double nums2 = optn.pop();
double nums1 = optn.pop();
optn.push(calcu(nums1, opt, nums2));
}
break;
}
}
}
}
return optn.top();
}