【C++】一篇文章带你深入了解stack、queue 和 priority_queue

在这里插入图片描述

目录

一、stack的介绍和使用

1.1 stack的介绍

stack的介绍文档

  1. stack是一种容器适配器,专门用在具有LIFO(后进先出)操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。
  2. stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。
  3. stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类。
  4. 标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器,默认情况下使用deque

1.2 stack的使用

1.2.1.1 stack对象的构造

stack();   构造空栈

在这里插入图片描述


1.2.1.2 stack对象的容量操作

1.2.1.2.1 empty()函数
bool empty() const;		判断是否为空
int main()
{
	stack<int> st;
	cout << st.empty() << endl;

	st.push(1);
	cout << st.empty() << endl;

	return 0;
}

在这里插入图片描述


1.2.1.2.2 size()函数
size_type size() const;      获取元素个数
int main()
{
	stack<int> st;
	cout << st.size() << endl;

	for (int i = 0; i < 10; i++)
	{
		st.push(i);
	}
	cout << st.size() << endl;

	return 0;
}

在这里插入图片描述


1.2.1.3 stack对象的增删查改及访问

1.2.1.3.1 push()函数
void push (const value_type& val);  	入栈
int main()
{
	stack<int> st;

	for (int i = 0; i < 10; i++)
	{
		st.push(i);
	}

	return 0;
}

在这里插入图片描述


1.2.1.3.2 pop()函数
void pop();	  出栈

在这里插入图片描述


1.2.1.3.3 top()函数
访问栈顶元素
	  value_type& top();
const value_type& top() const;

在这里插入图片描述


1.3 stack的模拟实现

1.3.1 stack中 push 和 pop 的实现

namespace aj
{
    template<class T, class Container = deque<T>>
    class stack
    {
    public:
        void push(const T& x)
        {
            _con.push_back(x);
        }

        void pop()
        {
            _con.pop_back();
        }

    private:
        Container _con;
    };
};

1.3.2 stack中 empty 、size 和 top 的实现

namespace aj
{
    template<class T, class Container = deque<T>>
    class stack
    {
    public:
        T& top()
        {
            return _con.back();
        }

        const T& top()const
        {
            return _con.back();
        }

        size_t size()const
        {
            return _con.size();
        }

        bool empty()const
        {
            return _con.empty();
        }

    private:
        Container _con;
    };
};

1.3.3 stack 实现汇总及函数测试

namespace aj
{
    template<class T, class Container = deque<T>>
    class stack
    {
    public:
        stack()
        {}

        void push(const T& x)
        {
            _con.push_back(x);
        }

        void pop()
        {
            _con.pop_back();
        }

        T& top()
        {
            return _con.back();
        }

        const T& top()const
        {
            return _con.back();
        }

        size_t size()const
        {
            return _con.size();
        }

        bool empty()const
        {
            return _con.empty();
        }

    private:
        Container _con;
    };
};

void test_Stack()
{
    aj::stack<int> st;

    st.push(1);
    st.push(2);
    st.push(4);
    st.push(6);
    st.push(9);
    st.push(0);

    while (!st.empty())
    {
        cout << "top:" << st.top() << "    ";
        cout << "size:" << st.size() << "    ";
        cout << "empty:" << st.empty() << endl;
        st.pop();
    }
}

二、queue的介绍和使用

2.1 queue的介绍

queue的介绍文档

  1. 队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端提取元素。
  2. 队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列。
  3. 底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。
  4. 标准容器类deque和list满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标准容器deque。

2.2 queue的使用

2.2.1 queue的构造函数

queue();	  构造空队列
int main()
{
	queue<int> q;

	return 0;
}

在这里插入图片描述


2.2.2 queue对象的容量操作

2.2.2.1 empty()函数
bool empty() const;	  判断是否为空
int main()
{
	queue<int> q;
	cout << q.empty() << endl;

	q.push(1);
	cout << q.empty() << endl;

	return 0;
}

在这里插入图片描述


2.2.2.2 size()函数
size_type size() const;	  获取元素个数
int main()
{
	queue<int> q;
	cout << q.size() << endl;

	for (int i = 0; i < 10; i++)
	{
		q.push(i);
	}
	cout << q.size() << endl;

	return 0;
}

在这里插入图片描述


2.2.3 queue对象的增删查改及访问

2.2.3.1 push()函数
void push (const value_type& val);	  入队列
int main()
{
	queue<int> q;

	for (int i = 0; i < 5; i++)
	{
		q.push(i);
	}

	return 0;
}

在这里插入图片描述


2.2.3.2 pop()函数
void pop();	  出队列
int main()
{
	queue<int> q;

	for (int i = 0; i < 5; i++)
	{
		q.push(i);
	}

	q.pop();
	q.pop();

	return 0;
}

在这里插入图片描述


2.2.3.3 front()函数
返回队头的元素
	  value_type& front();
const value_type& front() const;
int main()
{
	queue<int> q;

	for (int i = 0; i < 5; i++)
	{
		q.push(i);
	}

	while (!q.empty())
	{
		cout << q.front() << ' ';
		q.pop();
	}

	cout << endl;

	return 0;
}

在这里插入图片描述


2.2.3.4 back()函数
返回队尾的元素
	  value_type& back();
const value_type& back() const;

在这里插入图片描述


2.3 queue的模拟实现

2.3.1 queue中 push 和 pop 的实现

namespace aj
{
    template<class T, class Container = deque<T>>
    class queue
    {
    public:
        void push(const T& x)
        {
            _con.push_back(x);
        }

        void pop()
        {
            _con.pop_front();
        }
    private:
        Container _con;
    };
}

2.3.2 queue中 empty 和 size 的实现

namespace aj
{
    template<class T, class Container = deque<T>>
    class queue
    {
    public:
        size_t size()const
        {
            return _con.size();
        }

        bool empty()const
        {
            return _con.empty();
        }

    private:
        Container _con;
    };
}

2.3.3 queue中 front 和 back 的实现

namespace aj
{
    template<class T, class Container = deque<T>>
    class queue
    {
    public:
        T& back()
        {
            return _con.back();
        }

        const T& back()const
        {
            return _con.back();
        }

        T& front()
        {
            return _con.front();

        }

        const T& front()const
        {
            return _con.front();
        }

    private:
        Container _con;
    };
}


2.3.4 queue 实现汇总及函数测试

namespace aj
{
    template<class T, class Container = deque<T>>
    class queue
    {
    public:
        queue()
        {}

        void push(const T& x)
        {
            _con.push_back(x);
        }

        void pop()
        {
            _con.pop_front();
        }

        T& back()
        {
            return _con.back();
        }

        const T& back()const
        {
            return _con.back();
        }

        T& front()
        {
            return _con.front();

        }

        const T& front()const
        {
            return _con.front();
        }

        size_t size()const
        {
            return _con.size();
        }

        bool empty()const
        {
            return _con.empty();
        }

    private:
        Container _con;
    };
}

void test_Queue()
{
    aj::queue<int> qu;

    qu.push(1);
    qu.push(2);
    qu.push(4);
    qu.push(6);
    qu.push(9);
    qu.push(0);

    while (!qu.empty())
    {
        cout << "front:" << qu.front() << "    ";
        cout << "back:" << qu.back() << endl;

        qu.pop();
    }
}

三、priority_queue的介绍和使用

3.1 priority_queue的介绍

priority_queue的介绍文档

  1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
  2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
  3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,priority_queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
  4. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。
  5. 标准容器类vectordeque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector
  6. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。

3.2 priority_queue的使用

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。注意:默认情况下priority_queue是大堆。


3.2.1 priority_queue的构造函数

构造一个空的优先级队列
priority_queue (const Compare& comp = Compare(),
                     const Container& ctnr = Container());
                     
构造并使用默认的比较函数和底层容器类型来初始化优先队列
template <class InputIterator>
         priority_queue (InputIterator first, InputIterator last,
                         const Compare& comp = Compare(),
                         const Container& ctnr = Container());
int main()
{
	priority_queue<int> pq;

	return 0;
}

int main()
{
	string s("Love");
	priority_queue<int> pq(s.begin(),s.end());

	return 0;
}

在这里插入图片描述

在这里插入图片描述


3.2.2 priority_queue对象的容量操作

3.2.2.1 empty()函数
bool empty() const;	  判断是否为空
int main()
{
	priority_queue<int> pq;
	cout << pq.empty() << endl;

	pq.push(1);
	cout << pq.empty() << endl;

	return 0;
}

在这里插入图片描述


3.2.2.2 size()函数
size_type size() const;	  获取元素个数

在这里插入图片描述


3.2.3 priority_queue对象的增删查改及访问

3.2.3.1 push()函数
void push (const value_type& val);	  
向优先级队列中插入一个元素val
int main()
{
	priority_queue<int> pq;

	for (int i = 0; i < 3; i++)
	{
		pq.push(i);
	}

	return 0;
}

在这里插入图片描述


3.2.3.2 pop()函数
void pop();	 
删除优先级队列中最大(最小)元素,即堆顶元素

在这里插入图片描述


3.2.3.3 top()函数
const value_type& top() const;
返回优先级队列中最大(最小)元素,即堆顶元素
int main()
{
	priority_queue<int> pq;

	for (int i = 0; i < 3; i++)
	{
		pq.push(i);
	}

	while (!pq.empty())
	{
		cout << pq.top() << ' ';
		pq.pop();
	}

	cout << endl;

	return 0;
}

在这里插入图片描述


3.3 priority_queue的模拟实现

3.3.1 priority_queue 中 向上调整函数 和 向下调整函数 的实现

namespace aj
{
    template<class T>
    class less
    {
    public:
        bool operator()(const T& x1, const T& x2)
        {
            return x1 < x2;
        }
    };

    template<class T>
    class greater
    {
    public:
        bool operator()(const T& x1, const T& x2)
        {
            return x1 > x2;
        }
    };

    template <class T, class Container = vector<T>, class Compare = less<T> >
    class priority_queue
    {
    public:
        void adjust_up(int child)
        {
            int parent = (child - 1) / 2;
            while (child > 0)
            {
                if (comp(c[parent] , c[child]))
                {
                    swap(c[child], c[parent]);
                    child = parent;
                    parent = (child - 1) / 2;
                }
                else
                {
                    break;
                }
            }
        }

        void adjust_down(int parent)
        {
            size_t child = parent * 2 + 1;
            while (child < c.size())
            {
                if (child + 1 < c.size() && comp(c[child] , c[child + 1]))
                {
                    child++;
                }

                // if (c[parent] < c[child])
                if (comp(c[parent] , c[child]))
                {
                    swap(c[child], c[parent]);
                    parent = child;
                    child = parent * 2 + 1;
                }
                else
                {
                    break;
                }
            }
        }

    private:
        Container c;
        Compare comp;
    };
};

3.3.2 priority_queue 中 无参构造函数 和 迭代器区间构造函数的实现

namespace aj
{ 
    template <class T, class Container = vector<T>, class Compare = less<T> >
    class priority_queue
    {
    public:
        priority_queue()
        {};

        template <class InputIterator>
        priority_queue(InputIterator first, InputIterator last)
            :c(first, last)
        {
            for (int i = (size() - 2) / 2; i > 0; --i)
            {
                adjust_up(i);
            }
        }

    private:
        Container c;
        Compare comp;
    };
};

3.3.3 priority_queue 中 push 和 pop 的实现

namespace aj
{
    template <class T, class Container = vector<T>, class Compare = less<T> >
    class priority_queue
    {
    public:

        void push(const T& x)
        {
            c.push_back(x);
            adjust_up(c.size() - 1);
        }

        void pop()
        {
            swap(c[0], c[c, size() - 1]);
            c.pop_back();
            adjust_down(0);
        }

    private:
        Container c;
        Compare comp;
    };
};

3.3.4 priority_queue 中 empty 、size 和 top 的实现

namespace aj
{
    template <class T, class Container = vector<T>, class Compare = less<T> >
    class priority_queue
    {
    public:
        bool empty() const
        {
            return c.empty();
        }

        size_t size() const
        {
            return c.size();
        }

        const T& top() const
        {
            return c[0];
        }

    private:
        Container c;
        Compare comp;
    };

};

3.3.5 priority_queue 实现汇总及函数测试

#pragma once

#include <iostream>
#include<vector>
#include<functional>

using namespace std;

namespace aj
{
    template<class T>
    class less
    {
    public:
        bool operator()(const T& x1, const T& x2)
        {
            return x1 < x2;
        }
    };

    template<class T>
    class greater
    {
    public:
        bool operator()(const T& x1, const T& x2)
        {
            return x1 > x2;
        }
    };

    template <class T, class Container = vector<T>, class Compare = less<T> >
    class priority_queue
    {
    public:
        priority_queue() 
        {};

        template <class InputIterator>
        priority_queue(InputIterator first, InputIterator last)
            :c(first,last)
        {
            for (int i = (size() - 2) / 2; i > 0; --i)
            {
                adjust_up(i);
            }
        }

        bool empty() const
        {
            return c.empty();
        }

        size_t size() const
        {
            return c.size();
        }

        const T& top() const
        {
            return c[0];
        }

        void adjust_up(int child)
        {
            int parent = (child - 1) / 2;
            while (child > 0)
            {
                if (comp(c[parent] , c[child]))
                {
                    swap(c[child], c[parent]);
                    child = parent;
                    parent = (child - 1) / 2;
                }
                else
                {
                    break;
                }
            }
        }

        void adjust_down(int parent)
        {
            size_t child = parent * 2 + 1;
            while (child < c.size())
            {
                if (child + 1 < c.size() && comp(c[child] , c[child + 1]))
                {
                    child++;
                }

                // if (c[parent] < c[child])
                if (comp(c[parent] , c[child]))
                {
                    swap(c[child], c[parent]);
                    parent = child;
                    child = parent * 2 + 1;
                }
                else
                {
                    break;
                }
            }
        }

        void push(const T& x)
        {
            c.push_back(x);
            adjust_up(c.size() - 1);
        }

        void pop()
        {
            swap(c[0], c[c, size() - 1]);
            c.pop_back();
            adjust_down(0);
        }

    private:
        Container c;
        Compare comp;
    };

    void test_priority_queue1()
    {
        priority_queue<int> pq;
        pq.push(1);
        pq.push(2);
        pq.push(3);
        pq.push(4);

        while (!pq.empty())
        {
            // cout << pq.size() << ' ';

            cout << pq.top() << ' ';
            pq.pop();
        }
    }

    void test_priority_queue2()
    {
        string s("I Love You");
        priority_queue<int> pq(s.begin(),s.end());

        while (!pq.empty())
        {
            // cout << pq.size() << ' ';

            cout << pq.top() << ' ';
            pq.pop();
        }
    }
};

四、容器适配器

4.1 什么是适配器

适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口

在这里插入图片描述


4.2 STL标准库中stack和queue的底层结构

虽然stackqueue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配器,这是因为stack和队列只是对其他容器的接口进行了包装,STL中stackqueue默认使用deque,比如:
在这里插入图片描述


4.3 deque的简单介绍

4.3.1 deque的原理介绍

deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高。

在这里插入图片描述
deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,其底层结构如下图所示:
在这里插入图片描述
双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂,如下图所示:

在这里插入图片描述
那deque是如何借助其迭代器维护其假想连续的结构呢?
在这里插入图片描述


4.3.2 deque的缺陷

vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。
list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。
但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构


4.4 为什么选择deque作为stack和queue的底层默认容器

stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构,都可以作为stack的底层容器,比如vector和list都可以;queue是先进先出的特殊线性数据结构,只要具有
push_back和pop_front操作的线性结构,都可以作为queue的底层容器,比如list。但是STL中对stack和queue默认选择deque作为其底层容器,主要是因为:

  1. stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行操作。
  2. 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,deque不仅效率高,而且内存使用率高。

结合了deque的优点,而完美的避开了其缺陷


结尾

如果有什么建议和疑问,或是有什么错误,大家可以在评论区中提出。
希望大家以后也能和我一起进步!!🌹🌹
如果这篇文章对你有用的话,请大家给一个三连支持一下!!🌹🌹
在这里插入图片描述

  • 94
    点赞
  • 79
    收藏
    觉得还不错? 一键收藏
  • 110
    评论
6、 函数模板和类模板 3 6.1函数模板 4 6.1.1为什么要有函数模板 4 6.1.2函数模板语法 5 6.1.3函数模板和模板函数 6 6.1.4函数模板做函数参数 6 6.1.5函数模板遇上函数重载 8 6.1.6 C++编译器模板机制剖析 10 6.2类模板 18 6.2.1为什么需要类模板 18 6.2.2单个类模板语法 18 6.2.3继承中的类模板语法 20 6.2.4类模板语法知识体系梳理 21 6.2.5类模板中的static关键字 23 6.3类模板在项目开发中的应用 25 6.4作业 29 7、C++的类型转换 29 7.1 类型转换名称和语法 29 7.2 类型转换一般性介绍 29 7.3 典型案例 30 7.3.1 static_cast用法和reinterpret_cast用法 30 7.3.2 dynamic_cast用法和reinterpret_cast用法 31 7.3.3 const_cast用法 33 7.4 总结 33 8、异常处理机制专题 33 8.1 异常处理的基本思想 34 8.1.1传统错误处理机制 34 8.1.2异常处理的基本思想 34 8.2 C++异常处理的实现 35 8.2.1异常基本语法 35 8.2.2栈解旋(unwinding) 39 8.2.3异常接口声明 40 8.2.4异常类型和异常变量的生命周期 40 8.2.5异常的层次结构(继承在异常中的应用) 46 8.3标准程序库异常 47 8.4训练强化 51 9 C++输入和输出流 51 9.1 I/O流的概念和流类库的结构 51 9.2标准I/O流 53 9.2.1标准输入流 55 9.2.2标准输出流 59 9.3文件I/O 66 9.3.1文件流类和文件流对象 66 9.3.2C++文件的打开与关闭 67 9.3.3C++对ASCII文件的读写操作 69 9.3.4 C++对二进制文件的读写操作 74 9.4作业练习 75 10、STL实用技术专题 79 10.1 STL(标准模板库)理论基础 79 10.1.1基本概念 79 10.1.2容器 80 10.1.3迭代器 82 10.1.4算法 82 10.1.5C++标准库 82 10.1.6模板简要回顾 85 10.2容器 86 10.2.1 STL的string 86 10.2.2Vector容器 90 10.2.3Deque容器 96 10.2.4stack容器 101 10.2.5Queue容器 103 10.2.6List容器 105 10.2.7优先级队列priority_queue 110 10.2.8Set和multiset容器 111 10.2.9Map和multimap容器 118 10.2.10容器共性机制研究 123 10.2.11其他 124 10.3算法 125 10.3.1算法基础 125 10.3.2STL算法中函数对象和谓词 138 10.3.3常用的遍历算法 148 10.3.4常用的查找算法 152 10.3.5常用的排序算法 154 10.3.6常用的拷贝和替换算法 156 10.3.7常用的算术和生成算法 157 10.3.8常用的集合算法 158 10.4 STL综合案例 159 10.4.1案例学校演讲比赛 159 10.4.2案例:足球比赛 161

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 110
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值