栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)加粗样式的原则。
入栈:从栈顶放入数据的操作。
出栈:从栈顶取出元素的操作。
栈用链表和顺序表两种数据结构都可以实现,我们要确定选择哪一种更优,我们来分析一下。
链表栈:如果选择单链表的话,我们应该选择头当栈顶,尾当栈底,不然的话,每次存取数据都要遍历一遍链表。所以选双链表会比较好一点。
数组栈:访问栈顶的时间复杂度为O(1),相比链表栈比较优。
所以下面我们用顺序表来实现栈的这种数据结构。
结构如下:初始化栈的大小为5
- #define InitSize 5
- template <class DateType>
- class Stack
- {
- public:
- private:
- DateType* data;//栈空间指针
- int size;//栈容量
- int top;//栈顶,栈中元素个数
- };
栈要实现的接口有以下几个:
- Stack();//初始化栈,初始化的大小是5
- Stack(const Stack& stack);//拷贝构造函数
- Stack& operator=(const Stack& stack);//等号运算符的重载
- ~Stack();//销毁栈
- bool ifFull();//判断栈是否满了
- bool isEmpty();//判断栈是否为空
- void Push(const DateType& val);//入栈
- bool Pop(DateType &item);//出栈,并获取出栈元素
- void ExpandStack();//扩容
- void PrintStack();//打印
初始化栈就是把结构体中的成员都初始化一下,方便后续的扩容等操作。
具体实现如下:
- //初始化栈,初始化的大小是5
- Stack()
- {
- data = new DateType[InitSize];
- if (data == NULL)
- {
- cout << "内存分配失败" << endl;
- exit(-1);
- }
- size = InitSize;
- top = 0;
- }
- //拷贝构造函数
- Stack(const Stack& stack)
- {
- this->data = new DateType[stack.size];
- if (data == NULL)
- {
- cout << "内存分配失败" << endl;
- exit(-1);
- }
- for (int i = 0; i < stack.size; i++)
- {
- this->data[i] = stack.data[i];
- }
- this->size = stack.size;
- this->top = stack.top;
- }
- //判断栈是否满了
- bool ifFull()
- {
- if (top == size)
- {
- return true;
- }
- return false;
- }
- //扩容
- void ExpandStack()
- {
- this->size = this->size == 0 ? 4 : 2 * this->size;
- DateType* tmp = new DateType[this->size];
- if (tmp == NULL)
- {
- cout << "内存分配失败" << endl;
- exit(-1);
- }
- for (int i = 0; i < top; i++)
- {
- tmp[i] = data[i];
- }
- delete[] data;
- data = tmp;
- }
- //判断栈是否为空
- bool isEmpty()
- {
- if (top == 0)
- {
- return true;
- }
- return false;
- }
压栈就是在栈顶插入元素,其中是肯定要考虑到扩容的问题,当this->top == this->size时,就要考虑到扩容了,扩容也是像之前顺序表那样每次扩一倍,这样可以一定程度地减少扩容次数,但同时是会带来一定的空间消耗的。
- //入栈
- void Push(const DateType& val)
- {
- if (ifFull())
- {
- ExpandStack();
- }
- data[top++] = val;
- }
出栈就是在栈顶pop掉一个元素,也就是top-1指向的位置,只需要将top进行一个减1的操作即可。
与此同时,我们要确保此次栈不为空,所以要进行判断栈空的操作,防止程序崩溃。
- //出栈,并获取出栈元素
- bool Pop(DateType &item)
- {
- if (isEmpty())
- {
- cout << "栈为空,无法出栈" << endl;
- return false;
- }
- item = data[--top];
- return true;
- }
运算符重载的注意事项在前面的博客我已经介绍过了,尤其是赋值运算符,感兴趣的小伙伴可以去看看,这里要注意几点
- //等号运算符的重载
- Stack& operator=(const Stack& stack)
- {
- //防止自赋值
- if (this == &stack)
- {
- return *this;
- }
- //防止内存泄漏
- if (data != NULL)
- {
- delete[] data;
- }
- //防止浅拷贝
- this->data = new DateType[stack.size];
- if (data == NULL)
- {
- cout << "内存分配失败" << endl;
- exit(-1);
- }
- for (int i = 0; i < stack.top; i++)
- {
- this->data[i] = stack.data[i];
- }
- this->size = stack.size;
- this->top = stack.top;
- return *this;
- }
- //打印
- void PrintStack()
- {
- for (int i = 0; i < top; i++)
- {
- cout << data[i] << " ";
- }
- cout << endl;
- }
- #define _CRT_SECURE_NO_WARNINGS
- #include<iostream> //引入头文件
- #include<string>//C++中的字符串
- using namespace std; //标准命名空间
- #define InitSize 5
- /*
- 栈,利用顺序表实现
- */
- template <class DateType>
- class Stack
- {
- public:
- //初始化栈,初始化的大小是5
- Stack()
- {
- data = new DateType[InitSize];
- if (data == NULL)
- {
- cout << "内存分配失败" << endl;
- exit(-1);
- }
- size = InitSize;
- top = 0;
- }
- //拷贝构造函数
- Stack(const Stack& stack)
- {
- this->data = new DateType[stack.size];
- if (data == NULL)
- {
- cout << "内存分配失败" << endl;
- exit(-1);
- }
- for (int i = 0; i < stack.size; i++)
- {
- this->data[i] = stack.data[i];
-
- }
- this->size = stack.size;
- this->top = stack.top;
- }
- //等号运算符的重载
- Stack& operator=(const Stack& stack)
- {
- //防止自赋值
- if (this == &stack)
- {
- return *this;
- }
- //防止内存泄漏
- if (data != NULL)
- {
- delete[] data;
- }
- //防止浅拷贝
- this->data = new DateType[stack.size];
- if (data == NULL)
- {
- cout << "内存分配失败" << endl;
- exit(-1);
- }
- for (int i = 0; i < stack.top; i++)
- {
- this->data[i] = stack.data[i];
-
- }
- this->size = stack.size;
- this->top = stack.top;
- return *this;
- }
- //销毁栈
- ~Stack()
- {
- if (data != NULL)
- {
- delete[] data;
- data = NULL;
- }
- }
- //判断栈是否满了
- bool ifFull()
- {
- if (top == size)
- {
- return true;
- }
- return false;
- }
- //判断栈是否为空
- bool isEmpty()
- {
- if (top == 0)
- {
- return true;
- }
- return false;
- }
- //入栈
- void Push(const DateType& val)
- {
- if (ifFull())
- {
- ExpandStack();
- }
- data[top++] = val;
- }
- //出栈,并获取出栈元素
- bool Pop(DateType &item)
- {
- if (isEmpty())
- {
- cout << "栈为空,无法出栈" << endl;
- return false;
- }
- item = data[--top];
- return true;
- }
- //扩容
- void ExpandStack()
- {
- this->size = this->size == 0 ? 4 : 2 * this->size;
- DateType* tmp = new DateType[this->size];
- if (tmp == NULL)
- {
- cout << "内存分配失败" << endl;
- exit(-1);
- }
- for (int i = 0; i < top; i++)
- {
- tmp[i] = data[i];
- }
- delete[] data;
- data = tmp;
- }
- //打印
- void PrintStack()
- {
- for (int i = 0; i < top; i++)
- {
- cout << data[i] << " ";
- }
- cout << endl;
- }
- private:
- DateType* data;//栈空间指针
- int size;//栈容量
- int top;//栈顶,栈中元素个数
- };
- int main()
- {
- Stack<int> stack;
- stack.Push(1);
- stack.Push(2);
- stack.Push(3);
- stack.Push(4);
- stack.Push(5);
- stack.PrintStack();
- cout << "-------------------------" << endl;
- int b = 0;
- stack.Pop(b);
- cout << b << endl;
- stack.Pop(b);
- cout << b << endl;
- stack.PrintStack();
- cout << "-------------------------" << endl;
- Stack<int> stack2(stack);
- stack2.PrintStack();
- cout << "-------------------------" << endl;
- Stack<int> stack3;
- stack3 = stack2;
- stack3.PrintStack();
- cout << "-------------------------" << endl;
- system("pause");
- return EXIT_SUCCESS;
- }
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头。
队列的结构,我们选取单链表来实现,秩序进行头删和为插的不足即可。如果选数组,那么每一次删头我们都要挪动一遍数据,这种方式不优,所以我们还是选取用单链表来实现。
定义的结构如下:
- template<class DateType>
- //链队的结点类型
- struct Node
- {
- DateType data;
- Node<DateType> *next;
- Node(Node<DateType>* p = NULL)
- {
- next = p;
- }
- //构造函数
- Node(DateType val, Node<DateType>* p = NULL)
- {
- data = val;
- next = p;
- }
- };
- template <class DateType>
- class Queue
- {
- public:
- private:
- //声明,也是定义,只不过定义的是指针类型,保存的应该是地址,未初始化
- //队头指针
- Node<DateType>* front;
- //队尾指针
- Node<DateType>* rear;
- };
- Queue();//构造函数,初始化队列
- ~Queue();//析构函数
- bool QueuePush(const DateType& val);//队尾入队
- bool QueuePop(DateType& val);//对头出队列
- bool getFront(DateType& val) const;//获取对头元素的值
- bool getRear(DateType& val);//获取队尾元素的值
- void MakeEmpty();//将队列清空
- bool isEmpty() const;//判断队列是否为NULL
- int getSize() const;//返回队列元素的个数
- void PrintQueue();//输出队列元素
初始化很简单,只要将头指针和尾指针都置空。
- //构造函数
- Queue()
- {
- front = NULL;
- rear = NULL;
- }
- //判断队列是否为NULL
- bool isEmpty() const
- {
- if (front == NULL)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
出队就是进行单链表尾删的操作,要考虑链表为空时不能进行删除,还要注意的是只有一个节点进行删除是要改变尾指针的指向。
- //队尾入队列
- bool QueuePush(const DateType& val)
- {
- if (front == NULL)
- {
- //如果队列为空,直接用指针开辟结点
- front = rear = new Node<DateType>(val);
- if (front == NULL)
- {
- cout << "内存分配失败" << endl;
- return false;
- }
- }
- else
- {
- Node<DateType>* p = new Node<DateType>(val);
- rear->next = p;
- if (rear->next == NULL)
- {
- cout << "内存分配失败" << endl;
- return false;
- }
- //更新尾结点
- rear = rear->next;
- }
- return true;
- }
出队就是进行单链表尾删的操作,要考虑链表为空时不能进行删除,还要注意的是只有一个节点进行删除是要改变尾指针的指向。
- bool QueuePop(DateType& val)
- {
- //如果队列为空,不允许出列
- if (isEmpty())
- {
- return false;
- }
- else
- {
- Node<DateType>* p = front;
- val = front->data;
- front = front->next;
- delete p;
- return true;
- }
- }
首先要确保链表不为空,对头就是返回头节点的大小,队尾就是返回尾节点的大小。
具体实现如下:
- //获取对头元素的值
- bool getFront(DateType& val) const
- {
- if (isEmpty())
- {
- return false;
- }
- val = front->data;
- return true;
- }
- //获取队尾元素的值
- bool getRear(DateType& val) {
- if (isEmpty())
- {
- return false;
- }
- val = rear->data;
- return true;
- }
遍历一遍链表,同时进行计数操作,最后返回计数结果即可。
- //返回队列元素的个数
- int getSize() const
- {
- //函数返回队列元素的个数
- Node<DateType>* p = front;
- int count = 0;
- while (p != NULL)
- {
- ++count;
- p = p->next;
- }
- return count;
- }
为了防止内存泄漏,动态内存申请的空间一定要我们自己手动释放,养成一个良好的习惯。所以要将链表的空间逐个释放。
- //将队列清空
- void MakeEmpty()
- {
- //置空队列,释放链表中所有的结点
- Node<DateType>* current;
- if (front != NULL)
- {
- current = front;
- front = front->next;
- delete current;
- }
- }
- //输出队列元素
- void PrintQueue()
- {
- Node<DateType>* p = front;
- while (p != NULL)
- {
- cout << p->data << " ";
- p = p->next;
- }
- cout << endl;
- }
- #define _CRT_SECURE_NO_WARNINGS
- #include<iostream> //引入头文件
- #include<string>//C++中的字符串
- using namespace std; //标准命名空间
- /*
- 队列,单链表实现
- */
- template<class DateType>
- //链队的结点类型
- struct Node
- {
- DateType data;
- Node<DateType> *next;
- Node(Node<DateType>* p = NULL)
- {
- next = p;
- }
- //构造函数
- Node(DateType val, Node<DateType>* p = NULL)
- {
- data = val;
- next = p;
- }
- };
- template <class DateType>
- class Queue
- {
- public:
- //构造函数
- Queue()
- {
- front = NULL;
- rear = NULL;
- }
- //析构函数
- ~Queue()
- {
- MakeEmpty();
- }
- //队尾入队列
- bool QueuePush(const DateType& val)
- {
- if (front == NULL)
- {
- //如果队列为空,直接用指针开辟结点
- front = rear = new Node<DateType>(val);
- if (front == NULL)
- {
- cout << "内存分配失败" << endl;
- return false;
- }
- }
- else
- {
- Node<DateType>* p = new Node<DateType>(val);
- rear->next = p;
- if (rear->next == NULL)
- {
- cout << "内存分配失败" << endl;
- return false;
- }
- //更新尾结点
- rear = rear->next;
- }
- return true;
- }
- //对头出队列
- bool QueuePop(DateType& val)
- {
- //如果队列为空,不允许出列
- if (isEmpty())
- {
- return false;
- }
- else
- {
- Node<DateType>* p = front;
- val = front->data;
- front = front->next;
- delete p;
- return true;
-
- }
- }
- //获取对头元素的值
- bool getFront(DateType& val) const
- {
- if (isEmpty())
- {
- return false;
- }
- val = front->data;
- return true;
- }
- //获取队尾元素的值
- bool getRear(DateType& val) {
- if (isEmpty())
- {
- return false;
- }
- val = rear->data;
- return true;
- }
- //将队列清空
- void MakeEmpty()
- {
- //置空队列,释放链表中所有的结点
- Node<DateType>* current;
- if (front != NULL)
- {
- current = front;
- front = front->next;
- delete current;
- }
- }
- //判断队列是否为NULL
- bool isEmpty() const
- {
- if (front == NULL)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- //返回队列元素的个数
- int getSize() const
- {
- //函数返回队列元素的个数
- Node<DateType>* p = front;
- int count = 0;
- while (p != NULL)
- {
- ++count;
- p = p->next;
- }
- return count;
- }
- //输出队列元素
- void PrintQueue()
- {
- Node<DateType>* p = front;
- while (p != NULL)
- {
- cout << p->data << " ";
- p = p->next;
- }
- cout << endl;
- }
- private:
- //声明,也是定义,只不过定义的是指针类型,保存的应该是地址,未初始化
- //队头指针
- Node<DateType>* front;
- //队尾指针
- Node<DateType>* rear;
- };
- int main()
- {
- Queue<int> que;
- que.QueuePush(1);
- que.QueuePush(2);
- que.QueuePush(3);
- que.QueuePush(4);
- que.PrintQueue();
- cout << "----------------------" << endl;
- int b = 0;
- que.QueuePop(b);
- cout << b << endl;
- que.QueuePop(b);
- cout << b << endl;
- que.PrintQueue();
- cout << "----------------------" << endl;
- int c = 0;
- que.getFront(c);
- cout << c << endl;
- que.PrintQueue();
- cout << que.getSize() << endl;
- cout << "----------------------" << endl;
- system("pause");
- return EXIT_SUCCESS;
- }