合并 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();
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[]={,,,,,,,,,,,};
number1 num1[]={,,,,,,,,,,,};
number2 num2[]={,,,,,,,,,,,}; 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=;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=;num1[i].x;i++)
que5.push(num1[i]);
for(i=;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 ;
}
/*
运行结果 :
采用默认优先关系:
(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[] = { , , , , , };
  vector<int> testdata;
  testdata.insert(testdata.begin(), data, data + );
// 排序算法
  sort(testdata.begin(), testdata.end(), compare); // 升序
  return ;
}
//2  lambda表达式的解法:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std; int main(void)
{
  int data[] = { , , , , , };
  vector<int> testdata;
  testdata.insert(testdata.begin(), data, data + );
  sort(testdata.begin(), testdata.end(), [](int a, int b){ return a > b; });
  return ;
}

场景二  

  使用auto来接收一个lambda表达式,当然我们也可以直接使用C++11里面的新特性function来接收lambda表达式,两者等价的,因为auto是自动类型转换,所以在某些场合使用起来更方便。

#include <iostream>
#include <functional>
using namespace std;
int main(void)
{
  int x = , y = ;
  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 ;
}
//最终的运行结果都是: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() << endl;
  cout << "recursion(3):" << recursion() << endl;
  cout << "recursion(4):" << recursion() << endl;
  return ;
}

//运行结果:

//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

C++—lambda表达式+优先队列 prority_queue+关键字decltype的更多相关文章

  1. C++ 11 Lambda表达式、auto、function、bind、final、override

    接触了cocos2dx 3.0,就必须得看C++ 11了.有分享过帖子:[转帖]漫话C++0x(四) —- function, bind和lambda.其实最后的Lambda没太怎么看懂. 看不懂没关 ...

  2. Java 8特性探究(1):通往lambda之路与 lambda表达式10个示例

    本文由 ImportNew 函数式接口 函数式接口(functional interface 也叫功能性接口,其实是同一个东西).简单来说,函数式接口是只包含一个方法的接口.比如Java标准库中的ja ...

  3. Java 8 Lambda表达式10个示例【存】

    PS:不能完全参考文章的代码,请参考这个文件http://files.cnblogs.com/files/AIThink/Test01.zip 在Java 8之前,如果想将行为传入函数,仅有的选择就是 ...

  4. 【Java学习笔记之三十一】详解Java8 lambda表达式

    Java 8 发布日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表达式,它将允许我们将行为传到函数里.在Java 8之前 ...

  5. Java8新特性-Lambda表达式是什么?

    目录 前言 匿名内部类 函数式接口 和 Lambda表达式语法 实现函数式接口并使用Lambda表达式: 所以Lambda表达式是什么? 实战应用 总结 前言 Java8新特性-Lambda表达式,好 ...

  6. jdk8 lambda表达式总结

    Java8 lambda表达式10个示例   1. 实现Runnable线程案例 使用() -> {} 替代匿名类: //Before Java 8: new Thread(new Runnab ...

  7. Java 8 lambda表达式示例

    例1.用lambda表达式实现Runnable 我开始使用Java 8时,首先做的就是使用lambda表达式替换匿名类,而实现Runnable接口是匿名类的最好示例.看一下Java 8之前的runna ...

  8. java8 快速入门 lambda表达式 Java8 lambda表达式10个示例

    本文由 ImportNew - lemeilleur 翻译自 javarevisited.欢迎加入翻译小组.转载请见文末要求. Java 8 刚于几周前发布,日期是2014年3月18日,这次开创性的发 ...

  9. Java8 lambda表达式10个示例

    Java 8 刚于几周前发布,日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表达式,它将允许我们将行为传到函数里.在Ja ...

随机推荐

  1. BZOJ 2600: [Ioi2011]ricehub 双指针+贪心

    不难发现,当我们要选的区间确定后,一定会把仓库安排到中间的稻草上(如果是偶数个的话中间两个都行). 然后按照坐标从小到大枚举右指针,左指针一定不递减,双指针扫一下就行了. code: #include ...

  2. H5实现横向滚动的方法总结

    小程序中有横向滚动的swiper,H5中目前得手动实现. 实现方法如下: 外层需要设置: overflow: scroll;white-space: nowrap; 内层需要设置: display: ...

  3. singer tap-minio-csv 使用

    使用tap-minio-csv 我们可以将s3 中csv 的文件,通过singer 的target 写到不用的系统中,可以兼容 s3 的存储类型,以下是一个集成minio 的测试,将minio 中的c ...

  4. JDBC-select练习&jdbc工具类&数据库登录案例

    一.select练习 1.说明 练习: * 定义一个方法,查询emp表的数据将其封装为对象,然后装载集合,返回. 1. 定义Emp类 2. 定义方法 public List<Emp> fi ...

  5. (持续更新) C# 面试技术点、常见SQL技术点 和 解决高并发的相关技术

    这篇博客 持续更新. 方便小伙伴们学习与面试前的复习

  6. 如何保证MQ的顺序性?比如Kafka

    三.如何保证消息的顺序性 1. rabbitmq 拆分多个queue,每个queue一个consumer,就是多一些queue而已,确实是麻烦点:或者就一个queue但是对应一个consumer,然后 ...

  7. 工具系列 | 使用FormData方式上传文件

    服务端代码 /** * 文件上传 */ public function uploadFile() { Log::error('文件上传 : '.json_encode($_FILES)); $dir ...

  8. Oracle 如何恢复删除并提交的表数据

    在Oracle的数据库中,如果不小心删除数据,该如何恢复数据呢? 有两种方法 :scn 方法和时间戳方法 一.恢复删除数据的SQL语法(建议用时间戳) 1.通过scn恢复删除且已提交的数据 1)获得当 ...

  9. linux命令详解之du命令

    du命令概述du命令作用是估计文件系统的磁盘已使用量,常用于查看文件或目录所占磁盘容量.du命令与df命令不同,df命令是统计磁盘使用情况,详见linux命令详解之df命令.du命令会直接到文件系统内 ...

  10. Navicat连接MySQL8.0出现1251-Client does not support authentication protocol requested by server;

    因为安装的MySQL是8.0版本的,因为在安装的时候采用了新的加密方式. 我们需要使用 cmd命令,连接mysql 1.   更改加密方式 mysql> ALTER USER 'root'@'l ...