C++ 棧-隊列-優先級隊列

目錄

1 棧

2?隊列

3 deque 介紹

4 優先級隊列

5 反向迭代器


棧也是我們在C語言就模擬實現過的一種數據結構,在C++中,棧其實和我們前面模擬實現過的string、vector等容器有一點區別,站起是不是容器,而是一種容器適配器,我們可以打開C++官方文檔找到stack的介紹來看一下

同時我們打開隊列的文檔就會發現,隊列也是一種容器適配器,

那么適配器到底是什么東西呢?適配器其實是一種設計模式,是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性、程序的重用性。

在前面的容器的模擬實現中,困擾我們頗多的迭代器也是一種設計模式。而適配器是設計模式中結構模式中的一種。 適配器本質上是進行轉換的,使用現有的東西封裝轉換成你想要的東西。

1 棧

我們說棧是一種容器適配器,而適配器的本質是進行轉換。?

我們發現棧的結構很少,因為他的結構限制了他的接口,而在之前C語言我們實現棧使用一個動態順序表來實現的,因為棧的接口其實就相當于順序表的尾插尾刪,使用數組完成效率很高,這其實就是一種轉換,我們將順序表通過封裝接口轉換出我們想要的棧。而在C++中,我們也能夠使用前面模擬實現過的 vector 和 list 來作為棧的適配容器,具體使用哪個其實都行,在棧的實現上,它們各有千秋,而我們看到的庫里面的棧的默認適配容器則是一個雙端隊列 deque?

這個容器我們還沒學過,在棧和隊列后面會講一下。

目前我們實現棧就使用vector來作默認容器,因為其實 vector 實現棧的效率其實跟deuqe差不多的。棧的基本的結構如下,就是一個vector,然后封裝vector的接口來實現棧的接口

?

?

棧的構造函數只有一個,而這個構造函數我們直接理解為無參的構造就行了,這里的缺省值的意思其實就是可以使用我們的適配器容器的對象來初始化,但是我們一般實現棧是不需要這樣初始化的。

既然是一個無參構造,那么我們需要自己寫嗎?不需要,因為編譯器自動生成的就夠用了,棧對象中只有一個我們實現過的vector的對象,而我們實現過的vector是有默認構造的,那么編譯器自動生成構造函數就會去調用vector的默認構造去對st進行初始化。那么析構也是一樣的,我們也不需要自己去實現。

那么剩下的幾個接口 size 、empty 、top 、 push 、pop就很簡單了。

????????

		bool empty()const{return st.empty();}size_t size()const{return st.size();}T& top(){assert(!empty());return st.back();}const T& top()const{assert(!empty());return st.back();}void push(const T& val){st.push_back(val);}void pop(){assert(!empty());st.pop_back();}

這樣一來,一個簡單的棧就實現了。棧是不需要實現迭代器的,因為不需要,對于棧而言,遍歷整個棧就是需要用戶不斷地取top,然后pop,而不是像容器一樣通過迭代器遍歷。

2?隊列

隊列是先入先出的結構,也就是頭插和頭刪,尾插頭刪vector的效率就不如list了,所以我們實現queue使用list作為默認容器。?

?

其他的接口也基本上和stack差不多

	bool empty()const{return q.empty();}size_t size()const{return q.size();}const T& front()const{assert(!empty());return q.front();}const T& back()const{assert(!empty());return q.back();}void push(const T& val){q.push_back(val);}void pop(){assert(!empty());q.pop_front();}

3 deque 介紹

為什么stack和queue的默認適配容器都是 deque?雙端隊列呢?deque到底是何方神圣?

在介紹 deque之前,我們先回想一下vector和list都有什么缺點。vector的缺點就是擴容的開銷大,因為他的擴容需要挪動數據,同時他的頭插頭刪的開銷也很大,不適合作為隊列的適配容器。 而list的缺點則是不支持隨機訪問,同時由于空間不連續,他的CPU高速緩存命中率低。

那么能不能設計一個容器,能夠完美融合vector和list的優點,同時能夠規避他們的缺點呢?從結果上來說,肯定是沒有的,如果有容器能夠完美替代vector和list,如今我們也不會學習這兩個容器了,早就被湮沒在歷史中。雙端隊列就是一個兼具?vector 和 list 的優點的一個容器 ,比如他的頭插頭刪效率高,cpu命中率高等,但是他的這些性能都比不上單獨拎出來的 list 和vector,相當于不上不下的水平,list 和vector在各自的領域是極致效率的,而deque的效率最多只能用拔尖來描述,比不上list 和vector。

deque是怎么做到既適合頭插頭刪,同時cpu命中率還高的呢?這就要從它的結構說起了。

deque其實是由一個一個的數組來構成的,同時 deque 內部存儲了一個指針數組,來保存些數組的地址。

?

當我們第一次插入數據時,首先申請一個數組buffer,buffer的大小是由我們自己來指定的靜態數組。 第一個申請的數組的指針保存在 中控數組的中間位置上

?

而當我們不斷尾插數據時,數組的尾部進行插入,如果數組滿了,再次尾插,則會再開辟一個同樣大小的數組,將數組的地址保存在中控數組的后一個位置

?

依次類推,當尾插到一定數量時,中控數組的后半部分都已經被填滿了,這時候就會被中控數組進行二倍擴容

?

擴容之后會將原中控數組保存的指針全部拷貝到新的數組中,同時不會釋放原來的 buffer ,再創建一個新的buffer來存儲新插入的數據。 所以說,deque其實也是有擴容的消耗的,只是他的消耗相比于vector來說要小得多,它只需要挪動指針數組就行了。

而如果要頭插的話,則需要在中控數組的中間位置的前面填充新創建的buffer的指針,然后將新的數組從專門用于頭插的 buffer 的尾部開始插入,不斷往前插入,過程基本類似于尾插。

而deque支持的隨機訪問則需要在 [ ]重載的時候進行一定的運算轉換來找到對用下標的數據,所以他的隨機訪問的效率其實也是不如vector的。

同時,我們也能發現一個問題,就是deque在進行中間位置的插入刪除的話會很復雜,如果要插入或者刪除的數據個數剛好是 buffer 大小的整數倍還好說,如果不是整數倍,則要比 vector 和list 麻煩的多,所以其實deque的任意位置插入刪除的效率也是比不上 list 的?

但是這其實也是它的優勢所在,他進行除了中間位置插入刪除之外的其他的操作的操作都還不錯,我們知道 queue 和stack是不需要中間位置的插入刪除的,所以由 deque 作為 stack 和 queue 的默認適配容器剛剛好,很通用。?

如果要模擬實現 deque ,最復雜的其實還不是他的下標轉換以及他的中間位置的插入刪除,而是他的迭代器的實現,他的迭代器的實現需要四個指針來實現,具體的實現大家可以在網上查閱相關資料,總之十分的復雜,簡直折磨人 。我們只需要知道她大概的實現就OK了,最重要的就是中控數組也就是一個指針數組就行了。

4 優先級隊列

優先級隊列其實就是把優先級最高的或者優先級最低的數據放在第一個,那么之前我們有沒有學過類似的結構呢? 學過,就是我們的堆。 大堆就是把最大的數放在堆頂,也就是優先級最高的數據放在最前面,而小堆就是把優先級最小的數據放在最前面。而我們以前實現了堆,他的物理結構其實是一個數組,也就是一個vector,而他的邏輯結構我們看成是一棵完全二叉樹。

那么優先級隊列的默認適配容器我們就能夠使用vector來實現。同時,雖然他叫做優先級隊列,但是他的本質上還是一個堆,同時他也只能夠讀取頭部數據以及頭刪,他的插入是需要進行調整來保證堆結構的。

我們可以看一下庫里面的優先級隊列的模板參數,我們發現模板參數除了我們意料之中的數據類型,適配容器,還有一個 less<...> ,這個less< >相信我們在leetcode刷題的時候或者我們使用sort的時候也經常使用。

我們先不管這個less 仿函數,假設我們就用 vector 實現一個大堆,實現完之后我們再將仿函數加上。

他的接口中,默認的構造和析構編譯器自動生成的就夠用,不需要我們自己寫。而 empty ,size 和top接口實現起來也十分簡單,

	public:bool empty()const{return pq.empty();}size_t size()const{return pq.size();}const T& top()const{assert(!empty());return pq[0];}

他的重點在插入和刪除上,對于插入數據,以前我們使用堆的時候插入數據首先是插入在數組的最后,也就是尾插,然后通過向上調整來維持堆結構。我們可以寫一下push和adjustup的實現

		//push 就是尾插void push(const T& val){pq.push_back(val);//然后向上調整adjust(size()-1); //傳新插入的數據的的下標}void adjust(size_t child){size_t parent = (child - 1) / 2; //父節點的下標while (child) //child 為 0 時退出循環{if (pq[child] >pq[parent])  //假設是建大堆、{swap(pq[child],pq[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}

然后實現 pop ,pop 就是刪除 棧頂元素,但是刪除棧頂元素之后我們還需要通過向下調整來保證棧結構。

		void pop(){swap(pq.front(),pq.back());pq.pop_back();adjustdown(0); //開始調整下標 }//向下調整void adjustdown(size_t parent){size_t maxchild = parent*2+1;while (maxchild<size()){if (maxchild + 1 < pq.size() && pq[maxchild + 1] > pq[maxchild]) //假設是大堆maxchild++;if (pq[parent] < pq[maxchild]){swap(pq[parent],pq[maxchild]);parent = maxchild;maxchild = parent*2 + 1;}elsebreak;}}

這樣一來就寫好了彈出棧頂元素和向下調整的方法了。

最后再實現一個迭代器區間的構造函數,

這個函數還是要用到仿函數,但是我們在這里其實也可以不使用仿函數,因為我們可以直接復用adjustdown來進行向下調整建大堆,一會講完了仿函數之后我們也只需要修改向下調整和向上調整的邏輯就行了,而不用修改這里的構造函數。

向下調整建大堆我們只需要找到第一個非葉子節點,然后往前遍歷,讓每一棵子樹都是大堆就行了。至于為什么不用向上調整建大堆 可以 看一下我的數據結構專欄的堆的實現,是由于向下調整建堆的效率比向上調整建堆的效率是要高得多的,效率差距主要體現在葉子節點的向上調整中。

	//默認構造priority_queue(){}//迭代器區間構造template<class InputIterator>priority_queue(InputIterator first, InputIterator last):pq(first,last){size_t end = (size() - 1 - 1) / 2;while (end!=0){adjustdown(end);end--;}adjustdown(end);}

這里要注意的一點就是,如果我們寫了迭代器區間的構造,那么編譯器就不會自動生成默認構造函數了,我們需要自己實現一個默認構造,而這里的priority_queue的默認構造我們也什么都不用寫,編譯器會再初始化列表自動調用vector的默認構造來進行初始化,析構函數也是一樣的,編譯器自動生成的就能夠完成析構了。

仿函數

實現完一個大堆,我們就要開始該清楚仿函數是什么東西,只有搞清楚了仿函數我們才能夠實現一個完成的優先級隊列。

仿函數其實就是一個類,這個類實例化出來的對象就叫仿函數,類里面重載了( ) 這個函數調用操作符。叫做仿函數的原因是這個類的對象可以像函數調用一樣實現按我們的邏輯。

比如:

template<typename T>
class myless
{
public:bool operator()(const T& x, const T& y)const {return x < y;}
};

這樣一個類能夠干什么呢?

我們使用 sort 的時候是不是有這樣的用法??

sort ( container :: iterator first? , container :: iterator last , less<T> ( ) )

這里面的 less<T>() 就是一個仿函數,為什么這么說呢?參考我們上面實現的仿函數類, <T>表示數據類型是T,編譯器在實例化出這個匿名對象的時候就會使用具體的 T 去實例化,然后用這個匿名對象的 ( ) 也就是 operator() 來傳參,也就是將這個成員函數傳給了 sort 。

		vector<int> v;v.push_back(5);v.push_back(7);v.push_back(4);v.push_back(3);v.push_back(9);v.push_back(2);vector<int>v1 = v;sort(v.begin(),v.end(),myless<int>());for (auto e : v){cout << e << " ";}cout << endl;sort(v1.begin(), v1.end(), less<int>());for (auto e : v){cout << e << " ";}cout << endl;

我們可以看一下我們自己實現的的 myless 和庫里面的 less 是不是一樣的

其實仿函數的傳參就有點類似于我們以前C語言傳遞函數指針,但是函數指針寫起來很復雜,尤其是對于一些復雜的函數指針,同時函數指針也不支持泛型編程,每一個類型的比較都需要寫一個函數來實現,但是仿函數他就是一個類實例化出來的對象,我們傳參穿的也是這個對象,我們只需要顯式傳遞數據類型,就能夠實例化出我們想要的數據類型的比較函數。

上面的傳參我們也可以使用一個有名對象來傳參

		myless<int> mless;sort(v.begin(),v.end(),mless);

我們以前的泛型都是類型的泛型,而這里的仿函數的泛型則是邏輯的泛型。

那么我們就可以用仿函數來完善我們的優先級隊列了。

	template<typename T ,typename container_type=vector<T>,typename CMP=less<T>>
void adjustup(size_t child){CMP cmp;size_t parent = (child - 1) / 2; //父節點的下標while (child) //child 為 0 時退出循環{//if (pq[child] >pq[parent])  //假設是建大堆if(cmp(pq[parent],pq[child])){swap(pq[child],pq[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}
		//向下調整void adjustdown(size_t parent){CMP cmp;size_t maxchild = parent*2+1;while (maxchild<size()){//if (maxchild + 1 < pq.size() && pq[maxchild + 1] > pq[maxchild]) //假設是大堆if (maxchild + 1 < pq.size() && cmp(pq[maxchild],pq[maxchild+1])) //假設是大堆maxchild++;//if (pq[parent] < pq[maxchild])if (cmp(pq[parent] , pq[maxchild])){swap(pq[parent],pq[maxchild]);parent = maxchild;maxchild = parent*2 + 1;}elsebreak;}}

這還只是仿函數的基礎用法,仿函數還有一些高級用法,比如我們要實現兩個對象的大小的比較

class A 
{
public:A(int n = 0):_n(n){}bool operator<(const A& a)const{return _n < a._n;}
private:int _n;
};

我們首先要在該類中重載 < 運算符,然后創建兩個對象進行比較


int main()
{A a1(10);A a2(20);int ret = myless<A>()(a1,a2);cout << ret << endl;return 0;
}

目前來看沒什么問題,但是當我們的對象是使用 new 申請的呢?這時候我們一般使用的就是對象的指針了,如果我們想要直接傳這兩個指針進行兩個指針所指向的對象比較,目前我們實現的仿函數類是做不到的,

	A* p1 = new A(20);A* p2 = new A(10);int ret = myless<A*>()(p1,p2);cout << ret << endl;

?我們只能這么用,而且這樣比較出來的結果還不一定是對的,因為這樣比較的話,比較的是兩個指針的大小,而不是對象的大小了,但是我們就是想要比較類對象的大小要怎么做呢??

很簡單,我們只需要在仿函數類中再重載一個( )函數就行了.

	bool operator<(const A& a)const{return _n < a._n;}

這樣一來,如果我們傳的是A類型的指針,我們也可以直接使用 myless<A>的仿函數進行比較

	A* p1 = new A(20);A* p2 = new A(10);int ret = myless<A>()(p1,p2);cout << ret << endl;

我們這里舉例的例子其實用的不是很合適,更恰當的例子是類似于一個排序算法的場景,既要支持對象本身的比較,還要支持參數是對象指針時對指針所指向的對象的比較。

5 反向迭代器

為什么我們前面沒實現反向迭代器,因為反向迭代器其實就是一種適配器,為什么這么說呢?我們知道,反向迭代器和正向迭代器唯一的區別就是 ++ 和 -- 的方向,正向迭代器 ++? 是從前往后走 ,而反向迭代器的 ++ 是從后往前走,所以要實現反向迭代器我們只需要封裝和復用正向迭代器的代碼,就能夠很輕松的實現。

拿 list 的反向迭代器來舉例,反向迭代器的 rbegin() 和 rend() 的位置是哪呢?

我們可以向正向迭代器一樣,讓?rbegin 指向最后一個節點,也就是在迭代器中維護 phead->prev ,然后rend 指向第一個節點的前一個節點,也就是 phead ,這樣是一個標準的反向迭代器,但是這樣卻沒有最大程度的復用正向迭代器。 讓 rbegin 直接復用 正向迭代器的 end ,讓rend 直接復用 begin ,但是這樣設計有一個要注意的地方就是,解引用的時候 ,要返回的不是當前指針指向的節點的數據,而是當前節點的建一個節點的數據,因為我們相當于 將區間整體往右移了一個節點。 還有就是要實現 反向迭代器的 ++ ,我們就需要正向迭代器中重載了 - - ,要實現 - - ,則需要復用正向迭代器的 ++ 。?

那么反向迭代器的實現就很簡單了,下面是一個針對 list 的反向迭代器的簡單的實現,可能會有一些問題,但是大體的框架就是這樣了。

template<typename T, typename ref, typename ptr>class reverse_list_iterator {public:typedef _list_iterator<T> _list_iterator;//迭代器的構造reverse_list_iterator(_list_iterator it):_it(it){}reverse_list_iterator& operator++(){return --_it;}reverse_list_iterator& operator++(int) //后置++{return _it--;}bool operator!=(reverse_list_iterator& it1)const{return _it!=it1._it;}ref operator*(){list_iterator tmp=_it;--tmp;return *tmp;}const T& operator*()const{list_iterator tmp = _it;--tmp;return *tmp;}ptr operator->(){return _it.operator->();}listnode* pnode;private:_list_iterator _it;};

但是,其實反向迭代器不是針對某一個容器來設計的,他的重點在于復用,能夠用不同類型的正向迭代器,來轉換出相應的反向迭代器。

	template<typename Iterator>

但是這里面需要用到一些牛逼的技術來實現 從 正向迭代器中提取出 ref 和 ptr 用于 * 和 -> 的返回類型。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/40179.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/40179.shtml
英文地址,請注明出處:http://en.pswp.cn/web/40179.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java必知必會-j2ee規范(上)

文章目錄 一、WEB三大規范二、j2ee相關接口(上)1.javax.servlet.Servlet2.javax.servlet.http.HttpServlet3.javax.servlet.ServletRequest4.javax.servlet.http.HttpServletRequest5.javax.servlet.ServletResponse6.javax.servlet.http.HttpServletResponse7. javax.servlet.…

k8s record 20240705

k8s 安全管理 request 是1g&#xff0c;你得不到要求&#xff0c;我就不創建了&#xff0c;這就是準入控制二次校驗 SA就是serviceAccount。 內部是SA和 token, 外部用戶進來就是 .kube/config文件 namespace下的是role&#xff0c;整個集群是 ClusterRole. 動作就是Binding li…

pytest-rerunfailures:優化測試穩定性的失敗重試工具

筆者在執行自動化測試用例時&#xff0c;會發現有時候用例失敗并非代碼問題&#xff0c;而是由于服務正在發版&#xff0c;導致請求失敗&#xff0c;從而降低了自動化用例的穩定性&#xff0c;最后還要花時間定位到底是自身case的原因還是業務邏輯問題&#xff0c;還是其他原因…

大數據面試題之Presto[Trino](3)

目錄 Presto如何處理數據的聚合操作&#xff1f; Presto支持哪些類型的JOIN操作&#xff1f; 如何在Presto中使用子查詢&#xff1f; 解釋Presto中的窗口函數。 Presto中的Page和Block是什么&#xff1f; 描述Presto如何處理列式存儲數據。 ORC和Parquet格式在Presto中的…

適合家居建材企業的CRM系統盤點(2024版)

當前&#xff0c;CRM市場上&#xff0c;國際巨頭的市場優勢正在逐漸減弱&#xff0c;國內CRM企業奮起追趕&#xff0c;呈現出強勁的崛起勢頭。因此&#xff0c;對于家居建材企業來講&#xff0c;在進行CRM選型時&#xff0c;如何選擇一款合適的系統是關乎企業高效發展的重要課題…

探索C嘎嘎的奇妙世界:第十九關---STL(list的模擬實現)

1. 基本框架 首先&#xff0c;我們先從節點的準備工作入手&#xff0c;請看示例&#xff1a; #pragma once #include<iostream> #include<assert.h> using namespace std; //節點 template<class T> struct ListNode {ListNode<T>* _next;Li…

矩陣鍵盤與密碼鎖

目錄 1.矩陣鍵盤介紹?編輯 2.掃描的概念 3.代碼演示&#xff08;讀取矩陣鍵盤鍵碼&#xff09; 4.矩陣鍵盤密碼鎖 1.矩陣鍵盤介紹 為了減少I/O口的占用&#xff0c;通常將按鍵排列成矩陣形式&#xff0c;采用逐行或逐列的 “掃描”&#xff0c;就可以讀出任何位置按鍵的狀態…

免殺筆記 ----> ShellCode Loader !!!

學了那么久的前置知識&#xff0c;終于到了能上線的地方了&#xff01;&#xff01;&#xff01; 不過這里還沒到免殺的部分&#xff0c;距離bypass一眾的殺毒軟件還有很長的路要走&#xff01;&#xff01; 目錄 1.ShellCode 2.ShellCode Loader的概念 3.可讀可寫可…

字符串函數5-9題(30 天 Pandas 挑戰)

字符串函數 1. 相關知識點1.5 字符串的長度條件判斷1.6 apply映射操作1.7 python大小寫轉換1.8 正則表達式匹配2.9 包含字符串查詢 2. 題目2.5 無效的推文2.6 計算特殊獎金2.7 修復表中的名字2.8 查找擁有有效郵箱的用戶2.9 患某種疾病的患者 1. 相關知識點 1.5 字符串的長度條…

代碼隨想錄算法訓練營第四十四天|188.買賣股票的最佳時機IV、309.最佳買賣股票時機含冷凍期、714.買賣股票的最佳時機含手續費

188.買賣股票的最佳時機IV 題目鏈接&#xff1a;188.買賣股票的最佳時機IV 文檔講解&#xff1a;代碼隨想錄 狀態&#xff1a;不會 思路&#xff1a; 在股票買賣1使用一維dp的基礎上&#xff0c;升級成二維的即可。 定義dp[k1][2]&#xff0c;其中 dp[j][0] 表示第j次交易后持…

虛擬ECU:純電動汽車發展下的新選擇

人類文明的進步是一個不斷自我否定、自我超越的過程。21世紀以來&#xff0c;隨著科技進步和經濟社會發展&#xff0c;能源和交通系統已從獨立于自然環境的孤立系統&#xff0c;轉變為與自然、技術、社會深度耦合的復雜系統。為實現可持續發展和應對氣候變化&#xff0c;世界各…

【居家養老實訓室】:無障礙設施建設與評估

本文圍繞居家養老實訓室中的無障礙設施建設與評估展開討論。首先闡述了無障礙設施對于居家養老的重要性&#xff0c;接著詳細介紹了常見的居家養老無障礙設施類型&#xff0c;包括出入口、通道、臥室、衛生間等區域的設施。然后重點探討了無障礙設施的評估方法和標準&#xff0…

【C++航海王:追尋羅杰的編程之路】關聯式容器的底層結構——AVL樹

目錄 1 -> 底層結構 2 -> AVL樹 2.1 -> AVL樹的概念 2.2 -> AVL樹節點的定義 2.3 -> AVL樹的插入 2.4 -> AVL樹的旋轉 2.5 -> AVL樹的驗證 2.6 -> AVL樹的性能 1 -> 底層結構 在上文中對對map/multimap/set/multiset進行了簡單的介紹&…

《簡歷寶典》02 - 如果你是HR,你會優先打開哪份簡歷?

現在的求職環境不必多說&#xff0c;其實我們大家都還是很清楚的。所以&#xff0c;在這個環境下&#xff0c;寫一份優秀的簡歷&#xff0c;目的與作用也不必多說。那么&#xff0c;這一小節呢&#xff0c;我們先從簡歷這份文檔的文檔名開始說起。 目錄 1 你覺得HR們刷簡歷的時…

【深度學習】圖形模型基礎(5):線性回歸模型第二部分:單變量線性回歸模型

1.引言 在統計學與機器學習的廣闊領域中&#xff0c;線性回歸作為一種基礎而強大的預測技術&#xff0c;其核心在于通過輸入變量&#xff08;或稱預測器、自變量&#xff09;來估計輸出變量&#xff08;響應變量、因變量&#xff09;的連續值。本章聚焦于線性回歸的一個基本但…

Spring-@Component和@Configuration的區別

前言 在Spring框架中&#xff0c;Configuration和Component注解都是用于組件掃描和管理Bean的生命周期&#xff0c;但它們有著不同的用途和應用場景 Component 注解 Component是一個通用的 stereotype 注解&#xff0c;表明一個Java類為Spring框架中的一個Bean組件。Spring會自…

【C++】相機標定源碼筆記- 立體視覺相機的校準和圖像矯正類

類主要用于雙目相機的標定和矯正。它包含了讀取和保存相機模型、計算標定參數以及矯正圖像的功能。通過這些功能&#xff0c;可以實現雙目相機的標定和矯正&#xff0c;從而提高雙目相機的精度和穩定性。 公有函數&#xff1a; 構造函數、帶參構造函數、析構函數、讀取雙目相機…

摩斯邀您參加“WAIC 2024世界人工智能大會”

2024世界人工智能大會暨人工智能全球治理高級別會議&#xff08;簡稱“WAIC 2024”&#xff09;將于7月在上海世博中心、世博展覽館舉行&#xff0c;論壇時間為7月4日-6日&#xff0c;展覽時間為7月5日-7日。大會展覽面積超5.2萬平方米&#xff0c;重點圍繞核心技術、智能終端、…

STM32要學到什么程度才算合格?

在開始前剛好我有一些資料&#xff0c;是我根據網友給的問題精心整理了一份「嵌入式的資料從專業入門到高級教程」&#xff0c; 點個關注在評論區回復“888”之后私信回復“888”&#xff0c;全部無償共享給大家&#xff01;&#xff01;&#xff01; STM32 這玩意兒要學到啥…

今天聊聊AI

AI是在幫助開發者還是取代他們&#xff1f; 在軟件開發領域&#xff0c;生成式人工智能&#xff08;AIGC&#xff09;正在改變開發者的工作方式。無論是代碼生成、錯誤檢測還是自動化測試&#xff0c;AI工具正在成為開發者的得力助手。然而&#xff0c;這也引發了對開發者職業…