栈的定义:栈是一种重要的线性结构,栈是线性表的一种具体形式。
官方定义:栈(Stack)是一个后进先出(Last in first out,LIFO)的线性表,它要求只在表尾进行删除和插入操作。所谓的栈,其实也就是一个特殊的线性表(顺序表、链表),但是它在操作上有一些特殊的要求和限制:
栈的元素必须“后进先出”。
栈的操作只能在这个线性表的表尾进行。
注:对于栈来说,这个表尾称为栈的栈顶(top),相应的表头称为栈底(bottom)。
栈的插入操作(Push),叫做进栈,也称为压栈,入栈。
栈的删除操作(Pop),叫做出栈,也称为弹栈。
栈的顺序存储结构:
栈的顺序存储结构和栈的链式存储结构。
最开始栈中不含有任何数据,叫做空栈,此时栈顶就是栈底。然后数据从栈顶进入,栈顶栈底分离,整个栈的当前容量变大。数据出栈时从栈顶弹出,栈顶下移,整个栈的当前容量变小。
看一下数据结构:
typedef struct
{
ElemType *base;
ElemType *top;
int stackSize;
}sqStack;
它包含了三个元素:base ,top ,stackSize。其中base是指向栈底的指针变量,top是指向栈顶的指针变量,stackSize指示栈的当前可使用的最大容量。
创建一个栈
#define STACK_INIT_SIZE 100
initStack(sqStack *s)
{
s->base = (ElemType *)malloc( STACK_INIT_SIZE * sizeof(ElemType) );
if( !s->base )
exit(0);
s->top = s->base; // 最开始,栈顶就是栈底
s->stackSize = STACK_INIT_SIZE;
}
入栈操作又叫压栈操作,就是向栈中存放数据。
入栈操作要在栈顶进行,每次向栈中压入一个数据,top指针就要+1,知道栈满为止。
#define SATCKINCREMENT 10
void Push(sqStack *s, ElemType e)
{
// 如果栈满,追加空间
if( s->top – s->base >= s->stackSize )
{
s->base = (ElemType *)realloc(s->base, (s->stackSize + STACKINCREMENT) * sizeof(ElemType));
if( !s->base )
exit(0);
s->top = s->base + s->stackSize; // 设置栈顶
s->stackSize = s->stackSize + STACKINCREMENT; // 设置栈的最大容量
}
*(s->top) = e;
s->top++;
}
出栈操作就是在栈顶取出数据,栈顶指针随之下移的操作。
每当从栈内弹出一个数据,栈的当前容量就-1。
void Pop(sqStack *s,ElemType *e)
{
if( s->top == s->base) // 栈已空空是也
return;
*e = *--(s->top);
}
栈的另一种定义:
typedef int ElemType;
typedef struct
{
ElemType data[MAXSIZE];
int top; // 用于标注栈顶的位置
int stackSize;
}
不推荐用这种方法。
清空一个栈:
清空一个栈,就是将栈中的元素全部作废,但栈本身物理空间并不发生改变(不是销毁)。
因此我们只要将s->top的内容赋值为s->base即可,这样s->base等于s->top,也就表明这个栈是空的了。这个原理跟高级格式化只是但单纯地清空文件列表而没有覆盖硬盘的原理是一样的。
void ClearStack(sqStack *s){
s->top = s->base;
}
销毁一个栈:
销毁一个栈与清空一个栈不同,销毁一个栈是要释放掉该栈所占据的物理内存空间,因此不要把销毁一个栈与清空一个栈这两种操作混淆。
void DestroyStack(sqStack *s){
int i, len;
len = s->stackSize;
for( i=0; i < len; i++ ){
free( s->base );
s->base++;
}
s->base = s->top = NULL;
s->stackSize = 0;
}
计算栈的当前容量:
计算栈的当前容量也就是计算栈中元素的个数,因此只要返回s.top-s.base即可。
注意,栈的最大容量是指该栈占据内存空间的大小,其值是s.stackSize,它与栈的当前容量不是一个概念哦。
int StackLen(sqStack s)
{
return(s.top – s.base); // 初学者需要重点讲解
}
代码库(二进制转换十进制)
#include <iostream>
#include <cmath>
using namespace std;
#define STACK_INIT_SIZE 20
#define STACKINCREMENT 10
typedef char ElemType;
typedef struct
{
ElemType *base;
ElemType *top;
int stackSize;
}sqStack;
void InitStack(sqStack *s)
{
s->base = (ElemType *)malloc(STACK_INIT_SIZE * sizeof(ElemType));
if( !s->base )
{
exit(0);
}
s->top = s->base;
s->stackSize = STACK_INIT_SIZE;
}
void Push(sqStack *s, ElemType e)
{
if( s->top - s->base >= s->stackSize )
{
s->base = (ElemType *)realloc(s->base, (s->stackSize + STACKINCREMENT) * sizeof(ElemType));
if( !s->base )
{
exit(0);
}
}
*(s->top) = e;
s->top++;
}
void Pop(sqStack *s, ElemType *e)
{
if( s->top == s->base )
{
return;
}
*e = *--(s->top);
}
int StackLen(sqStack s)
{
return static_cast<int>(s.top - s.base);
}
int main()
{
ElemType c;
sqStack s{};
int len, i, sum = 0;
InitStack(&s);
cout << "请输入二进制数,输入#符号表示结束:\t";
cin >> c;
while( c != '#' )
{
Push(&s, c);
cin >> c;
}
getchar(); // 把'\n'从缓冲区去掉
len = StackLen(s);
cout << "栈的当前容量是: " << len << endl;
for( i=0; i < len; i++ )
{
Pop(&s, &c);
sum = static_cast<int>(sum + (c - 48) * pow(2, i));
}
cout << "转化为十进制数是: " << sum << endl;
return 0;
}
代码库(二进制转换十六进制)
#include <iostream>
#include <cmath>
using namespace std;
#define STACK_INIT_SIZE 20
#define STACKINCREMENT 10
typedef char ElemType;
typedef struct
{
ElemType *base;
ElemType *top;
int stackSize;
}sqStack;
// 函数功能:初始化栈
// 参数*s:栈的地址
void InitStack(sqStack *s)
{
s->base = (ElemType *)malloc(STACK_INIT_SIZE * sizeof(ElemType));
if( !s->base )
{
exit(0);
}
s->top = s->base;
s->stackSize = STACK_INIT_SIZE;
}
// 函数功能:入栈操作
// 参数*s:栈的地址
// 参数e:待压入栈的元素
void Push(sqStack *s, ElemType e)
{
if( s->top - s->base >= s->stackSize )
{
s->base = (ElemType *)realloc(s->base, (s->stackSize + STACKINCREMENT) * sizeof(ElemType));
if( !s->base )
{
exit(0);
}
s->top = s->base + s->stackSize;
s->stackSize = s->stackSize + STACKINCREMENT;
}
*(s->top) = e;
s->top++;
}
// 函数功能:弹栈操作
// 参数*s:栈的地址
// 参数e:存放从栈里弹出的数据
void Pop(sqStack *s, ElemType *e)
{
if( s->top == s->base )
{
return;
}
*e = *--(s->top);
}
// 函数功能:计算栈s的当前长度
// 参数s:栈
int StackLen(sqStack s)
{
return static_cast<int>(s.top - s.base);
}
int main()
{
ElemType c;
sqStack s1{};
sqStack s2{};
int len, i, j, sum = 0;
InitStack(&s1); // 初始化栈s1,用来存放二进制输入
cout << "请输入二进制数,输入#符号表示结束:\t";
cin >> c;
while( c != '#' )
{
if( c=='0' || c=='1' ) // 检查输入是否二进制
Push(&s1, c);
cin >> c;
}
getchar(); // 把'\n'从缓冲区去掉
len = StackLen(s1);
InitStack(&s2); // 初始化栈s2,用来存放转换的八进制
for( i=0; i < len; i+=4 )
{
for( j=0; j < 4; j++ )
{
Pop( &s1, &c ); // 取出栈顶元素
sum = static_cast<int>(sum + (c - 48) * pow(2, j));
if( s1.base == s1.top )
{
break;
}
}
switch( sum )
{
case 10: sum = 'A'; break;
case 11: sum = 'B'; break;
case 12: sum = 'C'; break;
case 13: sum = 'D'; break;
case 14: sum = 'E'; break;
case 15: sum = 'F'; break;
default: sum += 48;
}
Push(&s2, static_cast<ElemType>(sum));
sum = 0;
}
cout << "\n转化为十六进制数是: ";
while( s2.base != s2.top )
{
Pop( &s2, &c );
cout << c;
}
cout << endl;
return 0;
}
代码库(二进制转换八进制)
#include <iostream>
#include <cmath>
using namespace std;
#define STACK_INIT_SIZE 20
#define STACKINCREMENT 10
typedef char ElemType;
typedef struct
{
ElemType *base;
ElemType *top;
int stackSize;
}sqStack;
// 函数功能:初始化栈
// 参数*s:栈的地址
void InitStack(sqStack *s)
{
s->base = (ElemType *)malloc(STACK_INIT_SIZE * sizeof(ElemType));
if( !s->base )
{
exit(0);
}
s->top = s->base;
s->stackSize = STACK_INIT_SIZE;
}
// 函数功能:入栈操作
// 参数*s:栈的地址
// 参数e:待压入栈的元素
void Push(sqStack *s, ElemType e)
{
if( s->top - s->base >= s->stackSize )
{
s->base = (ElemType *)realloc(s->base, (s->stackSize + STACKINCREMENT) * sizeof(ElemType));
if( !s->base )
{
exit(0);
}
s->top = s->base + s->stackSize;
s->stackSize = s->stackSize + STACKINCREMENT;
}
*(s->top) = e;
s->top++;
}
// 函数功能:弹栈操作
// 参数*s:栈的地址
// 参数e:存放从栈里弹出的数据
void Pop(sqStack *s, ElemType *e)
{
if( s->top == s->base )
{
return;
}
*e = *--(s->top);
}
// 函数功能:计算栈s的当前长度
// 参数s:栈
int StackLen(sqStack s)
{
return static_cast<int>(s.top - s.base);
}
int main()
{
ElemType c;
sqStack s1{};
sqStack s2{};
int len, i, j, sum = 0;
InitStack(&s1); // 初始化栈s1,用来存放二进制输入
cout << "请输入二进制数,输入‘#’号表示结束:\t";
cin >> c;
while( c != '#' )
{
if( c=='0' || c=='1' ) // 检查输入是否二进制
Push(&s1, c);
cin >> c;
}
getchar(); // 把'\n'从缓冲区去掉
len = StackLen(s1);
InitStack(&s2); // 初始化栈s2,用来存放转换的八进制
for( i=0; i < len; i+=3 )
{
for( j=0; j < 3; j++ )
{
Pop( &s1, &c ); // 取出栈顶元素
sum = static_cast<int>(sum + (c - 48) * pow(2, j));
if( s1.base == s1.top )
{
break;
}
}
Push(&s2, static_cast<ElemType>(sum + 48));
sum = 0;
}
cout << "\n转化为八进制数是: ";
while( s2.base != s2.top )
{
Pop( &s2, &c );
cout << c;
}
cout << endl;
return 0;
}
栈的链式存储:
栈因为只是栈顶来做插入和删除操作,所以比较好的方法就是将栈顶放在单链表的头部,栈顶指针和单链表的头指针合二为一。
栈的链式存储结构定义:
teypedef struct StackNode
{
ElemType data; // 存放栈的数据
struct StackNode *next;
} StackNode, *LinkStackPtr;
teypedef struct LinkStack
{
LinkStackPrt top; // top指针
int count; // 栈元素计数器
}
对于栈链的Push操作,假设元素值为e的新结点是s,top为栈顶指针,我们得到如下代码:
Status Push(LinkStack *s,ElemType e)
{
LinkStackPtr p = (LinkStackPtr) malloc(sizeof(StackNode));
p->data = e;
p->next = s->top;
s->top = p;
s->count++;
return OK;
}
bool Push(LinkStack *s,ElemType e)
{
LinkStackPtr p = (LinkStackPtr) malloc(sizeof(StackNode));
p->data = e;
p->next = s->top;
s->top = p;
s->count++;
return true;
}
链栈的出战Pop操作,假设变量p用来存储要删除的栈顶结点,将栈顶指针下移一位,最后释放p即可。
Status Pop(LinkStack *s,ElemType *e)
{
LinkStackPtr p;
if( StackEmpty(*s)) // 判断是否为空栈
return ERROR;
*e = s->top->data;
p = s->top;
s->top = s->top->next;
free(p);
s->count--;
return OK;
}
bool Pop(LinkStack *s,ElemType *e)
{
LinkStackPtr p;
if( StackEmpty(*s)) // 判断是否为空栈
return false;
*e = s->top->data;
p = s->top;
s->top = s->top->next;
free(p);
s->count--;
return true;
}
RPN,逆波兰计算器
#include <iostream>
using namespace std;
#define STACK_INIT_SIZE 20
#define STACKINCREMENT 10
#define MAXBUFFER 10
typedef double ElemType;
typedef struct {
ElemType *base;
ElemType *top;
int stackSize;
} sqStack;
void InitStack(sqStack *s) {
s->base = (ElemType *) malloc(STACK_INIT_SIZE * sizeof(ElemType));
if (!s->base)
exit(0);
s->top = s->base;
s->stackSize = STACK_INIT_SIZE;
}
void Push(sqStack *s, ElemType e) {
// 栈满,追加空间
if (s->top - s->base >= s->stackSize) {
s->base = (ElemType *) realloc(s->base, (s->stackSize + STACKINCREMENT) * sizeof(ElemType));
if (!s->base)
exit(0);
s->top = s->base + s->stackSize;
s->stackSize = s->stackSize + STACKINCREMENT;
}
*(s->top) = e; // 存放数据
s->top++;
}
void Pop(sqStack *s, ElemType *e
) {
if (s->top == s->base)
return;
*e = *--(s->top); // 将栈顶元素弹出并修改栈顶指针
}
int StackLen(sqStack s) {
return static_cast<int>(s.top - s.base);
}
int main() {
sqStack s{};
char c;
double d, e;
char str[MAXBUFFER];
int i = 0;
InitStack( &s );
cout << "请按逆波兰表达式输入待计算数据,数据与运算符之间用空格隔开,以#作为结束标志: \t";
cin >> c;
while( c != '#' )
{
while( isdigit(c) || c=='.' ) // 用于过滤数字
{
str[i++] = c;
str[i] = '\0';
if( i >= 10 )
{
cout << "出错:输入的单个数据过大" << endl;
return -1;
}
cin >> c;
if( c == ' ' )
{
d = atof(str);
Push(&s, d);
i = 0;
break;
}
}
switch( c )
{
case '+':
Pop(&s, &e);
Pop(&s, &d);
Push(&s, d+e);
break;
case '-':
Pop(&s, &e);
Pop(&s, &d);
Push(&s, d-e);
break;
case '*':
Pop(&s, &e);
Pop(&s, &d);
Push(&s, d*e);
break;
case '/':
Pop(&s, &e);
Pop(&s, &d);
if( e != 0 )
{
Push(&s, d/e);
}
else
{
cout << "\n出错:除数为零" << endl;
return -1;
}
break;
default:break;
}
cin >> c;
}
Pop(&s, &d);
cout << "\n最终的计算结果为:" << d << endl;
return 0;
}
讲完了栈,现在开始看看队列。
队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
与栈相反,队列是一种先进先出(First In First Out, FIFO)的线性表。
与栈相同的是,队列也是一种重要的线性结构,实现一个队列同样需要顺序表或链表作为基础。
队列的数据结构定义:
typedef struct QNode {
ElemType data;
struct QNode *next;
} QNode, *QueuePrt;
typedef struct {
QueuePrt front, rear; // 队头、尾指针
} LinkQueue;
创建一个队列要完成两个任务:一是在内存中创建一个头结点,二是将队列的头指针和尾指针都指向这个生成的头结点,因为此时是空队列。
void initQueue(LinkQueue*q)
{
q->front=q->rear=(QueuePtr)malloc(sizeof(QNode));
if( !q->front )
exit(0);
q->front->next = NULL;
}
入队列:
InsertQueue(LinkQueue *q, ElemType e)
{
QueuePtr p;
p = (QueuePtr)malloc(sizeof(QNode));
if( p == NULL )
exit(0);
p->data = e;
p->next = NULL;
q->rear->next = p;
q->rear = p;
}
出队列:
DeleteQueue(LinkQueue *q, ELemType *e)
{
QueuePtr p;
if( q->front == q->rear )
return;
p = q->front->next;
*e = p->data;
q->front->next = p->next;
if( q->rear == p )
q->rear = q->front;
free(p);
}
销毁一个队列:
由于链队列建立在内存的动态区,因此当一个队列不再有用时应当把它及时销毁掉,以免过多地占用内存空间。
DestroyQueue(LinkQueue*q)
{
while( q->front )
{
q->rear =q->front->next;
free( q->front );
q->front = q->rear;
}
}
下面看一下循环队列:
定义一个循环队列
#define MAXSIZE 100
typedef struct
{
ElemType *base; // 用于存放内存分配基地址
//这里你也可以用数组存放
int front;
int rear;
};
初始化一个循环队列
void initQueue(cycleQueue *q)
{
q->base = (ElemType *) malloc (MAXSIZE * sizeof(ElemType));
if( !q->base )
exit(0);
q->front = q->rear = 0;
}
入队列操作
bool InsertQueue(cycleQueue *q,ElemType e)
{
if( (q->rear+1)%MAXSIZE ==q->front )
return false; // 队列已满
q->base[q->rear] = e;
q->rear = (q->rear+1) %MAXSIZE;
}
出队列操作
bool DeleteQueue(cycleQueue*q, ElemType *e)
{
if(q->front == q->rear )
return false; // 队列为空
*e= q->base[q->front];
q->front= (q->front+1) % MAXSIZE;
}
C++循环队列模板
#include <iostream>
using namespace std;
class Queue{
private:
int *data; //定义指向整型的指针,从而动态开辟内存
int head,tail,length,count; //head指向队首,tail指向队尾,length表示队列的长度,count用于记录队列中元素个数,从而判断队列是否已满
public:
explicit Queue (int length_input){ //构造函数,对新声明的队列对象进行初始化
data = new int [length_input]; //动态开辟100个整型数据的空间
length = length_input; //为队列的长度赋初始值
head = 0; //起初head为0,表示head值未改变的情况下,一直指向首元素
tail = -1; //tail初始值为-1,表示此时队列为空,无任何数据元素
count = 0;
}
~Queue(){
delete [] data; //析构函数,删除动态开辟的内存
}
void push(int element){ //入队操作,只能从队列的尾部插入数据元素
if(count < length){ //队列未满的时候才能插入,否则则插入失败
tail = (tail + 1) % length; //分两种情况,如果队尾指针此时并未指向队列的最后一位,那么队尾指针直接前移,而当队尾指针此时指向最后一位时
data[tail] = element; //那么当队列未满时,则队尾指针将跳转至数组起始位置,再将数据元素插入队尾指针指向的位置
++count; //入队成功,队列中元素数量加一
}
}
bool pop(){
if(count < 0){ //队列为空,出队失败
return false;
}
head = (head + 1) % length; //同样,根据循环队列的性质得出
--count; //出队成功,队列中元素数量减一
return true;
}
int top(){
if(count > 0){ //队列不为空的情况下才能获取队首元素
return data[head];
}else{
return false;
}
}
void output(){
for(int i = head;i != tail + 1;i = (i + 1)%length){ //从队首一直遍历到队尾,当遍历到最后一位时,跳转至数组起始位置
//特别注意此时循环截止的条件应该是i != tail + 1,因此可能tail的值小于head
cout << data[i] << " ";
}
cout << endl;
}
};
int main() {
Queue queue(100); //声明一个队列对象,并初始化
for(int i = 1;i <= 10;++i){
queue.push(i); //将1-10这10个数据元素依次插入队列中
}
queue.output(); //调用输出的方法
cout << "当前的队首元素为:" << queue.top() << endl;
queue.pop(); //出队
queue.output(); //调用输出的方法
cout << "出队操作后的队首元素为:" << queue.top() << endl;
queue.pop(); //出队
queue.output(); //调用输出的方法
cout << "出队操作后的队首元素为:" << queue.top() << endl;
return 0;
}
非模板循环队列
#include <iostream>
using namespace std;
#define MAXSIZE 100
typedef double ElemType;
typedef struct
{
ElemType *base; // 用于存放内存分配基地址
//这里你也可以用数组存放
int front;
int rear;
int count;
}cycleQueue;
void initQueue(cycleQueue *q)
{
q->base = (ElemType *) malloc (MAXSIZE * sizeof(ElemType));
if( !q->base )
exit(0);
q->front = q->rear = 0;
}
bool InsertQueue(cycleQueue *q,ElemType e)
{
if( (q->rear+1)%MAXSIZE ==q->front )
return false; // 队列已满
q->base[q->rear] = e;
q->rear = (q->rear+1) %MAXSIZE;
q->count++;
}
bool DeleteQueue(cycleQueue *q, ElemType *e)
{
if(q->front == q->rear )
return false; // 队列为空
*e= q->base[q->front];
q->front= (q->front+1) % MAXSIZE;
}
int main(){
cycleQueue queue{};
double d;
initQueue(&queue);
InsertQueue(&queue, 1);
InsertQueue(&queue, 2);
InsertQueue(&queue, 3);
InsertQueue(&queue, 4);
InsertQueue(&queue, 5);
DeleteQueue(&queue, &d);
cout << d << endl;
DeleteQueue(&queue, &d);
cout << d << endl;
DeleteQueue(&queue, &d);
cout << d << endl;
InsertQueue(&queue, 6);
InsertQueue(&queue, 7);
InsertQueue(&queue, 8);
InsertQueue(&queue, 9);
InsertQueue(&queue, 0);
DeleteQueue(&queue, &d);
cout << d << endl;
DeleteQueue(&queue, &d);
cout << d << endl;
DeleteQueue(&queue, &d);
cout << d << endl;
DeleteQueue(&queue, &d);
cout << d << endl;
DeleteQueue(&queue, &d);
cout << d << endl;
DeleteQueue(&queue, &d);
cout << d << endl;
return 0;
}