数据结构
栈
什么是栈?---栈(stack)是限定仅在表尾进行插入或者删除的线性表,分为栈顶,栈低。
不含元素的空表称为空栈。因为栈限定在表尾进行插入或者删除,所以栈又被称为先出后出
的线性表。
PS:代码区内的cout语句仅用来帮助你更好的理解栈的工作进程,实际手写栈的时候不需要加入它
1.顺序栈
栈和线性表类似,也有两种存储表示方法顺序栈和链栈,链栈的操作是线性表操作的特例。
顺序栈即栈的顺序存储结构是利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时设top指示栈顶元素在顺序栈中的位置,top = -1表示空栈。
由于栈在使用的过程中所需要的大小难以估计,所以通常是先为栈分配一个MAX_SIZE基本容量,我们以下述类型说明作为顺序栈(C++)的定义:
初始化栈
在类中,我们用构造函数来初始化栈,top=-1来标记为空栈,new一个地址给data数组(此处以字符数组为例),不要忘记析构函数delete[] data,要释放资源。
Stack::Stack()
{
size = MAX_SIZE;
top = -1;
data = new char[MAX_SIZE];//缺省构造函数分配默认最大内存空间
}
Stack::Stack(int s)
{
size = s;
top = -1;
data = new char[size];//根据指定大小s分配栈的内存空间
}
Stack::~Stack()
{
delete[]data;//内存回收
}
栈满与栈空
size-1位字符数组的最后一个元素的下标,当top==size-1,即入栈元素已经达到最大值了,为满栈。
若top==-1,即没有元素入栈,为空栈。
bool Stack::isFull()
{
if (top >= size-1)
{
return 1;//栈已满
}
else
return 0;//栈未满
}
bool Stack::isEmpty()
{
if (top == -1)
return 1;//栈为空
else
return 0;//栈已有元素
}
入栈与出栈
入栈时,需要先考虑栈是否为满栈状态。若为满栈,则不能继续入栈,若不为满栈,才可以继续入栈;每次加入一个元素,top++,即top往后移动一位,并将该元素ch赋值给此时对应的字符数组中---data[top]
出栈时,需要先考虑栈是否为空栈状态。若为空栈,则不能继续出栈,若不为空栈,才可以继续出栈;每次弹出一个元素(实际上该元素还在之前的位置,只是top索引减小了,搜索不到,相当于弹出该元素),top--,即top往前移动一位,返回data[top+1]这个被“弹出”的元素(top已经减减过了,所以要加一恢复它,得到弹出的元素)
void Stack::push(char ch)
{
if (isFull())//解决数据上溢问题
{
cout << "栈已满,无法入栈" << endl;
}
else
{
top++;
data[top] = ch;
cout << "已将元素" << ch << "压入栈中" << endl;
cout << "此时top= " << top << endl;
}
}
char Stack::pop()
{
if (!isEmpty())
{
cout << "出栈中..." << endl;
top--;
cout << "此时top= " << top << endl;
return data[top + 1];
}
else//解决数据下溢问题
{
cout << "栈已为空,无法继续出栈" << endl;
}
}
弹出元素但不出栈
与上面的出栈类似,只是不用top--。
char Stack::getTop()
{
if (top != -1)
{
cout << "获得栈顶元素中..." << endl;
cout << "此时top= " << top << endl;
cout << "栈顶元素为:";
return data[top];
}
else
{
cout << "栈已为空,无法获得栈顶元素" << endl;
}
}
完整的顺序栈
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int MAX_SIZE = 100;
class Stack//以存储字符型数据为例
{
private:
char* data;//线性表,指针变量指向栈的数组,保存首地址
int size;//堆栈的实际大小
int top;//栈顶
public:
Stack();
Stack(int s);
~Stack();
void push(char ch);//入栈
char pop();//出栈并返回栈顶元素
char getTop();//获得栈顶元素但不出栈
bool isEmpty();//栈是否为空
bool isFull();//栈是否满
void setNull(int len);//设置栈为空
void display();
};
Stack::Stack()
{
size = MAX_SIZE;
top = -1;
data = new char[MAX_SIZE];//缺省构造函数分配默认最大内存空间
}
Stack::Stack(int s)
{
size = s;
top = -1;
data = new char[size];//根据指定大小s分配栈的内存空间
}
Stack::~Stack()
{
delete[]data;//内存回收
}
void Stack::push(char ch)
{
if (isFull())//解决数据上溢问题
{
cout << "栈已满,无法入栈" << endl;
}
else
{
top++;
data[top] = ch;
cout << "已将元素" << ch << "压入栈中" << endl;
cout << "此时top= " << top << endl;
}
}
char Stack::pop()
{
if (!isEmpty())
{
cout << "出栈中..." << endl;
top--;
cout << "此时top= " << top << endl;
return data[top + 1];
}
else//解决数据下溢问题
{
cout << "栈已为空,无法继续出栈" << endl;
}
}
char Stack::getTop()
{
if (!isEmpty())
{
cout << "获得栈顶元素中..." << endl;
cout << "此时top= " << top << endl;
cout << "栈顶元素为:";
return data[top];
}
else
{
cout << "栈已为空,无法获得栈顶元素" << endl;
}
}
bool Stack::isEmpty()
{
if (top == -1)
return 1;//栈为空
else
return 0;//栈不为空
}
bool Stack::isFull()
{
if (top >= size - 1)
{
return 1;//栈已满
}
else
return 0;//栈未满
}
void Stack::setNull(int len)//设置栈空
{
memset(data, 0, len * sizeof(char));
this->top = -1;
cout << "此时top= " << top << endl;
}
void Stack::display()
{
cout << "元素:NULL";
for (int i = 0; i <= top; i++)
{
cout <<"->"<< data[i];
}
cout << endl;
cout << "此时top= " << top << endl;
cout << endl;
}
int main()
{
int n;
cout << "请输入栈的大小:";
cin >> n;
Stack a(n);
int m;
cout << "请输入入栈的元素的个数:";
cin >> m;
if (a.isEmpty())//栈若为空,则进行入栈
{
for (int i = 0; i < m; i++)
{
if (!a.isFull())//判断栈是否已满
{
cout << "请输入入栈的元素:";
char b;
cin >> b;
a.push(b);
}
}
}
cout << endl;
a.display();
a.setNull(n);//设置栈空
a.display();
//cout << a.getTop();//获得栈顶元素但不出栈
//cout << endl;
//char x;//检查上溢问题
//cin >> x;
//a.push(x);
/*for (int i = 0; i < n; i++)
{
if (!a.isEmpty())
{
cout<<a.pop();
cout << "出栈已完成" << endl;
cout << endl;
}
}
cout << endl;
cout << "此时的栈:" << endl;
a.display();*/
return 0;
}
顺序栈的异常捕获
异常处理机制的原理:在真正导致错误的语句发送之前,并且异常发生的条件已经具备了,使用我们自定义的的软件异常或者系统的软件异常来代替它,从而阻止它,因此当异常抛出时,真正的错误实际上还没有发生。
异常处理机制的语法结构:try{}块包含可能会有异常抛出的代码段; catch{}块包含用户自定义的异常处理代码; throw是一条语句,用于抛出异常; 需要注意的是:一条throw语句只能抛出一条异常,一个catch子句也只能捕获一种异常,异常捕获必须和try块结合使用,并 且可以通过异常组合在一个地点捕获多种异常。
注意:1、 异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个处理代码。 2、被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个。 3、对于每一个抛出的异常,总能找到一个对应的throw语句,一些在是我们自己定义的语句,一些是标准库之中的。若没有写 throw语句,但是还是会catch到了异常。 4、由于异常处理机制采用的是类型匹配而不是值判断,因此在catch语句中只需要参数类型,除非确实要使用那个异常对象。
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int MAX_SIZE = 100;//默认栈的大小
class Stack//以存储字符型数据为例
{
private:
char* data;//线性表,指针变量指向栈的数组,保存首地址
int size;//堆栈的实际大小
int top;//栈顶
public:
Stack();
Stack(int s);
~Stack();
void push(char ch);//入栈
char pop();//出栈并返回栈顶元素
char getTop();//获得栈顶元素但不出栈
bool isEmpty();//栈是否为空
bool isFull();//栈是否满
void setNull(int len);//设置栈为空
void display();
class Full {};//定义异常内部类
class Empty {};//定义异常内部类
};
Stack::Stack()
{
size = MAX_SIZE;
top = -1;
data = new char[MAX_SIZE];//缺省构造函数分配默认最大内存空间
}
Stack::Stack(int s)
{
size = s;
top = -1;
data = new char[size];//根据指定大小s分配栈的内存空间
}
Stack::~Stack()
{
delete[]data;//内存回收
}
void Stack::push(char ch)
{
if (isFull())//数据上溢,丢出异常
{
throw Full();
}
else
{
data[++top] = ch;
}
}
char Stack::pop()
{
if (isEmpty())//数据下溢,丢出异常
{
throw Empty();
}
else
{
return data[top--];
}
}
char Stack::getTop()
{
if (!isEmpty())
{
return data[top];
}
else
{
throw Empty();
}
}
bool Stack::isEmpty()
{
if (top == -1)
return 1;//栈为空
else
return 0;//栈已有元素
}
bool Stack::isFull()
{
if (top >= size - 1)
{
return 1;//栈已满
}
else
return 0;//栈未满
}
void Stack::setNull(int len)
{
memset(data, 0, len * sizeof(char));
top = -1;
}
void Stack::display()
{
cout << "元素:";
for (int i = 0; i <= top; i++)
{
printf("\t%c", data[i]);
}
cout << endl;
cout << "此时top= " << top << endl;
cout << endl;
}
int main()
{
Stack s1(2);//栈的大小为2
char ch;
try
{
s1.push('a');
s1.push('b');
s1.push('c');//试图压入第三个元素'c',将会引发异常捕获
}
catch (Stack::Full)//捕获异常
{
cout << "栈已满,无法入栈" << endl;
}
try
{
ch = s1.pop();
cout << ch << endl;
ch = s1.pop();
cout << ch << endl;
ch = s1.pop();//试图弹出第三个元素,将会引发异常捕获
cout << ch << endl;
}
catch (Stack::Empty)//捕获异常
{
cout << "栈已空,无法出栈" << endl;
}
return 0;
}
链栈
链栈的存储结构与单链表的存储结构相同。由于栈是在栈顶进行删除和添加元素的,因此,设置一个头结点为栈顶是最方便的
链栈的结构
以结构体的形式来储存;next指针指向下一个结点
typedef struct DNode
{
int len;//数据域来记录链栈的长度
struct DNode* next;//结点中的next指针(结构体指针),指向下一个结构体
char data;
}Jnode, * link;//Jnode相当于struct Node的别名,*link相当于一个指针类型
链栈的初始化
link initialStack()//初始化链栈,返回头指针
{
link head = new struct DNode;//创建一个头指针(结构体类型)
//link head = new Jnode;效果和上一句一样
head->len = 0;//初始化长度
head->next = NULL;//头指针设置为空,防止野指针
return head;//返回头指针
}
判断栈空
bool isEmpty(link head)//传入头指针判断栈是否为空
{
if (head->len == 0 || head->next == NULL)
{
return 1;
}
else
{
return 0;
}
}
入栈
每次入栈相当于新创建一个结点
然后改变指针的指向,从而插入新的结点
void push(link head, char a)//每次入栈相当于新创建一个结点
{
head->len++;
link node = new Jnode;
node->next = head->next;//让新创建的结点与头结点后面的第一个结点相联系
head->next = node;//让头结点指向新结点,元素入栈
node->data = a;
}
出栈
出栈相当于改变指针指向结点的顺序
将头指针指向被删除的结点的下一个结点,以保证链栈不会断裂,再将第一个结点删除
void pop(link head)//出栈相当于改变指针指向结点的顺序
{
if (!isEmpty(head))
{
head->len--;//链栈的结点个数减少
link node = head->next;//从第一个结点开始
head->next = node->next;//让头指针指向第二个结点,node(第一个结点)是要被删除的,
delete node;//删除第一个结点
}
}
出栈并获得栈顶元素
char getpop(link head)//出栈并获得出栈的元素
{
if (!isEmpty(head))
{
head->len--;
link node = head->next;//从第一个元素开始
head->next = node->next;//让头指针指向第二个结点
char a = node->data;
delete node;
return a;
}
}
打印链栈
void show(link head)
{
if (head->next == NULL)
{
cout << "栈已为空";
}
link node = head->next;//为了不改变头指针,将其赋值给行新的变量
while (node != NULL)
{
cout << node->data << " ";
node = node->next;//指向下一个结点
}
cout << endl;
cout << "栈的长度:" << head->len << endl;
}
完整的链栈
#include<iostream>
using namespace std;
#define MAX_SIZE 100
typedef struct DNode
{
int len;//数据域来记录链栈的长度
struct DNode* next;//结点中的next指针(结构体指针),指向下一个结构体
char data;
}Jnode, * link;//node相当于struct Node的别名,*link相当于一个指针类型
link initialStack()//初始化链栈,返回头指针
{
link head = new struct DNode;//创建一个头指针(结构体类型)
//link head = new Jnode;效果和上一句一样
head->len = 0;//初始化长度
head->next = NULL;//头指针设置为空,防止野指针
return head;//返回头指针
}
bool isEmpty(link head)//传入头指针判断栈是否为空
{
if (head->len == 0 || head->next == NULL)
{
return 1;
}
else
{
return 0;
}
}
void push(link head, char a)//每次入栈相当于新创建一个结点
{
head->len++;
link node = new Jnode;
node->next = head->next;//让新创建的结点与头结点后面的第一个结点相联系
head->next = node;//让头结点指向新结点,元素入栈
node->data = a;
}
void pop(link head)//出栈相当于改变指针指向结点的顺序
{
if (!isEmpty(head))
{
head->len--;//链栈的结点个数减少
link node = head->next;//从第一个结点开始
head->next = node->next;//让头指针指向第二个结点,node(第一个结点)是要被删除的,
delete node;//删除第一个结点
}
}
char getpop(link head)//出栈并获得出栈的元素
{
if (!isEmpty(head))
{
head->len--;
link node = head->next;//从第一个元素开始
head->next = node->next;//让头指针指向第二个结点
char a = node->data;
delete node;
return a;
}
}
void show(link head)
{
if (head->next == NULL)
{
cout << "栈已为空";
}
link node = head->next;//为了不改变头指针,将其赋值给行新的变量
while (node != NULL)
{
cout << node->data << " ";
node = node->next;//指向下一个结点
}
cout << endl;
cout << "栈的长度:" << head->len << endl;
}
int main()
{
link head = initialStack();
cout << "请输入结点个数:";
int t;
cin >> t;
cout << "请输入入栈元素:";
for (int i = 0; i < t; i++)
{
char x;
cin >> x;
push(head, x);
}
cout << "入栈后:" << endl;
show(head);
int n;
cout << "请输入出栈的元素的个数:";
cin >> n;
for (int i = 0; i < n; i++)
{
pop(head);
}
cout << "出栈后" << endl;
show(head);
int m;
cout << "请输入出栈的元素的个数:";
cin >> m;
if (!isEmpty(head))
{
for (int i = 0; i < m; i++)
{
cout << "出栈元素:" << getpop(head) << " ";
}
cout << endl;
}
else
{
cout << "栈已空" << endl;
}
}