一、栈
1、栈(stack)是一种线性存储结构
-
-
- 栈中的数据元素遵守先进后出的原则,FILO结构
- 限定只能在栈顶进行插入与删除操作
- 栈的操作包括压栈出栈
-
2、栈的常用操作
-
-
- 压栈(push)
- 弹栈或出站(pop)
- 求栈的大小
- 判断栈是否为空
- 获取栈顶元素的值
-
3、栈的常见分类
-
-
- 基于数组的栈,以数组为底层数据结构,通常以数组头为栈底,数组头到数组尾为栈顶的生长方向
- 基于单链表的栈,以链表为底层的数据结构时,以链表头为栈顶,便于节点的插入与删除,压栈产生的结点将一直出现在链表的头部
-
4、使用标准库的栈时,需要包含对应的头文件,
-
1 #include<stack> 2 stack<int> s; 3 s.empty(); 4 s.size(); 5 s.top(); 6 s.push(); 7 s.pop();
- 基于数组的栈
-
1 #include <stack> 2 #include <iostream> 3 using namespace std; 4 5 int main() 6 { 7 stack<int> mystack; 8 int sum = 0; 9 for (int i = 0; i <= 10; i++){ 10 mystack.push(i); 11 } 12 cout << "size is " << mystack.size() << endl; 13 while (!mystack.empty()){ 14 cout << " " << mystack.top(); 15 mystack.pop(); 16 } 17 cout << endl; 18 system("pause"); 19 return 0; 20 } 21 //size is 11 22 // 10 9 8 7 6 5 4 3 2 1 0
-
- 基于单链表的栈
-
1 #include <iostream> 2 using namespace std; 3 template<class T>class Stack 4 { 5 private: 6 struct Node 7 { 8 T data; 9 Node *next; 10 }; 11 Node *head; 12 Node *p; 13 int length; 14 15 public: 16 Stack() 17 { 18 head = NULL; 19 length = 0; 20 } 21 void push(T n)//入栈 22 { 23 Node *q = new Node; 24 q->data = n; 25 if (head == NULL) 26 { 27 q->next = head; 28 head = q; 29 p = q; 30 } 31 else 32 { 33 q->next = p; 34 p = q; 35 } 36 length++; 37 } 38 39 T pop()//出栈并且将出栈的元素返回 40 { 41 if (length <= 0) 42 { 43 abort(); 44 } 45 Node *q; 46 int data; 47 q = p; 48 data = p->data; 49 p = p->next; 50 delete(q); 51 length--; 52 return data; 53 } 54 int size()//返回元素个数 55 { 56 return length; 57 } 58 T top()//返回栈顶元素 59 { 60 return p->data; 61 } 62 bool isEmpty()//判断栈是不是空的 63 { 64 if (length == 0) 65 { 66 return true; 67 } 68 else 69 { 70 return false; 71 } 72 } 73 void clear()//清空栈中的所有元素 74 { 75 while (length > 0) 76 { 77 pop(); 78 } 79 } 80 }; 81 int main() 82 { 83 Stack<char> s; 84 s.push('a'); 85 s.push('b'); 86 s.push('c'); 87 while (!s.isEmpty()) 88 { 89 cout << s.pop() << endl; 90 } 91 system("pause"); 92 return 0; 93 }
-
二、队列
1、队列与栈一样是一种线性存储结构
-
-
- 队列中的数据元素遵循先进先出的原则即FIFO原则
- 在队尾添加元素,在队头删除元素
- 队的操作包括入队和出队
-
2、队列的操作
-
-
- 入队(push)
- 出队(pop)
- 求队列中元素的个数
- 判断队列是否为空
- 获取队首元素
-
3、队列的分类
-
-
- 基于数组的循环队列(循环队列)
- 基于链表的队列(链队列)
-
4、C++队列queue模板类定义在<queue>头文件中,queue模板类需要两个模板参数,一个是元素类型,一个是容器类型。元素类型是必要的,容器类型是可选的,默认为deque类型。C++队列Queue是一种容器适配器,它给予程序员一种先进先出(FIFO)的数据结构。
- 如何判断队列是空队列还是已满呢
-
- 栈空:队首标志=队尾标志,表示栈空
- 栈满:队尾+1=队首时,表示栈满
-
- 使用标准库的队列时,应包含相关头文件,
-
#include<queue> queue<int> q; q.empty(); q.size(); q.push(); q.pop(); q.front(); q.back();
基于数组的循环队列- 以数组作为底层数据结构时,一般将队列实现为循环队列。这是因为队列在顺序存储上的不足:每次从数组头部删除元素(出队)后,需要将头部以后的所有元素往前移动一个位置,这是一个时间复杂度为O(n)的操作。
- 循环队列可以将数组看成一个首位相连的圆环,删除元素时将队首标志往后移动,添加元素时若数组尾部已经没有空间,则考虑数组头部的空间是否空闲,如果是则在数组头部进行插入。
-
1 #include <queue> 2 #include <iostream> 3 using namespace std; 4 5 int main(){ 6 queue<int> q; 7 for (int i = 0; i < 10; i++){ 8 q.push(i); 9 } 10 if (!q.empty()){ 11 cout << "队列q非空!" << endl; 12 cout << "q中有" << q.size() << "个元素" << endl; 13 } 14 cout << "队头元素为:" << q.front() << endl; 15 cout << "队尾元素为:" << q.back() << endl; 16 for (int j = 0; j < 10; j++){ 17 int tmp = q.front(); 18 cout << tmp << " "; 19 q.pop(); 20 } 21 cout << endl; 22 if (!q.empty()){ 23 cout << "队列非空!" << endl; 24 } 25 system("pause"); 26 return 0; 27 }
- 循环队列基于数组的C++实现代码如下:
-
1 #include <iostream> 2 #include <queue> 3 #include <string> 4 using namespace std; 5 6 template <typename T> 7 class LoopQueue 8 { 9 public: 10 LoopQueue(int c = 10); 11 ~LoopQueue(); 12 bool isEmpty(); //队列的判空 13 int size(); //队列的大小 14 bool push(T t); //入队列 15 bool pop(); //出队列 16 T front(); //队首元素 17 18 private: 19 int capacity; 20 int begin; 21 int end; 22 T* queue; 23 }; 24 25 26 template<typename T> 27 LoopQueue<T>::LoopQueue(int c = 10) 28 :capacity(c), begin(0), end(0), queue(nullptr) 29 { 30 queue = new T[capacity]; 31 }; 32 33 template<typename T> 34 LoopQueue<T>::~LoopQueue() 35 { 36 delete[]queue; 37 } 38 39 template <typename T> 40 bool LoopQueue<T>::isEmpty() //判断循环队列是否为空 41 { 42 if (begin == end) 43 return true; 44 return false; 45 }; 46 47 template<typename T> 48 int LoopQueue<T>::size() 49 { 50 return (end - begin + capacity) % capacity; //计算循环队列的长度 51 }; 52 53 template<typename T> 54 bool LoopQueue<T>::push(T t) 55 { 56 if (end + 1 % capacity == begin) //判断队列是否已满 57 { 58 return false; 59 } 60 queue[end] = t; 61 end = (end + 1) % capacity; 62 return true; 63 }; 64 65 template <typename T> 66 bool LoopQueue<T>::pop() //判断队列是否为空 67 { 68 if (end == begin) 69 { 70 return false; 71 } 72 begin = (begin + 1) % capacity; 73 return true; 74 }; 75 76 template <typename T> 77 T LoopQueue<T>::front() 78 { 79 if (end == begin) 80 { 81 return false; 82 } 83 return queue[begin]; 84 }; 85 86 int main() 87 { 88 LoopQueue<string> queue(6); 89 queue.push("one"); 90 queue.push("two"); 91 queue.push("three"); 92 queue.push("four"); 93 queue.push("five"); 94 cout << "队列长度" << queue.size() << endl; 95 while (!queue.isEmpty()) 96 { 97 cout << queue.front() << endl; 98 queue.pop(); 99 } 100 getchar(); 101 //system("pause"); 102 return 0; 103 }
基于链表的队列(链队列)- 链队列是基于链表实现的队列,它不存在数组的O(n)的元素移动问题和内存空间浪费问题。我们所要确定的就是链表哪头做队首,哪头做队尾。显然我们应该将链表头做队首,链表尾做队尾。存储一个指向队尾的指针,方便从链表尾插入元素,使用带头结点的链表,方便从链表头删除元素。
- 链式队列的C++实现
-
1 #include <iostream> 2 #include <cstdlib> 3 using namespace std; 4 5 struct QNode //定义队列结点的数据结构 6 { 7 QNode *next; //指针域,指向下一个结点 8 double data; //数据域,存储队列信息 9 }; 10 11 struct LinkQueue //定义队列的数据结构 12 { 13 QNode *front; //队首指针,指向QNode类型的指针 14 QNode *rear; //队尾指针 15 }; 16 17 void InitQueue(LinkQueue &Q) //构造一个空的队列 18 { 19 QNode *q; 20 q = new QNode; //申请一个结点的空间 21 q->next = NULL; //当作头结点 22 //队首与队尾指针都指向这个结点,指针域为NULL 23 Q.front = q; 24 Q.rear = q; 25 } 26 27 int IsEmpty(LinkQueue &Q) //判断队列是否为空 28 { 29 if (Q.rear == Q.front) 30 return 0; 31 else 32 return 1; 33 } 34 35 void EnQueue(LinkQueue &Q, double e) //从队列尾部插入元素 36 { 37 QNode *p; //新创建一个结点 38 p = new QNode; 39 p->next = NULL; 40 p->data = e; //输入数据信息 41 //将新结点插入队列尾部 42 Q.rear->next = p; 43 Q.rear = p; //设置新的尾结点 44 } 45 46 void DeQueue(LinkQueue &Q, double &e) //从队列首部删除一个结点 47 { 48 QNode *p; 49 p = Q.front->next; 50 e = p->data; //保存要出队列的数据 51 Q.front->next = p->next; //将下一个结点当作头结点后面链接的第一个结点 52 if (Q.rear == p) //如果要删除的元素即为尾结点,则将头指针赋予尾指针,一同指向头结点,表示队列为空 53 Q.rear = Q.front; 54 delete p; 55 } 56 57 void DestoryQueue(LinkQueue &Q) //销毁一个队列 58 { 59 while (Q.front) 60 { 61 Q.rear = Q.front; //从头节点开始,一个一个删除队列结点,释放空间 62 delete Q.front; 63 Q.front = Q.rear; 64 } 65 } 66 int main() 67 { 68 LinkQueue *Q; //定义一个队列Q 69 Q = new LinkQueue; 70 InitQueue(*Q); 71 cout << "开始往队列里输入数据,以-1作为结束符" << endl; 72 cout << "请输入一个数:" << endl; 73 double a, x; 74 cin >> a; 75 while (a != -1) 76 { 77 EnQueue(*Q, a); 78 cout << "请输入一个数:" << endl; 79 cin >> a; 80 } 81 //输出队列元素,队首->队尾 82 QNode *p; 83 p = Q->front->next; 84 if (p == NULL) //如果为空表,直接退出 85 { 86 cout << "队列为空!" << endl; 87 return 0; 88 } 89 cout << "队列数据依次为:" << endl; 90 while (p != NULL) 91 { 92 cout << p->data << " "; 93 p = p->next; 94 } 95 cout << endl; 96 //删除队列元素 97 while (!IsEmpty(*Q)) 98 { 99 DeQueue(*Q, x); 100 cout << x << " "; 101 } 102 //释放内存空间 103 delete Q->front; 104 delete Q; 105 system("pause"); 106 return 0; 107 }
- 用单链表表示的链式队列特别适合于数据元素变化较大的情况,而且不存在溢出的情况。
- 循环队列中判断队空的方法是front==rear,队满的方法是判断front=(real+1)%MAXSIZE。(为什么不用一个length表示队长,当length=MAXSIZE时表示队满,原因就是,在频繁的队列操作中多出一个变量会大量的增加执行时间,所以不如浪费一个数组空间划算。)
-
-