C++初阶—stackqueue

news2024/11/26 15:28:19

目录

1. stack的介绍和使用

1.1 stack的介绍

1.2 stack使用及OJ

 1.2.1 最小栈

1.2.2 栈的弹出压入顺序

1.2.3 逆波兰表达式求值

1.2.4 用两个栈实现一个队列

2. queue的介绍和使用

2.1 queue的介绍

2.2 queue的使用及OJ

2.2.1 用队列实现栈 

2.3 queue的模拟实现

3. priority_queue的介绍和使用(优先级队列)

3.1 priority_queue的介绍

3.2 priority_queue的使用

3.3 priority_queue的使用及OJ

3.3.1 数组中第K个最大的元素

3.4 priority_queue的模拟实现

4. 仿函数

4.1 仿函数介绍

4.2 仿函数less和greater

4.3 仿函数的优缺点及作用

5. 容器适配器

5.1 什么是适配器

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

5.3 deque的简单介绍(了解)

5.3.1 deque的原理介绍

5.3.2 deque的缺陷

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

5.5 STL标准库中对于stack、queue以及priority_queue的模拟实现


1. stack的介绍和使用

1.1 stack的介绍

stack文档

翻译:

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

1.2 stack使用及OJ

 1.2.1 最小栈

分析图:

struct CountReal{
    CountReal(int value,size_t count = 0)
        :_count(count)
        ,_value(value){}

    size_t _count;
    int _value;
};

class MinStack {
public:
    MinStack() {

    }
    
    void push(int val) {
        if(_min.empty() || (!_min.empty() && val < _min.top()._value)){
            _min.push(CountReal(val));
        }else if(val == _min.top()._value){
            _min.top()._count++;
        }
        _st.push(val);
    }
    
    void pop() {
        if(_st.top() == _min.top()._value){
            if(_min.top()._count == 0){
                _min.pop();
            }
            else{
                _min.top()._count--;
            }
        }
        _st.pop();
    }
    
    int top() {
        return _st.top();
    }
    
    int getMin() {
        return _min.top()._value;
    }

private:
    stack<int> _st;
    stack<CountReal> _min;    
};

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack* obj = new MinStack();
 * obj->push(val);
 * obj->pop();
 * int param_3 = obj->top();
 * int param_4 = obj->getMin();
 */

1.2.2 栈的弹出压入顺序

分析图:

class Solution {
public:
    bool IsPopOrder(vector<int>& pushV, vector<int>& popV) {
        // write code here
        int popCount = 0, pushCount = 0;
        stack<int> st;
        while(pushCount < pushV.size()){
            st.push(pushV[pushCount++]);
            while(!st.empty() && popV[popCount] == st.top()){
                st.pop();
                popCount++;
            }
        }
        return st.empty();
    }
};

1.2.3 逆波兰表达式求值

分析图:

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<long long> st;
        for(auto& str:tokens){
            if(str == "+" || str == "-" || str == "*" || str == "/"){
                long long valRight = st.top();st.pop();
                long long valLeft = st.top();st.pop();
                //switch判断条件必须时常量表达式,不能是string
                switch(str[0]){
                    case '+':st.push(valLeft + valRight);break;
                    case '-':st.push(valLeft - valRight);break;
                    case '*':st.push(valLeft * valRight);break;
                    case '/':st.push(valLeft / valRight);break;
                }
            }else{
                st.push(stoll(str));
            }
        }
        return st.top();
    }
};

1.2.4 用两个栈实现一个队列

分析图:

class MyQueue {
public:
//自定义类型调用各自的默认函数,构造函数和析构函数,因此,不需要显示写
    void push(int x) {
        _pushSt.push(x);
    }
    
    int pop() {
        int val = peek();
        _popSt.pop();
        return val;
    }
    
    int peek() {
        if(_popSt.empty()){
            while(!_pushSt.empty()){
                _popSt.push(_pushSt.top());
                _pushSt.pop();
            }
        }
        return _popSt.top();
    }
    
    bool empty() {
        return _popSt.empty() && _pushSt.empty();
    }
private:
    stack<int> _popSt;
    stack<int> _pushSt;
};

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue* obj = new MyQueue();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->peek();
 * bool param_4 = obj->empty();
 */

1.3 stack初步模拟实现

从栈的接口中可以看出,栈实际是一种特殊的vector,只需要尾插尾删,因此使用vector完全可以模拟实现stack,并且支持随机访问,尾插尾删时间复杂度为常数次!!!

    template<class T> class stack {
    public:
        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();
        }
        bool empty() const {
            return _con.empty();
        }
        size_t size() const {
            return _con.size();
        }

    private:
        std::vector<T> _con;
    };

2. queue的介绍和使用

2.1 queue的介绍

queue文档介绍

翻译:

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

2.2 queue的使用及OJ

2.2.1 用队列实现栈 

分析图:

template<class T> class Queue{
public:
    T& front(){
        return _ls.front();
    }
    bool empty(){
        return _ls.empty();
    }
    void pop(){
        _ls.pop_front();
    }
    void push(T& x){
        _ls.push_back(x);
    }
    size_t size(){
        return _ls.size();
    }
    T& back(){
        return _ls.back();
    }
private:
    list<T> _ls;
};

class MyStack {
public:
    MyStack() {

    }
    
    void push(int x) {
        _queue2.push(x);
        while(!_queue1.empty()){
            _queue2.push(_queue1.front());
            _queue1.pop();
        }
        swap(_queue1,_queue2);
    }
    
    int pop() {
        int val = _queue1.front();
        _queue1.pop();
        return val;
    }
    
    int top() {
        return _queue1.front();
    }
    
    bool empty() {
        return _queue1.empty();
    }
private:
    Queue<int> _queue2;
    Queue<int> _queue1;
};

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack* obj = new MyStack();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->top();
 * bool param_4 = obj->empty();
 */

2.3 queue的模拟实现

因为queue的接口中存在头删和尾插,因此使用vector来封装效率太低,vector进行大量头删时需要不断移动数据,故可以借助list来模拟实现queue, 具体如下:

template<class T> class Queue{
public:
    T& front(){
        return _ls.front();
    }
    bool empty(){
        return _ls.empty();
    }
    void pop(){
        _ls.pop_front();
    }
    void push(T& x){
        _ls.push_back(x);
    }
    size_t size(){
        return _ls.size();
    }
    T& back(){
        return _ls.back();
    }
private:
    list<T> _ls;
};

3. priority_queue的介绍和使用(优先级队列)

3.1 priority_queue的介绍

priority_queue优先级队列文档介绍

翻译:

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

3.2 priority_queue的使用

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

【注意】

1. 默认情况下,priority_queue是大堆。

#include <vector>
#include <queue>
#include <functional> // greater算法的头文件
void TestPriorityQueue()
{
	// 默认情况下,创建的是大堆,其底层按照小于号比较
	vector<int> v{3, 2, 7, 6, 0, 4, 1, 9, 8, 5};
	priority_queue<int> q1;
	for (auto& e : v)
		q1.push(e);
	cout << q1.top() << endl;
	// 如果要创建小堆,将第三个模板参数换成greater比较方式
	priority_queue<int, vector<int>, greater<int>> q2(v.begin(), v.end());
	cout << q2.top() << endl;
}

2. 如果在priority_queue中放自定义类型的数据,用户需要在自定义类型中提供> 或者< 的重载。

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}
	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}
	friend ostream& operator<<(ostream& _cout, const Date& d)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}
private:
	int _year;
	int _month;
	int _day;
};
void TestPriorityQueue()
{
	// 大堆,需要用户在自定义类型中提供<的重载
	priority_queue<Date> q1;
	q1.push(Date(2018, 10, 29));
	q1.push(Date(2018, 10, 28));
	q1.push(Date(2018, 10, 30));
	cout << q1.top() << endl;
	// 如果要创建小堆,需要用户提供>的重载
	priority_queue<Date, vector<Date>, greater<Date>> q2;
	q2.push(Date(2018, 10, 29));
	q2.push(Date(2018, 10, 28));
	q2.push(Date(2018, 10, 30));
	cout << q2.top() << endl;
}

3.3 priority_queue的使用及OJ

3.3.1 数组中第K个最大的元素

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int> p(nums.begin(), nums.end());
        while(--k){
            p.pop();
        }
        return p.top();
    }
};

3.4 priority_queue的模拟实现

通过对priority_queue的底层结构就是堆,因此此处只需对对进行通用的封装即可。

    template<class T, class Container = std::vector<T>, class Compare = std::less<T>>
    class priority_queue {
    private:

        //log N
        void AdJustDown(size_t parent) {
            Compare compare;
            size_t child = parent * 2 + 1;
            while (child < _con.size()) {
                if (child + 1 < _con.size() && compare(_con[child], _con[child + 1])) {
                    child += 1;
                }
                if (compare(_con[parent],_con[child])) {
                    std::swap(_con[child], _con[parent]);
                    parent = child;
                    child = parent * 2 + 1;
                }
                else {
                    break;
                }
            }
        }
        //log N
        void AdJustUp(size_t child) {
            Compare compare;
            while (child > 0) {
                size_t parent = (child - 1) / 2;
                if (compare(_con[parent], _con[child])) {
                    std::swap(_con[parent], _con[child]);
                }
                else {
                    break;  
                }
                child = parent;
            }
        }
    public:
        typedef T* iterator;
        priority_queue() {

        }
        priority_queue(iterator first, iterator last) {
            while (first != last) {
                _con.push_back(*first);
                first++;
            }
            for (int i = int(_con.size() - 1 - 1) / 2; i >= 0; i--) {
                AdJustDown(i);
            }
        }
        void push(const T& x) {
            _con.push_back(x);
            AdJustUp(_con.size() - 1);
        }
        void pop() {
            std::swap(_con[0], _con[_con.size() - 1]);
            _con.pop_back();
            AdJustDown(0);
        }
        const T& top() const {
            return _con.front();
        }
        bool empty() const {
            return _con.empty();
        }
        size_t size() const {
            return _con.size();
        }

    private:
        //std::vector<T> _con;
        Container _con;
    };

4. 仿函数

4.1 仿函数介绍

仿函数由来

我们都知道,函数的入参除了是普通变量之外,还可以是函数指针(C 语言中就经常这么用)。那有没有什么手段能代替函数指针呢?答案是有。

  • 定义一个类,类里面定义了某个方法,将该类的对象作为函数的入参,那么在函数中就能调用这个类中的方法

还有更简单的方法吗?答案还是有。

  • 定义一个类,类里面重载函数运算符(),将该类的对象作为函数的入参,那么在函数中同样能调用重载符()里面的方法

所以说,仿函数就是仿造的函数,它并不是一个真正意义上的函数。它是一个类中的运算符()重载,但它具有函数的功能。

仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。

在C语言中,提供了函数指针,但是函数指针参数类型是确定的,而且当参数类型不确定时,会面临无法复用,因此面临此问题,C++提供了仿函数!!!通常仿函数配合着模板使用!!!

参考文章:仿函数

4.2 仿函数less和greater

C++具有很多仿函数,正式仿函数实现了类对象可以像函数一样使用,配合模板参数,更加体现了C++的泛型编程!!!

namespace TestFunctor {
	template<class T> class less {
	public:
		bool operator()(const T& l, const T& r) {
			return l < r;
		}
	};
	template<class T> class greater {
	public:
		bool operator()(const T& l, const T& r) {
			return l > r;
		}
	};
	void testFunctor() {
		less<int> IsFunc;
		std::cout << IsFunc(1, 2) << std::endl;
		std::cout << IsFunc(1, -2) << std::endl;
		greater<int> GtFunc;
		std::cout << GtFunc(1, 2) << std::endl;
		std::cout << GtFunc(1, -2) << std::endl;
	}
	void testFunctor1() {
		int arr[] = { 3,2,7,6,0,4,1,9,8,5 };
		Thb::priority_queue<int, std::vector<int>, greater<int>> pq1(arr, arr + sizeof(arr) / sizeof(arr[0]));
		while (!pq1.empty()) {
			std::cout << pq1.top() << " ";
			pq1.pop();
		}
		std::cout << std::endl;
	}
}

4.3 仿函数的优缺点及作用

仿函数的优缺点

优点:
1)仿函数比函数指针的执行速度快,函数指针时通过地址调用,而仿函数是对运算符operator进行自定义来提高调用的效率。
2)仿函数比一般函数灵活,可以同时拥有两个不同的状态实体,一般函数不具备此种功能。
3)仿函数可以作为模板参数使用,因为每个仿函数都拥有自己的类型。
缺点:
1)需要单独实现一个类。
2)定义形式比较复杂。

仿函数的作用

仿函数通常有下面四个作用:
1)作为排序规则,在一些特殊情况下排序是不能直接使用运算符<或者>时,可以使用仿函数。
2)作为判别式使用,即返回值为bool类型。
3)同时拥有多种内部状态,比如返回一个值得同时并累加。
4)作为算法for_each的返回值使用。

5. 容器适配器

5.1 什么是适配器

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

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

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

5.3 deque的简单介绍(了解)

5.3.1 deque的原理介绍

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

 

双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂

deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,其底层结构如下图所示:

5.3.2 deque的缺陷

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

5.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的优点,而完美的避开了其缺陷。

5.5 STL标准库中对于stack、queue以及priority_queue的模拟实现

namespace Thb {
    template<class T, class Container = std::deque<T>> class stack {
    public:
        explicit stack(const Container& ctnr = Container())
            :_con(ctnr) 
        {
        }
        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();
        }
        bool empty() const {
            return _con.empty();
        }
        size_t size() const {
            return _con.size();
        }

    private:
        //std::vector<T> _con;
        Container _con;
    };

    template<class T, class Container = std::deque<T>> class queue {
    public:
        void push(const T& x) {
            _con.push_back(x);
        }
        void pop() {
            _con.pop_front();
        }
        T& front() {
            return _con.front();
        }
        const T& front() const {
            return _con.front();
        }
        T& back() {
            return _con.back();
        }
        const T& back() const {
            return _con.back();
        }
        bool empty() const {
            return _con.empty();
        }
        size_t size() const {
            return _con.size();
        }

    private:
        //std::vector<T> _con;
        Container _con;
    };

    template<class T, class Container = std::vector<T>, class Compare = std::less<T>>
    class priority_queue {
    private:

        //log N
        void AdJustDown(size_t parent) {
            Compare compare;
            size_t child = parent * 2 + 1;
            while (child < _con.size()) {
                if (child + 1 < _con.size() && compare(_con[child], _con[child + 1])) {
                    child += 1;
                }
                if (compare(_con[parent],_con[child])) {
                    std::swap(_con[child], _con[parent]);
                    parent = child;
                    child = parent * 2 + 1;
                }
                else {
                    break;
                }
            }
        }
        //log N
        void AdJustUp(size_t child) {
            Compare compare;
            while (child > 0) {
                size_t parent = (child - 1) / 2;
                if (compare(_con[parent], _con[child])) {
                    std::swap(_con[parent], _con[child]);
                }
                else {
                    break;  
                }
                child = parent;
            }
        }
    public:
        typedef T* iterator;
        priority_queue() {

        }
        priority_queue(iterator first, iterator last) {
            while (first != last) {
                _con.push_back(*first);
                first++;
            }
            for (int i = int(_con.size() - 1 - 1) / 2; i >= 0; i--) {
                AdJustDown(i);
            }
        }
        void push(const T& x) {
            _con.push_back(x);
            AdJustUp(_con.size() - 1);
        }
        void pop() {
            std::swap(_con[0], _con[_con.size() - 1]);
            _con.pop_back();
            AdJustDown(0);
        }
        const T& top() const {
            return _con.front();
        }
        bool empty() const {
            return _con.empty();
        }
        size_t size() const {
            return _con.size();
        }

    private:
        //std::vector<T> _con;
        Container _con;
    };
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/665561.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用户测试:确保产品质量的关键一环

用户测试&#xff1a;确保产品质量的关键一环 在当今竞争激烈的市场中&#xff0c;产品的质量是企业脱颖而出的关键因素之一。为了确保产品的质量&#xff0c;用户测试成为了开发过程中不可或缺的一环。用户测试是通过让真实用户使用产品并提供反馈意见来验证产品的功能、易用性…

【系统开发】尚硅谷 - 谷粒商城项目笔记(四):JSR303数据校验

文章目录 JSR303数据校验引入依赖和简介配置验证规则开启验证BindResult校验的统一异常处理JSR303分组校验自定义校验注解 JSR303数据校验 引入依赖和简介 引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-bo…

Python和c语言爬虫如何选择?

Python是最受欢迎的爬虫语言之一&#xff0c;因为它易于学习和使用&#xff0c;有大量的库和框架可供选择。JavaScript通常用于Web爬虫&#xff0c;因为它可以直接在浏览器中运行&#xff0c;可以轻松地从动态网站中提取数据。java是一种广泛使用的语言&#xff0c;它有很多强大…

提高电商平台精准营销效果的IP定位离线库应用场景

随着电子商务的快速发展&#xff0c;越来越多的人们选择在线购物。随之而来的是消费者数量的增加和商品竞争的激烈。如何精准地找到目标客户&#xff0c;并进行有效的营销&#xff0c;成为了电商平台需要攻克的难题。在这种情况下&#xff0c;IP定位离线库技术的应用成为了电商…

Python基础语法第一章、认识Python

一、计算机基础概念 1.1什么是计算机? 很多老一辈的人, 管下面这个叫做计算机. 然鹅, 它只是 "计算器", 和计算机是有很大区别的. 现在我们所说的计算机, 不光能进行算术运算, 还能进行逻辑判断, 数据存储, 网络通信等等功能, 以至于可以自动的完成非常复杂的工作…

SerDes的原理解析

01 SerDes简介 首先我们要了解什么是SerDes&#xff0c;SerDes的应用场景又是什么呢&#xff1f;SerDes又有哪些常见的种类&#xff1f;做过FPGA的小伙伴想必都知道串口&#xff0c;与并行传输技术相比&#xff0c;串行传输技术的引脚数量少、扩展能力强、采 用点对点的连接方式…

从uCOSii中抠出来的内存管理程序

从uCOSii中抠出来的内存管理程序 1、学习uCOSii的内存管理的原因 操作系统和内存管理是分不开的&#xff0c;每个操作系统都有自己的一套内存管理方法。在实际应用中&#xff0c;我们尽量使用其自带的内存管理。学习和使用uCOSii也有一段时间了&#xff0c;觉得它的内存管理方…

高效处理报表,掌握原生JS打印和导出报表为PDF的顺畅技巧!

摘要&#xff1a;本文由葡萄城技术团队于CSDN原创并首发。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 前言篇 在日常工作中&#xff0c;报表打印和导出为PDF是经常要处理的任务之一。除了方…

管理类联考——写作——素材篇——论说文——写作素材02——志篇:毅力·坚持

管理类专业学位联考 (写作能力) 论说文素材 02——志篇&#xff1a;毅力坚持 论文说材料: 骐骥一跃&#xff0c;不能十步&#xff1b;驽马十驾&#xff0c;功在不舍。 ——《荀子劝 学》 一&#xff1a;道理论据 咬住青山不放松&#xff0c;立根原在破岩中&#xff1b;千磨…

gitLens插件简单使用(默认上传github)

1.安装 在vscode中的插件管理输入如下后下载 GitLens — Git supercharged 2.配置 点击文件--首选项--设置 点击右上角设置小图标 3.github使用 首先仓库文件一定是要git init是git所管理的 1.在代码文件夹下使用git init创建仓库 2.打开vscode的git管理 3.点击添加暂存区…

如何使用ArcGIS加载天地图

天地图是自然资源部主管&#xff0c;国家基础地理信息中心负责建设的国家地理信息公共服务平台&#xff0c;于2011年1月18日上线。 有的时候可能需要将在线的天地图加载到ArcGIS内&#xff0c;但是加载方式越来越复杂&#xff0c;很多方法都需要申请key&#xff0c;这里为大家…

C++基础(3)——类和对象(2)

前言 本文主要介绍了C中类和对象的基本知识。 4.2.5&#xff1a;深拷贝和浅拷贝 浅拷贝&#xff1a;编译器给我们提供的拷贝函数就是等号复制操作 深拷贝&#xff1a;自己手动重写一个拷贝构造函数&#xff0c;重新new 浅拷贝会出现的问题&#xff1a;如果使用编译器提供的…

AI是什么?AI工具集网站大全

AI是什么&#xff1f; AI 是人工智能的缩写&#xff0c;指的是通过计算机技术和算法来实现智能的能力。我们人类的智能是基于我们的大脑所实现的&#xff0c;而 AI 因此也常被称为机器智能。AI技术需要机器能够感知、推理和行动&#xff0c;这些都需要底层算法的支持&#xff…

2.2C++公有继承与私有继承

C公有继承 C中的公有继承是指一个类可以从另一个类继承公有成员&#xff0c;包括公有成员函数和变量。 公有继承是面向对象编程中最基本的继承方式&#xff0c;它表示父类的公有成员在子类中仍然是公有成员&#xff0c;可以被外部访问。 我写一个 Animal 的基类&#xff0c;…

哪些公司里面有高性能计算方向cuda方向岗位?

CUDA可以为高性能计算、科学计算、深度学习和人工智能、图形渲染和游戏开发、并行数据处理等领域提供了强大的并行计算能力和编程模型。它加速了计算任务的执行&#xff0c;推动了科学研究和创新的进程&#xff0c;同时也为开发者提供了更多的工具和资源&#xff0c;促进了开放…

留个档,Unity Animator state节点的Motion动态替换AnimationClip

前言 由于Unity没有提供直接替换的API&#xff0c;所以在仅限的API下进行逻辑操作。 替换的原理是差不多的&#xff0c;利用AnimatorOverrideController&#xff0c;进行运行时的覆盖。 网上搜索很多文章是利用 名字字符串作为hash的key来进行替换。不满足我自己项目中的需求…

【系统开发】尚硅谷 - 谷粒商城项目笔记(五):分布式缓存

文章目录 分布式缓存缓存使用场景redis作缓存中间件引入redis依赖配置redis堆外内存溢出 缓存失效问题缓存穿透缓存雪崩缓存击穿 Redisson分布式锁导入依赖redisson配置类可重入锁读写锁缓存一致性解决 缓存-SpringCache简介Cacheable自定义缓存配置CacheEvictCachePut原理与不…

【网络】协议的定制与Json序列化和反序列化

文章目录 应用层初识TCP协议通讯流程定制协议再谈协议网络版本计算器Protocal.hppCalServerCalClient Json的安装 应用层 我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层 初识TCP协议通讯流程 建立链接和断开链接 基于TCP协议&#xff0c…

六、使用深度学习构建人脸识别模型

本章介绍机器学习中人脸识别的历史以及从零开始如何构建一个人脸识别模型,含所有训练数据,源代码,不强制要求GPU。使用 docker 来管理库依赖项,提供与平台无关的一致环境。使用 Dlib 进行预处理,使用 Tensorflow + Scikit-learn 训练能够根据图像预测身份的分类器。 1、人…

《吉林省教育学院学报》简介及投稿邮箱

《吉林省教育学院学报》简介&#xff1a; 主管单位 吉林省教育厅 主办单位 吉林省教育学院 出版周期&#xff1a;月刊 国际刊号&#xff1a;ISSN&#xff1a;1671-1580&#xff1b;国内刊号&#xff1a;CN&#xff1a;22-1296/G4&#xff1b;邮发代号&#xff1a;12-223 出…