爆肝万字!C++ Stack/Queue/Priority_queue 从入门到源码手撕,仿函数 + 容器适配器 + 二叉堆,面试吊打 99% 人!
栈和队列没有迭代器,他们只是容器适配器,不是容器。容器才有迭代器
文章目录
栈Stack
➡️有关 栈的实现/二叉树的实现 的知识,可以点击这里进行学习⬅️
头文件:stack
本质
template <class T, class Container = deque<T> > class stack;
- 后进先出
- 栈是一种“容器适配器” ——>栈可能是由list,vector或deque容器(称为底层容器)变化而来
- 底层容器,如果不传,默认是deque
成员函数
-
-
Construct stack (public member function )
-
-
-
Test whether container is empty (public member function )
-
-
-
Return size (public member function )
-
-
-
Access next element (public member function )
-
-
-
Insert element (public member function )
-
-
-
Construct and insert element (public member function )
-
-
-
Remove top element (public member function )
-
-
-
Swap contents (public member function )
-
实现
stack.h
#pragma once
namespace lcj
{
template<class T, class Container = vector<T>>
class stack
{
public:
/// <summary>
/// 不用写构造函数,因为container是自定义类型,会调用对应的构造函数
/// </summary>
///
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_back();
}
const T& top() const
{
return _con.back();///不能通过‘[ ]’访问,当container是list时,没有这个功能
}
size_t size() const
{
return _con.size();
}
bool empty()const
{
return _con.size() == 0;
}
private:
Container _con;
};
}
按需实例化:
在.h中写的类中的成员函数,并不会全都被实例化,编译器只实例化使用的接口——>编译器不检查细节语法,有时候不会报错,但是使用的时候还是会发生错误——>写函数的时候还是要写一个检查一个
队列queue
本质
template <class T, class Container = deque<T> > class queue;
- 先进先出
- 也是一种“容器适配器” ——>栈可能是由list,vector或deque容器(称为底层容器)变化而来
- 底层容器,如果不传,默认是deque
成员函数
-
-
Construct queue (public member function )
-
-
-
Test whether container is empty (public member function )
-
-
-
Return size (public member function )
-
-
-
Access next element (public member function )
-
-
-
Access last element (public member function )
-
-
-
Insert element (public member function )
-
-
-
Construct and insert element (public member function )
-
-
-
Remove next element (public member function )
-
-
-
Swap contents (public member function )
-
实现
queue.h
#pragma once
namespace lcj
{
template<class T, class Container = list<T>>
class queue
{
public:
/// <summary>
/// 不用写构造函数,因为container是自定义类型,会调用对应的构造函数
/// </summary>
///
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_front();
}
const T& back() const
{
return _con.back();///不能通过‘[ ]’访问,当container是list时,没有这个功能
}
const T& front() const
{
return _con.front();///不能通过‘[ ]’访问,当container是list时,没有这个功能
}
size_t size() const
{
return _con.size();
}
bool empty()const
{
return _con.size() == 0;
}
private:
Container _con;
};
}
deque双端队列
stack和queue的缝合
成员函数
| 函数分类 | 函数名 | 功能说明 |
|---|---|---|
| 构造 / 析构 / 赋值 | (constructor) | 构造 deque 容器(公有成员函数) |
| (destructor) | 销毁 deque 容器(公有成员函数) | |
| operator= | 给 deque 容器赋值(公有成员函数) | |
| 迭代器(Iterators) | begin | 返回指向容器起始位置的迭代器(公有成员函数) |
| end | 返回指向容器末尾位置的迭代器(公有成员函数) | |
| rbegin | 返回指向容器反向起始位置的反向迭代器(公有成员函数) | |
| rend | 返回指向容器反向末尾位置的反向迭代器(公有成员函数) | |
| cbegin | 返回指向容器起始位置的 const 迭代器(公有成员函数) | |
| cend | 返回指向容器末尾位置的 const 迭代器(公有成员函数) | |
| crbegin | 返回指向容器反向起始位置的 const 反向迭代器(公有成员函数) | |
| crend | 返回指向容器反向末尾位置的 const 反向迭代器(公有成员函数) | |
| 容量管理(Capacity) | size | 返回容器当前存储的元素数量(公有成员函数) |
| max_size | 返回容器可容纳的最大元素数量(公有成员函数) | |
| resize | 改变容器的大小(公有成员函数) | |
| empty | 检测容器是否为空(公有成员函数) | |
| shrink_to_fit | 释放容器未使用的内存,收缩到适配当前元素大小(公有成员函数) | |
| 元素访问(Element access) | operator[] | 访问容器中指定下标位置的元素(公有成员函数) |
| at | 访问容器中指定下标位置的元素,带越界检查(公有成员函数) | |
| front | 访问容器的第一个元素(公有成员函数) | |
| back | 访问容器的最后一个元素(公有成员函数) | |
| 内容修改(Modifiers) | assign | 给容器重新赋值,覆盖原有内容(公有成员函数) |
| push_back | 在容器末尾添加一个元素(公有成员函数) | |
| push_front | 在容器开头插入一个元素(公有成员函数) | |
| pop_back | 删除容器末尾的最后一个元素(公有成员函数) | |
| pop_front | 删除容器开头的第一个元素(公有成员函数) | |
| insert | 在容器指定位置插入元素(公有成员函数) | |
| erase | 删除容器指定位置的元素(公有成员函数) | |
| swap | 交换两个 deque 容器的内容(公有成员函数) | |
| clear | 清空容器内的所有元素(公有成员函数) | |
| emplace_front | 在容器开头直接构造并插入一个元素(公有成员函数) | |
| emplace_back | 在容器末尾直接构造并插入一个元素(公有成员函数) | |
| 分配器(Allocator) | get_allocator | 获取容器使用的内存分配器(公有成员函数) |
| 非成员函数重载 | relational operators | deque 容器的关系比较运算符(函数模板) |
| swap | 交换两个 deque 容器的内容(函数模板) |
priority_queue优先级队列
本质
template <class T, class Container = vector<T>,
class Compare = less<typename Container::value_type> > class priority_queue;
- 就是个堆,默认是大根堆
- 也是容器适配器,默认是vector(因为下标访问用的多)
头文件:queue‼️
不是单独的‼️
成员函数
-
-
Construct priority queue (public member function )
-
-
-
Test whether container is empty (public member function )
-
-
-
Return size (public member function )
-
-
-
Access top element (public member function )
-
-
-
Insert element (public member function )
-
-
-
Construct and insert element (public member function )
-
-
-
Remove top element (public member function )
-
-
-
Swap contents (public member function )
-
应用
大根堆
priority_queue<int, vector<int>, less<int>> pq;中的:less<int>也可以不写,是默认的,这时候是大根堆——>打印出来是从大到小的
#include<queue>
using namespace std;///必须在#include"stack.h"的前面,这样,如果stack.h里面有std::cout这种函数也没问题
int main()
{
priority_queue<int, vector<int>, less<int>> pq;
pq.push(3);
pq.push(777);
pq.push(66);
pq.push(88);
pq.push(22);
pq.push(9);
while (!pq.empty())
{
cout << pq.top() << endl;
pq.pop();
}
return 0;
}
小根堆
——>从小到大打印
只需要改 less<int>为greater<int>即可
实现
位置:
- 在
namespace lcj中
基础:
- 通过类模板实现:
template <class T, class Container = std::vector<T>, class Compare = Less<T> >class T:priority_queue中存的类型class Container = std::vector<T>:priority_queue 也是一种“容器适配器” ——>可能是由list,vector或deque容器(称为底层容器)变化而来class Compare = Less<T>:仿函数, ( 重载了 () 运算符的类,长得像函数,实际是对象。)。需要你在前面专门写两个仿函数(只有operator() 的类模板):Greater和Less
push:堆插入,需要提前写向上调整算法void Adjustup(size_t child)pop:删除堆顶元素,需要提前写向下调整算法:void Adjustdown(int parent)
完整代码
template <class T>///类模板——>得到的类中只有operator()——>仿函数
class Less///——>用来判断小于的仿函数
{
public:
bool operator() (const T& x, const T& y)
{
return x < y;
}
};
template <class T>
class Greater///——>用来判断大于的仿函数
{
public:
bool operator() (const T& x, const T& y)
{
return x > y;
}
};
template <class T, class Container = std::vector<T>, class Compare = Less<T> >///传参传的是仿函数(一种类)
/// <summary>
/// 在创建priorityqueue的时候,直接传仿函数就能判断是大根堆还是小根堆
class priority_queue
{
public:
priority_queue()
{
}
void Adjustup(size_t child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (comp(c[parent], c[child]))
{
std::swap(c[child], c[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
break;
}
}
void push(const T& x)
{
c.push_back(x);
Adjustup(c.size() - 1);
}
void Adjustdown(int parent)
{
int child = parent * 2 + 1;
while (child < c.size())
{
if (child + 1 < c.size() && comp(c[child], c[child + 1]))
++child;
if (comp(c[parent], c[child]))
{
std:: swap(c[parent], c[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void pop()
{
std::swap(c[0], c[c.size() - 1]);
c.pop_back();
Adjustdown(0);
}
bool empty() const
{
return c.size() == 0;
}
size_t size() const
{
return c.size() ;
}
const T& top() const
{
return c.front();
}
private:
Container c;
Compare comp;
};
};
拓展函数
make_heap(传begin和end)
- 可以将一个vector变成堆
sort_heap(创begin和end)
- 将堆进行堆排序(只能将堆进行堆排序‼️)
push_heap
pop_heap
题目
1.栈的压入、弹出序列
- 入栈序列,每次入栈一个值
- 如果入后的栈顶数据跟出栈序列匹配,——>出栈——>不断匹配,不断出栈
- 入栈序列入完了,就结束了
2.二叉树的层序遍历
原题链接:二叉树的层序遍历

这个题目给的是二叉树,要求是输出一个vector<vector<int>>类型的数组
1.实现二叉树层序遍历:
队列存节点指针,下来一个节点,放一个节点的子节点
-
创造一个队列,存的是TreeNode*类型的指针:
queue<TreeNode*> q; -
插入树根:
q.push(root); -
while循环遍历:
-
条件: 队列中还有元素:
while(!q.empty()) -
循环体:
-
得到队列头节点:
TreeNode* thisnode=q.front(); -
头删:
q.pop(); -
操作:……
-
将这个节点的孩子放进队列:
if(thisnode->left)
q.push(thisnode->left);
if(thisnode->right)
q.push(thisnode->right);
-
-
queue<TreeNode*> q;
int levelsize=0;
if(root)
{
q.push(root);
}
while(!q.empty())
{
TreeNode* thisnode=q.front();
q.pop();
//对thisnode的操作
if(thisnode->left)
q.push(thisnode->left);
if(thisnode->right)
q.push(thisnode->right);
}
2.进一步实现题目要求
在上面的条件下,我们一层一层输入数据
- levelsize作为内部的循环条件
- 在“插入树根”的时候,levelsize置为1——因为第一层只有一个节点
- 然后的循环中,只将levelsize个节点的子节点插入队列中
- 插入结束之后的队列的size,就是下一层的节点个数
- 在“插入树根”的时候,levelsize置为1——因为第一层只有一个节点
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root)
{
vector<vector<int>> vv;
queue<TreeNode*> q;
int levelsize=0;
if(root)
{
q.push(root);
levelsize=1;
}
while(!q.empty())
{
vector<int> v;
while(levelsize--)////////////////////////////////levelsize作为内部的循环条件
{
TreeNode* thisnode=q.front();
q.pop();
v.push_back(thisnode->val);//操作
if(thisnode->left)
q.push(thisnode->left);
if(thisnode->right)
q.push(thisnode->right);
}
vv.push_back(v); //////////////////////////////////////////操作
levelsize=q.size();/////////////////////////////插入结束之后的队列的size,就是下一层的节点个数
}
return vv;
}
};

更多推荐
所有评论(0)