数据结构与算法(C++实现)学习笔记(七)
1.链式栈
1.1链式栈简介
链式栈是一种数据存储结构,可以通过单链表的方式来实现,使用链式栈的优点在于它能够克服用数组实现的顺序栈空间利用率不高的特点,但是需要为每个栈元素分配额外的指针空间用来存放指针域。
栈是只能在某一端插入和删除的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底(push),最后的数据在栈顶(top)。链式栈中的元素以Node的形式存储,节点Node中存有此节点存于栈中的元素以及指向下个节点的指针。链式栈的数据成员只用保存指向栈顶节点的指针 *top_node。

1.2 链式栈和顺序栈
顺序栈的实现在于使用了数组这个基本数据结构,数组中的元素在内存中的存储位置是连续的,且编译器要求我们在编译期就要确定数组的大小,这样对内存的使用效率并不高,一来无法避免因数组空间用光而引起的溢出问题,二在系统将内存分配给数组后,则这些内存对于其他任务就不可用;而对于链栈而言,使用了链表来实现栈,链表中的元素存储在不连续的地址,由于是动态申请内存,所以我们可以以非常小的内存空间开始,另外当某个项不使用时也可将内存返还给系统。
1.3 代码实现链式栈
1.链式栈的设计
#ifndef LINKEDTASCK_H
#define LINKEDSTACK_H
template<class T> class LinkedStack;
template<class T>
class ChainNode
{
friend class LinkedStack<T>;
private:
//节点里的数据 新的节点所指向的节点 数据保存在data里,
ChainNode(const T& theData, ChainNode *n = 0)
:data(theData),link(n) {}
T data;
ChainNode<T> *link;
};
template<class T>
class LinkedStack
{
public:
LinkedStack() :top(0) {}
~LinkedStack() { MakeEmpty(); }
bool IsEmpty() const;
T& Top() const;
void Push(const T& e);
void Pop();
void MakeEmpty();
private:
ChainNode<T> *top;
};
template<class T>
bool LinkedStack<T> ::IsEmpty() const
{
return top == 0;
}
template <class T>
void LinkedStack<T>::Push(const T&e)
{
top = new ChainNode<T>(e, top);
}
template <class T>
T& LinkedStack<T>::Top() const
{
if (this->IsEmpty())
throw "Stack is empty.";
return top->data;
}
template<class T>
void LinkedStack<T>::Pop()
{
if (this->IsEmpty())
throw "Stack is empty. Cannot delete.";
ChainNode<T>* delNode = top;
top = top->link;
delete delNode;
}
template <class T>
void LinkedStack<T>::MakeEmpty()
{
while (!IsEmpty())
Pop();
}
#endif
2.测试链式栈
#include<iostream>
#include "linkedstack.h"
//用于测试
using namespace std;
int main()
{
LinkedStack<int> s;
s.Push(10);
cout << s.Top() << endl;
s.Push(20);
cout << s.Top() << endl;
s.Push(30);
cout << s.Top() << endl;
//s.Pop();
//cout << s.Top() << endl;
return 0;
}
2. 链式队列
2.1 链式队列简介
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(back)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
在队列的形成过程中,可以利用线性链表的原理,来生成一个队列。基于链表的队列,要动态创建和删除节点,效率较低,但是可以动态增长。队列采用的FIFO(first in first out),新元素(等待进入队列的元素)总是被插入到链表的尾部,而读取的时候总是从链表的头部开始读取。每次读取一个元素,释放一个元素。所谓的动态创建,动态释放。因而也不存在溢出等问题。由于链表由结构体间接而成,遍历也方便。

1.入队操作
在队尾添加元素,先将队尾元素的next指向添加的元素,然后将队尾指针重新指向新的队尾即可。
2.出队操作
头结结点指向的结点即为队头结点,出队操作,就是把队头结点干掉,先把头结点指向新的队头结点(也就是旧的队头结点的后继结点),然后释放旧的队头结点。如果链表除头结点外只剩一个元素时,则需将rear指向头结点即可。
2.代码实现链式队列
1.实现链式队列
#ifndef QUEUELI_H
#define QUEUELI_H
template<class T>
class Queue
{
public:
Queue();
~Queue();
bool isEmpty() const;
const T& getFront() const;
void enqueue(const T& x);
T dequeue();
void MakeEmpty();
private:
struct ListNode
{
T element;//数据域
ListNode *next;//指针域
ListNode(const T& theElement, ListNode *n=0)
:element(theElement),next(n) {}
};
ListNode *front;
ListNode *back;
};
template<class T>
Queue<T>::Queue()
{
front = back = 0;//空的链表
}
template<class T>
Queue<T>::~Queue()//析构函数,清空数据
{
MakeEmpty();
}
template<class T>
bool Queue<T> ::isEmpty() const
{
return front == 0;
}
template<class T>
const T& Queue<T>::getFront() const
{
if (isEmpty())
throw"Queue is empty.";
return front->element;
}
template <class T>
void Queue<T>::enqueue(const T& x)//入队操作
{
if (isEmpty())
back = front = new ListNode(x);
else
back = back->next = new ListNode(x);
}
template<class T>
T Queue<T> ::dequeue()//出队操作
{
T frontItem = getFront();
ListNode *old = front;
front = front->next; //front指向下一个,即删除
delete old; //对应着入队的new
return frontItem;
}
template <class T>
void Queue<T>::MakeEmpty()
{
while (!isEmpty())
dequeue();
}
#endif
2.测试链式队列
#include<iostream>
#include"QueueLi.h"
using namespace std;
int main()
{
Queue<int> myQ;
myQ.enqueue(10);
myQ.enqueue(20);
myQ.enqueue(20);
cout << myQ.getFront() << endl;
myQ.dequeue();
cout << myQ.dequeue() << endl;
cout << myQ.dequeue() << endl;
for (int j = 0; j < 8; j++)
{
for (int i = 0; i < 8; i++)
myQ.enqueue(i);
while (!myQ.isEmpty())
cout << myQ.dequeue() << endl;
}
}
参考视频:
https://www.bilibili.com/video/av31763085/?p=19
https://www.bilibili.com/video/av31763085/?p=20
1766





