合並 k 個排序鏈表,返回合並后的排序鏈表。請分析和描述算法的復雜度。
示例:
輸入:
[
1->4->5,
1->3->4,
2->6
]
輸出: 1->1->2->3->4->4->5->6
/* 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、引用和指針結合時需要特別小心。