合并 k 個排序鏈表,回傳合并后的排序鏈表,請分析和描述演算法的復雜度,
示例:
輸入:
[
1->4->5,
1->3->4,
2->6
]
輸出: 1->1->2->3->4->4->5->6鏈接:https://leetcode-cn.com/problems/merge-k-sorted-lists
/* struct ListNode{ int val; ListNode *next; ListNode(int x) : val(x),next(NULL) {} }; */ class Solution { //優先佇列解題 public: ListNode *mergeKLists(vector<ListNode*>& lists) { ListNode dummy(0); ListNode *tail = &dummy; auto comp=[](ListNode *a, ListNode *b) {return a->vl > b->val;}; priority_queue<ListNode*, vector<ListNode*>, decltype(comp)> q(comp); for(ListNode* list : lists) if(list)q.push(list); while(!q.empty()) { tail->next = q.top(); q.pop(); tail = tail->next; if(tail->next) q.push(tail->next); } return dummy.next; } };
優先佇列 — priority_queue
一、相關定義
優先佇列容器與佇列一樣,只能從隊尾插入元素,從隊首洗掉元素,但是它有一個特性,就是佇列中最大的元素總是位于隊首,所以出隊時,并非按照先進先出的原則進行,而是將當前佇列中最大的元素出隊,這點類似于給佇列里的元素進行了由大到小的順序排序,元素的比較規則默認按元素值由大到小排序,可以多載“<”運算子來重新定義比較規則,
優先級佇列可以用向量(vector)或雙向佇列(deque)來實作(注意list container不能用來實作queue,因為list的迭代器不是任意存取iterator,而pop中用到堆排序時是要求random access iterator 的!):
priority_queue<vector<int>, less<int> > pq1; // 使用遞增 less<int> 函式物件排序
priority_queue<deque<int>, greater<int> > pq2; // 使用遞減 greater<int> 函式物件排序
其成員函式有“判空(empty)” 、“尺寸(Size)” 、“堆疊頂元素(top)” 、“壓堆疊(push)” 、“彈堆疊(pop)”等,
二、基本操作
empty() 如果佇列為空,則回傳真
pop() 洗掉對頂元素,洗掉第一個元素
push() 加入一個元素
size() 回傳優先佇列中擁有的元素個數
top() 回傳優先佇列對頂元素,回傳優先佇列中有最高優先級的元素
在默認的優先佇列中,優先級高的先出隊,在默認的int型中先出隊的為較大的數,
頭檔案: #include <queue>
宣告方式:
1、普通方法
priority_queue<int> q; //通過操作,按照元素從大到小的順序出隊 priority_queue<int,vector<int>, greater<int> > q; //通過操作,按照元素從小到大的順序出隊
2、自定義優先級
struct cmp { operator bool ()(int x, int y) { return x > y; // x小的優先級高 //也可以寫成其他方式,如: return p[x] > p[y];表示p[i]小的優先級高 } }; priority_queue<int, vector<int>, cmp> q; //定義方法 //其中,第二個引數為容器型別,第三個引數為比較函式,
3、結構體宣告方式
struct node { int x, y; friend bool operator < (node a, node b) { return a.x > b.x; //結構體中,x小的優先級高 } }; priority_queue<node>q; //定義方法 //在該結構中,y為值, x為優先級, //通過自定義operator<運算子來比較元素中的優先級, //在多載”<”時,最好不要多載”>”,可能會發生編譯錯誤
三、代碼實作
優先佇列,其構造及具體實作我們可以先不用深究,我們現在只需要了解其特性,及在做題中的用法,
以一個例子來解釋吧(呃,寫完才發現,這個代碼包函了幾乎所有我們要用到的用法,仔細看看吧):
/*優先佇列的基本使用*/ #include<stdio.h> #include<functional> #include<queue> #include<vector> using namespace std; //定義結構,使用運算子多載,自定義優先級1 struct cmp1{ bool operator ()(int &a,int &b){ return a>b;//最小值優先 } }; struct cmp2{ bool operator ()(int &a,int &b){ return a<b;//最大值優先 } }; //定義結構,使用運算子多載,自定義優先級2 struct number1{ int x; bool operator < (const number1 &a) const { return x>a.x;//最小值優先 } }; struct number2{ int x; bool operator < (const number2 &a) const { return x<a.x;//最大值優先 } }; int a[]={14,10,56,7,83,22,36,91,3,47,72,0}; number1 num1[]={14,10,56,7,83,22,36,91,3,47,72,0}; number2 num2[]={14,10,56,7,83,22,36,91,3,47,72,0}; int main() { priority_queue<int>que;//采用默認優先級構造佇列 priority_queue<int,vector<int>,cmp1>que1;//最小值優先 priority_queue<int,vector<int>,cmp2>que2;//最大值優先 priority_queue<int,vector<int>,greater<int> >que3;//注意“>>”會被認為錯誤, //這是右移運算子,所以這里用空格號隔開 priority_queue<int,vector<int>,less<int> >que4;////最大值優先 priority_queue<number1>que5; priority_queue<number2>que6; int i; for(i=0;a[i];i++){ que.push(a[i]); que1.push(a[i]); que2.push(a[i]); que3.push(a[i]); que4.push(a[i]); } for(i=0;num1[i].x;i++) que5.push(num1[i]); for(i=0;num2[i].x;i++) que6.push(num2[i]); printf("采用默認優先關系:\n(priority_queue<int>que;)\n"); printf("Queue 0:\n"); while(!que.empty()){ printf("%3d",que.top()); que.pop(); } puts(""); puts(""); printf("采用結構體自定義優先級方式一:\n(priority_queue<int,vector<int>,cmp>que;)\n"); printf("Queue 1:\n"); while(!que1.empty()){ printf("%3d",que1.top()); que1.pop(); } puts(""); printf("Queue 2:\n"); while(!que2.empty()){ printf("%3d",que2.top()); que2.pop(); } puts(""); puts(""); printf("采用頭檔案\"functional\"內定義優先級:\n(priority_queue<int,vector<int>,greater<int>/less<int> >que;)\n"); printf("Queue 3:\n"); while(!que3.empty()){ printf("%3d",que3.top()); que3.pop(); } puts(""); printf("Queue 4:\n"); while(!que4.empty()){ printf("%3d",que4.top()); que4.pop(); } puts(""); puts(""); printf("采用結構體自定義優先級方式二:\n(priority_queue<number>que)\n"); printf("Queue 5:\n"); while(!que5.empty()){ printf("%3d",que5.top()); que5.pop(); } puts(""); printf("Queue 6:\n"); while(!que6.empty()){ printf("%3d",que6.top()); que6.pop(); } puts(""); return 0; } /* 運行結果 : 采用默認優先關系: (priority_queue<int>que;) Queue 0: 83 72 56 47 36 22 14 10 7 3 采用結構體自定義優先級方式一: (priority_queue<int,vector<int>,cmp>que;) Queue 1: 7 10 14 22 36 47 56 72 83 91 Queue 2: 83 72 56 47 36 22 14 10 7 3 采用頭檔案"functional"內定義優先級: (priority_queue<int,vector<int>,greater<int>/less<int> >que;) Queue 3: 7 10 14 22 36 47 56 72 83 91 Queue 4: 83 72 56 47 36 22 14 10 7 3 采用結構體自定義優先級方式二: (priority_queue<number>que) Queue 5: 7 10 14 22 36 47 56 72 83 91 Queue 6: 83 72 56 47 36 22 14 10 7 3 */
lambda運算式(C++11)
使用場景
1. lambda運算式又叫匿名函式(可以理解為一個未命名的行內函式),那么肯定就跟函式掛上關系了,通常情況寫你在編程的時候需要將這段代碼封裝到一個函式里面再來呼叫,那這個時候就避免不了取函式名了,那么這個時候你就要想起我們的lambda運算式了,它可以很好的幫你解決函式命名困難這個問題,
2. 在你的整個專案編程中,你獨立出來一個函式,但這個函式實作相對簡單并且可能在整個專案只使用了一次(即不存在復用的情況),那么這個時候我們就可以考慮使用下lambda運算式了,這樣可以讓代碼更加緊湊,更加容易維護,
簡單應用
先看看lambda運算式變數截取的方式:
[] 不截取任何變數
[&] 截取外部作用域中所有變數,并作為參考在函式體中使用
[=] 截取外部作用域中所有變數,并拷貝一份在函式體中使用
[=, &foo] 截取外部作用域中所有變數,并拷貝一份在函式體中使用,但是對foo變數使用參考
[bar] 截取bar變數并且拷貝一份在函式體重使用,同時不截取其他變數
[this] 截取當前類中的this指標,如果已經使用了&或者=就默認添加此選項,
場景一
比較兩個數的大小,第一個數比第二個數大的時候回傳true,反之回傳false,
// 1 傳統解法
#include <iostream> #include <vector> #include <algorithm> using namespace std; bool compare(int& a, int& b) { return a > b; } int main(void) { int data[6] = { 3, 4, 12, 2, 1, 6 }; vector<int> testdata; testdata.insert(testdata.begin(), data, data + 6); // 排序演算法 sort(testdata.begin(), testdata.end(), compare); // 升序 return 0; }
//2 lambda運算式的解法: #include <iostream> #include <vector> #include <algorithm> using namespace std; int main(void) { int data[6] = { 3, 4, 12, 2, 1, 6 }; vector<int> testdata; testdata.insert(testdata.begin(), data, data + 6); sort(testdata.begin(), testdata.end(), [](int a, int b){ return a > b; }); return 0; }
場景二
使用auto來接收一個lambda運算式,當然我們也可以直接使用C++11里面的新特性function來接收lambda運算式,兩者等價的,因為auto是自動型別轉換,所以在某些場合使用起來更方便,
#include <iostream> #include <functional> using namespace std; int main(void) { int x = 8, y = 9; auto add = [](int a, int b) { return a + b; }; std::function<int(int, int)> Add = [=](int a, int b) { return a + b; }; cout << "add: " << add(x, y) << endl; cout << "Add: " << Add(x, y) << endl; return 0; } //最終的運行結果都是:17
//決議: function中的第一個int是回傳值型別,括號里面的兩個int都是函式的引數型別.
場景三
使用lambda運算式來實作遞回演算法
遞回題目:已知f(1)=1,f(2)=2,那么請實作f(n)=f(n-1)+f(n-2),此處的n>2
#include <iostream> #include <functional> using namespace std; int main() { std::function<int(int)> recursion = [&recursion](int n) { return n < 2 ? 1 : recursion(n - 1) + recursion(n - 2); }; cout << "recursion(2):" << recursion(2) << endl; cout << "recursion(3):" << recursion(3) << endl; cout << "recursion(4):" << recursion(4) << endl; return 0; }
//運行結果:
//recursion(2):2
//recursion(3):3
//recursion(4):5
鏈接:https://blog.csdn.net/qq_34199383/article/details/80469780
decltype關鍵字(C++11)
一、decltype意義
有時我們希望從運算式的型別推斷出要定義的變數型別,但是不想用該運算式的值初始化變數(如果要初始化就用auto了),為了滿足這一需求,C++11新標準引入了decltype型別說明符,它的作用是選擇并回傳運算元的資料型別,在此程序中,編譯器分析運算式并得到它的型別,卻不實際計算運算式的值,
二、decltype用法
1.基本用法
int getSize();
int main(void)
{
int tempA = 2;
/*1.dclTempA為int*/
decltype(tempA) dclTempA;
/*2.dclTempB為int,對于getSize根本沒有定義,但是程式依舊正常,因為decltype只做分析,并不呼叫getSize,*/
decltype(getSize()) dclTempB;
return 0;
}
2.與const結合
double tempA = 3.0;
const double ctempA = 5.0;
const double ctempB = 6.0;
const double *const cptrTempA = &ctempA;
/*1.dclTempA推斷為const double(保留頂層const,此處與auto不同)*/
decltype(ctempA) dclTempA = 4.1;
/*2.dclTempA為const double,不能對其賦值,編譯不過*/
dclTempA = 5;
/*3.dclTempB推斷為const double * const*/
decltype(cptrTempA) dclTempB = &ctempA;
/*4.輸出為4(32位計算機)和5*/
cout<<sizeof(dclTempB)<<" "<<*dclTempB<<endl;
/*5.保留頂層const,不能修改指標指向的物件,編譯不過*/
dclTempB = &ctempB;
/*6.保留底層const,不能修改指標指向的物件的值,編譯不過*/
*dclTempB = 7.0;
3.與參考結合
int tempA = 0, &refTempA = tempA;
/*1.dclTempA為參考,系結到tempA*/
decltype(refTempA) dclTempA = tempA;
/*2.dclTempB為參考,必須系結到變數,編譯不過*/
decltype(refTempA) dclTempB = 0;
/*3.dclTempC為參考,必須初始化,編譯不過*/
decltype(refTempA) dclTempC;
/*4.雙層括號表示參考,dclTempD為參考,系結到tempA*/
decltype((tempA)) dclTempD = tempA;
const int ctempA = 1, &crefTempA = ctempA;
/*5.dclTempE為常量參考,可以系結到普通變數tempA*/
decltype(crefTempA) dclTempE = tempA;
/*6.dclTempF為常量參考,可以系結到常量ctempA*/
decltype(crefTempA) dclTempF = ctempA;
/*7.dclTempG為常量參考,系結到一個臨時變數*/
decltype(crefTempA) dclTempG = 0;
/*8.dclTempH為常量參考,必須初始化,編譯不過*/
decltype(crefTempA) dclTempH;
/*9.雙層括號表示參考,dclTempI為常量參考,可以系結到普通變數tempA*/
decltype((ctempA)) dclTempI = ctempA;
4.與指標結合
int tempA = 2;
int *ptrTempA = &tempA;
/*1.常規使用dclTempA為一個int *的指標*/
decltype(ptrTempA) dclTempA;
/*2.需要特別注意,運算式內容為解參考操作,dclTempB為一個參考,參考必須初始化,故編譯不過*/
decltype(*ptrTempA) dclTempB;
三、decltype總結
decltype和auto都可以用來推斷型別,但是二者有幾處明顯的差異:
1.auto忽略頂層const,decltype保留頂層const;
2.對參考操作,auto推斷出原有型別,decltype推斷出參考;
3.對解參考操作,auto推斷出原有型別,decltype推斷出參考;
4.auto推斷時會實際執行,decltype不會執行,只做分析,
總之在使用中程序中和const、參考和指標結合時需要特別小心,
連接:https://www.cnblogs.com/cauchy007/p/4966485.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/102075.html
標籤:C++
上一篇:C++:記憶體管理
下一篇:c++ 右值參考
