目录
一、前言
以前不管是在学校还是项目/笔试上都看过C++ primer,但是没有总结,所以隔很长时间后想用都会重新回顾,效率很低,所以打算整理一些常用知识点。
C++/Python:一切皆对象,当你将C语言内置数据类型看做自定义类型时,那么容器类可以放任何东西。
二、C++ Primer知识回顾
2.1 C++基础
- 如果想声明一个全局变量而非定义,使用关键字extern,而且不要显示地初始化变量:extern int i;//声明, int j;//声明并定义
- const修饰常量,想改变使用volatile, volatile const int a = 7; const修饰指针,左定值const int * a;,右定向int *const a;const修饰传参:值传递用不着,指针传递不改变,引用传递不改变const Test & t;一般类参数传递使用引用方式;const 修饰类成员函数get_cm()const,其目的是防止成员函数修改被调用对象的值,如果我们不想修改一个调用对象的值,所有的成员函数都应当声明为const成员函数。注意:const关键字不能与static关键字同时使用,因为static关键字修饰静态成员函数,静态成员函数不含有this指针,即不能实例化,const成员函数必须具体到某一实例
- auto 类型,让编译器操心,通过初始值来推断变量的类型;vector<int >v={1,2,3};for(auto &r:v) //对V中每个元素遍历,r是引用可读写v,也可以采用for(auto beg=v.begin();beg!=v.end();beg++)
- 标准库string:#include <string> using std::string;
//初始化
string s1;
string s2(s1);
string s2=s1;
string s3("value")//构造函数,多态
string s4="value";
string s5(n,'c');
//操作
s.empty()
s.size()
s[n]//返回第n个字符引用
string s6=s4+s5; //对象相加
for(auto c: str)
{
isalnum(c)//c是字母或数字为真
isalpha(c)//c是字母
is......//小写字母,大写字母,数字,十六进制
}
string line;
while (getline(cin, line))//getline()读取一整行
{
cout << line <<endl;
}
- vector表示对象的集合,是一种数据容器(动态数组),是最常用类;
#include <vector>
using std::vector;
//定义和初始化
vector<T> v1;
vector<T> v2=v1;
vector<T> v2(v1);
vector<T> v3(n,val);//有n个元素,都是val
vector<T> v4(n);//有n个元素
vector<T> v5{a,b,c...};
vector<T> v5={a,b,c...};
vector<T> v4{n};//有一个元素,是n
vector<T> v4{n,val};//有两个元素是n,val
//操作
v.empty()
v.size()
v.push_back()//尾部追加元素
v.clear();
v[n]//访问元素,只读
for(auto &i: v)
cout<<i<<endl;
std::vector<int> v3{1, 2, 2, 3, 2, 2, 5};
std::vector<int>::iterator it;
for(it = v3.begin(); it != v3.end();)
{
if(*it == 2)
it = v3.erase(it); //删除元素,返回值指向已删除元素的下一个位置
else
++it; //指向下一个位置
}
- 迭代器:拥有迭代器的类型同时拥有返回迭代器的成员,一般是begin和end成员,begin是指向第一个元素的迭代器,end指向容器的尾元素的下一个位置的迭代器,通过对迭代器解引用可以获取它指示的元素。
//迭代器类型
vector<int>::iterator itvec;
string::iterator itstr; //读写
vector<int>::iterconst_ator itvec;
string::const_iterator itstr; //只读
//使用
for(auto it=s.begin();it!=s.end && !isspace(*it);it++)
*it=toupper(*it);
- 数组指针和指针数组: 记住[]的优先级大于*, int *p[10], []与p先结合,int*是整形指针类型。int (*p)[10] , int类型的数组,由一个指针p表示。
- 指针函数和函数指针:指针函数是一个返回指针的函数,其本质是一个函数。函数指针是一个指针变量指向一个函数的入口地址。
- lambda表达式:记住中括号小括号大括号就行了[](){}。[capture标识符](parameters参数) mutable->return type{ statements }
[]lambda表达式的开始,[]为空没有任何函数对象参数,“=”函数体内可以使用lambda表达式所在作用范围内所有可见的局部变量(值传递);“&”按引用传递,“this”函数体可以使用lambda所在类中的成员变量。[],[names],[&],[=],[=,a,b] ,[&,a,b]
()操作符重载函数参数,无参数的时候可省略,参数可按照值(a,b),按照引用传递(&a,&b);调用一个lambda表达式时给定的实参会被用来初始化lambda表达式的形参,因此,实参和形参的类型必须匹配,与普通函数不同,lambda表达式不能有默认的参数。
auto add = [](int a, int b)->int{
return a + b;
};
cout << "a+b: " << (add(5,6) )<< endl;
可修改标识符mutable,可省略,按值传递函数对象加上mutable可以修改传递进来的拷贝;[m]()mutable{m=100;};
函数返回值,当函数返回值为void,或者只有一个return的地方时,这部分可以省略。
- 关联容器:map和set。map中的元素是一些关键字-值(key-value)对:关键字起到索引的作用,值表示与索引相关联的数据; set中每个元素只包含一个关键字,set支持高校的关键字查询操作——检查一个给定的关键字是否在set中
//map 基本用法C++ Maps是一种关联式容器,包含“关键字/值”对
begin() 返回指向map头部的迭代器
clear() 删除所有元素
count("ch") 返回指定元素出现的次数
empty() 如果map为空则返回true
end() 返回指向map末尾的迭代器
equal_range() 返回特殊条目的迭代器对
erase() 删除一个元素
find() 查找一个元素
get_allocator() 返回map的配置器
insert() 插入元素
key_comp() 返回比较元素key的函数
lower_bound() 返回键值>=给定元素的第一个位置
max_size() 返回可以容纳的最大元素个数
rbegin() 返回一个指向map尾部的逆向迭代器
rend() 返回一个指向map头部的逆向迭代器
size() 返回map中元素的个数
swap() 交换两个map
upper_bound() 返回键值>给定元素的第一个位置
value_comp() 返回比较元素value的函数
/*================初始化================*/
//直接赋值
map<string, int> m1;
m1["def"] = 2;
//插入
m2.insert({ "abc", 1 });
m2.insert(make_pair(string("def"), 2));
m2.insert(pair<string, int>(string("ghi"), 3));
//初始化列表
map<string,int> m3 = {{"string",1}, {"sec",2}, {"trd",3}};
//====================================
//取值,at, 或者下标[]
ID_Name.at(2016) = "Bob";
ID_Name[2016];
map的底层原理,是通过红黑树(一种非严格意义上的平衡二叉树)来实现的,因此map内部所有的数据都是有序的,map的查询、插入、删除操作的时间复杂度都是O(logn)
unordered_map和map类似,都是存储的key-value的值,可以通过key快速索引到value。不同的是unordered_map不会根据key的大小进行排序,存储时是根据key的hash值判断元素是否相同,即unordered_map内部元素是无序的(哈希组织的);
map按照建Key排序:map<string, int, greater<string> > name_score_map;第三个参数默认是less,即升序,使用greater为降序;
map按照value排序:STL中sort算法可以排序,但是只能对序列容器进行排序,就是线性的(如vector,list,deque),所以把map中的元素通过pair放到序列容器(如vector)中,然后再对这些元素进行排序。
pair类重载了<符,但是它并不是按照value进行比较的,而是先对key进行比较,key相等时候才对value进行比较。显然不能满足我们按value进行排序的要求。
typedef pair<string, int> PAIR;
bool cmp_by_value(const PAIR& lhs, const PAIR& rhs) {
return lhs.second < rhs.second;
}
struct CmpByValue {
bool operator()(const PAIR& lhs, const PAIR& rhs) {
return lhs.second < rhs.second;
}
};
int main() {
map<string, int> name_score_map;
name_score_map["LiMin"] = 90;
name_score_map["ZiLinMi"] = 79;
name_score_map["BoB"] = 92;
name_score_map.insert(make_pair("Bing",99));
name_score_map.insert(make_pair("Albert",86));
//把map中元素转存到vector中
vector<PAIR> name_score_vec(name_score_map.begin(), name_score_map.end());
sort(name_score_vec.begin(), name_score_vec.end(), CmpByValue());
// sort(name_score_vec.begin(), name_score_vec.end(), cmp_by_value);
for (int i = 0; i != name_score_vec.size(); ++i) {
cout << name_score_vec[i] << endl;
}
return 0;
2.2C++顺序容器
(1)除了固定大小的array外,其他容器都提供高效、灵活的内存管理,可以添加和删除元素,扩张和收缩容器大小。
string | 与vector相似的容器,专门用于保存字符,随机快速访问,在尾部插入和删除快 |
数组 | 静态数组=array,固定大小数组,支持快速随机访问,不能添加或者删除元素 |
动态数组=vector, 可变大小数组,支持随机访问。在尾部之外的位置插入和删除元素会很慢insert, erase | |
链表 | 单链表= forward_list,单向链表。支持单向顺序访问,在链表任何位置进行插入和删除速度都很快,额外内存开销较vector和deque,array大,没有size()操作 |
双链表=list,双向链表,支持双向顺序访问,在链表任何位置进行插入和删除速度都很快 | |
队列 | 单向队列=queue(默认deque);支持头部插入,尾部删除 queue支持push_back、pop_front |
单向数组队列=queue<类型,vector<类型>> | |
单向链式队列=queue<类型,list<类型>> | |
双向队列=deque 双端队列,支持快速随机访问,在头部位置插入和删除速度都很快;在中间位置添加或者删除元素代价大。 deque支持push_front、pop_front、push_back、pop_back | |
优先队列=priority_queue(默认vector), | |
堆 | meak_heap,push_heap,pop_heap,sort_heap |
数组:一种最基本的数据结构,它是内存上的一块连续存储空间。正因如此数组的随机访问很方便。但数组也有其固有的限制,大小分配后不能改变
链表:一种线性表(有n个元素组成的有限序列),链表是一种基础的数据结构,通常有一连串的节点组成。节点中存放数据和指向下一节点的指针。因为链表不是按线性的顺序存储结构,其查询某节点的时间是,插入操作。链表分为单链表,双链表和循环链表
栈: 一种后进先出的数据结构,可以用数组也可用链表实现。链表的实现形式更接近于栈的抽象概念,因为链表的节点数与栈中元素数目相同,而在数组实现形式中,数组的容量常常超过其尺寸。栈的直接应用包括函数调用,网页浏览记录,编辑器中的重做。栈也是其它数据结构和算法的基本组件。
STL中的stack是一种容器适配器,就是用其他容器最为底层实现,将其他容器转化为栈。Stack封装了入栈,出栈,取栈顶元素,查看大小和是否为空操作。默认情况下,stack用deque做底层容器。也可以将其修改为vector,list。
std::stack<int> deque_stack;
std::stack<int, std::vector<int>> vec_stack;
std::stack<int, std::list<int>> list_stack;
队列: 一种先进先出的数据结构,可以用数组也可以用链表实现队列。队列可用访问共享资源,排队购物。还用来构成其他更复杂的数据结构
STL中的queue是一种容器适配器,默认的底层实现容器是deque。这一点和stack很像,通过关闭或者限制deque的一些接口可以很轻松的实现stack和queue。Priority_queue优先级队列是一个拥有权值概念的单向队列queue,在STL的具体实现中也是以别的容器为底层数据结构,在利用堆的规则调整元素之间的位置。默认的底层实现是vector。这与queue的默认底层实现deque差别很大。
优先队列#include <queue>:
定义:priority_queue<Type, Container, Functional>
Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector),Functional 就是比较的方式,当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆
含义:它允许用户为队列中元素设置优先级,放置元素的时候不是直接放到队尾,而是放置到比它优先级低的元素前面,标准库默认使用<
操作符来确定优先级关系
//优先队列基本操作
top 访问队头元素
empty 队列是否为空
size 返回队列内元素个数
push 插入元素到队尾 (并排序)
emplace 原地构造一个元素并插入队列
pop 弹出队头元素
swap 交换内容
大顶堆greater和小顶堆less:
- 大顶和小顶表示堆的顶部是最大值还是最小值(父节点)
-
两个函数的头文件是< functional >
-
优先队列建堆的时候,默认是大根堆(less),第三个参数用greater会变成小根堆;
-
sort排序的时候,默认是从小到大,但是第三个参数用greater会变成从大到小
为什么less在优先级队列中反而是大顶堆?
C++优先队列是优先级高的在队首,定义优先级大小的方式是第三个参数;
less<int> :
小于号<
规定了优先级,表示优先队列后面的元素要小于优先队列前面的元素,因为优先队列队首的元素优先级最高,优先队列队尾元素的优先级最低,所以小于号<
就规定了优先队列后面的元素都要小于优先队列前面的元素(尾部优先级小于首部优先级),也就是形成一个大根堆,降序排序,每次权值最大的会被弹出来。greater<int>:
大于号>
规定了优先级,表示优先队列后面的元素要大于优先队列前面的元素,因为优先队列队首的元素优先级最高,优先队列队尾元素的优先级最低,所以大于号>
就规定了优先队列后面的元素都要大于优先队列前面的元素(尾部优先级小于首部优先级),也就是形成一个小根堆,升序排序,每次权值最小的会被弹出来
堆:STL中关于堆的操作,建堆make_heap(),加数据push_heap(),删数据pop_heap(),堆排序sort_heap()。头文件<algorithm>。
使用原则:
1、如果你需要高效的随即存取,而不在乎插入和删除的效率,使用vector
2、如果你需要大量的插入和删除,而不关心随即存取,则应使用list
3、如果你需要随即存取,而且关心两端数据的插入和删除,则应使用deque
(2)堆和栈:
-
栈具有数据结构中栈的特点,后进先出,所有存放在它里面的数据都是生命周期很明确(当然要求它不能存放太久,占有的空间确定而且占用空间小),能够快速反应的
-
堆可以理解它就是个一个可大可小,任你分配的听话的内存操作单元;因此它的特点就是动态的分配内存,适合存放大的数据量!比如一个对象的所有信息,虽然它的引用指向栈中的某个引用变量;
(3)总结:栈(先进后出)和队列(先进先出)是两种数据结构,可以用数组也可以用链表实现。
数组与链表是更加偏向数据存储方式的概念,数组在连续的空间中存储数据,随机读取效率高,但是数据添加删除的效率较低;
链表可以在非连续的空间中存储数据,随机访问效率低,数据添加删除效率高。
队列和栈是描述数据存取方式的概念,队列是先进先出,而堆栈是后进先出;队列和栈都可以使用数组或者链表实现。
#include<queue>// 队列
#include<stack>//栈
//定义
stack<int> s;//参数也是数据类型,这是栈的定义方式
queue<int> q; //参数是数据类型,这是队列的定义方式
//栈常用
s.empty()//如果栈为空返回true,否则返回false
s.size()//返回栈中元素的个数
s.pop()//删除栈顶元素但不返回其值
s.top()//返回栈顶的元素,但不删除该元素
s.push(X)//在栈顶压入新元素 ,参数X为要压入的元素
s.clear()
//队列常用
q.empty()// 如果队列为空返回true,否则返回false
q.size() // 返回队列中元素的个数
q.pop() //删除 队首 元素但不返回其值
q.push(X) //在 队尾 压入新元素 ,X为要压入的元素
q.front() // 返回 队首 元素的值,但不删除该元素
q.back() //返回 队尾 元素的值,但不删除该元素
q.clear()
q.rear()//指针指的直接是队尾元素
(4)选择容器原则:
- 除非你有很好的理由选择其他容器,否则应使用vector
- 如果你的程序有很多小的元素,且空间的额外开销很重要(),则不要使用list或者forward_list。
- 如果要求随机访问,使用vector或者deque
- 如果要求在容器中间插入或者删除元素,应使用list或者forward_list
- 如果程序要求在头尾位置插入或者 删除元素,不会在中间操作,应该使用deque
- 如果在读取输入时需要在容器中间位置插入元素,随后需要随机访问,那么先用list,然后将list内容拷贝到vector中
三、let's coding
2.1 无重复字符的最长子串
思路:建立hash表记录字符出现的位置,维护一个向右的滑动窗口,如果遇见不重复的就计算最大值,如果遇见重复的更新左边界值。
#include <string>
#include <iostream>
#include <algorithm>
class Solution {
public:
int lengthOfLongestSubstring(std::string s)
{
int m[256];//ASCII码数目
int left=0;//最左边
int ret=0;
for(int i=0; i< s.size(),i++)
{ /*如果没有遇见重复的,或者遇到left之前位置的字符*/
if(m[s[i]]==0 || left>m[s[i]])
{
ret = std::max(res,i-left+1);
}
else
{
left=m[s[i]];//遇见重复的更新left值
}
m[s[i]]=i+1; //保存字符所在位置
}
}
};
int main()
{
std::string strfind("abbca");
Solution slu;
std::cout << slu.lengthOfLongestSubstring(strfind) << std::endl;
return 0;
}
2.2 删除链表的倒数第 N 个结点
思路:解决链表和数组问题灵活使用双指针
方法1:预先指针指向头结点是为了方便定位链表,头结点既是第一个结点,也代表了整个链表;定义前指针start.后指针end,使它们间隔为n,执行删除操作
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n)
{
ListNode* pre = new ListNode(0);
pre->next = head;
ListNode* start = pre;
ListNode* end = pre;
//start go to n
while(n!=0)
{
start =start->next;
n--;
}
//find num n pointer
while(start->next!=nullptr)
{
start =start->next;
end =end->next;
}
//现在end位于倒数n+1个
end->next= end->next->next;
return pre->next;
}
};
方法二、
要求删除倒数第N个节点,可以先设两个指针同时指向链表的第一个节点,一个指针遍历链表统计出总共有多少个节点记为i,用总数减去N,即可以算出要删除的节点为正数第几个节点记为index=i-N,让另一个指针移动到index节点的前一个节点(如果要删除的节点不是第一个节点)。最后执行删除操作,如果要删除的节点为第一个节点,则需要修改头指针,反之,则直接删除即可
/** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */
struct ListNode* removeNthFromEnd(struct ListNode* head, int n)
{
int i = 1, j = i, index = 0;
struct ListNode *p, *ptr;
p = ptr = head;
/*寻找到尾节点*/
while (p->next != NULL)
{
p = p->next;
i++;
}
/*确定要删除的节点为正数第几个节点*/
index = i - n + 1;
/*将另一个指针移动到index节点的前一个位置*/
while (j + 1 < index)
{
ptr = ptr->next;
j++;
}
/*删除操作,判断要删除的节点是否为第一个节点*/
if (index != 1)
{
ptr->next = ptr->next->next;
return head;
}
else
{
return head = ptr->next;
}
};
2.3 移除元素
思路:先得到数组的总元素个数,使用迭代器循环所有元素,遇到val相等的就自减,然后删除掉该元素
std::vector<int>::iterator it;
it = v3.erase(it); //删除元素,返回值指向已删除元素的下一个位置
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int ret = nums.size();
for (vector<int>::iterator it=nums.begin();it!=nums.end();it++)
{
if(*it == val)
{
ret--;
it = nums.erase(it);
it--;
}
}
return ret;
}
};
2.4 有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
思路:使用hash和栈(先入后出)
class Solution {
public:
bool isValid(string s) {
map<char,char> brance={
{')','('},
{'}','{'},
{']','['}
};
stack<char> sck;
for(auto ch:s)
{
if(brance.count(ch))
{
if(sck.empty()||sck.top()!=brance[ch])
{
return false;
}
sck.pop();
}
else
{
sck.push(ch);
}
}
if(sck.empty())
{
return true;
}
else
{
return false;
}
}
};
2.5 替换后的最长重复字符
双指针,也就是滑动窗口的方法:
- 让后指针一直往后移动 同时每移动一次 统计一次窗口内当前不符合条件的元素的个数
- 当不符合条件的元素个数超过给定k的时候,让前指针向后移动
- 直至不符合条件的个数小于等于k 继续让right向后移动
- 依次进行 直至right到达数组的末尾
当满足一定条件,左指针向右移动,而右指针一直向右移动。
class Solution {
public:
int characterReplacement(string s, int k)
{
int right=0;
int left=0;
int cunt=0;//统计不符合条件次数
int ret=0;
int alpha[26]={0};//统计滑窗内字母出现次数
while(right<s.size())
{
alpha[s[right]-'A']++;
cunt = max(cunt,alpha[s[right]-'A']);//滑窗内重复最多的字符
//这里是滑窗
if( right-left+1-cunt>k )
{
//左指针向右移动
alpha[s[left]-'A']--;
left++;
}
else
{
ret = max(ret,right-left+1);
}
right++;
}
return ret;
}
};
2.6 数组中的第K个最大元素
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
sort(nums.begin(),nums.end(),[](int i,int j){
return i>j;
});
return nums[k-1];
}
};
//stupid
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
/*
sort(nums.begin(),nums.end(),[](int i,int j){
return i>j;
});*/
int temp;
for(int i=0;i<nums.size();i++)
{
for(int j=i;j<nums.size();j++)
{
if (nums[j]>nums[i])
{
temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
}
}
}
return nums[k-1];
}
};
函数名 | 功能描述 |
---|---|
sort | 对给定区间所有元素进行排序 |
stable_sort | 对给定区间所有元素进行稳定排序 |
partial_sort | 对给定区间所有元素部分排序 |
partial_sort_copy | 对给定区间复制并排序 |
nth_element | 找出给定区间的某个位置对应的元素 |
is_sorted | 判断一个区间是否已经排好序 |
partition | 使得符合某个条件的元素放在前面 |
stable_partition | 相对稳定的使得符合某个条件的元素放在前面 |
sort(begin, end, cmp)
,其中begin
为指向待sort()
的数组的第一个元素的指针,end
为指向待sort()
的数组的最后一个元素的下一个位置
的指针,cmp
参数为排序准则,如果没有的话,默认以非降序排序。
2.7 摆动排序
这个题目非常有意思:
给你一个整数数组 nums
,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]...
的顺序。
示例 1:输入:nums = [1,5,1,1,6,4]
输出:[1,6,1,5,1,4]
解释:[1,4,1,5,1,6] 同样是符合题目要求的结果,可以被判题程序接受。
示例 2:输入:nums = [1,3,2,2,3,1]
输出:[2,3,1,3,1,2]
思路:先升序排序,再倒序插值
class Solution {
public:
void wiggleSort(vector<int>& nums) {
int top = 0;
sort(nums.begin(), nums.end());//increase
vector<int> numscpy(nums);
int back = nums.size();
if (back % 2 == 0)
{
top = nums.size() / 2;
}
else
{
top = nums.size() / 2+1;
}
for (int i = 0; i<nums.size(); i++)
{
if (i % 2 == 0)
{
nums[i] = numscpy[--top];
}
else
{
nums[i] = numscpy[--back];
}
}
}
};
2.8 前 K 个高频元素
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k)
{
unordered_map<int, int> mapEle;
priority_queue<int, vector<int>, greater<int> > small_heap;
vector<int>vecRet;
int i = 0;
//step1: hash to static
for (auto v : nums)
{
if (mapEle.find(v) != mapEle.end())//have
{
mapEle[v] += 1;
}
else
{
mapEle[v] = 1;
}
}
//step2:find elements
for (auto x : mapEle)
{
if (i<k)
{
small_heap.push(x.second);
}
else
{
if (x.second > small_heap.top())
{
small_heap.pop();
small_heap.push(x.second);
}
}
i++;
}
//step3: save
for (auto x : mapEle)
{
if (!small_heap.empty())
{
for (i = 0; i<small_heap.size(); i++)
{
if (x.second == small_heap.top())
{
vecRet.push_back(x.first);
small_heap.pop();
break;
}
}
}
}
return vecRet;
}
};
上述使用的小顶堆不是特别灵活,导致最后的时间复杂度不满足题目要求。
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
//step1:计算频率
unordered_map<int,int> frenCnt;
for(auto &v:nums)
frenCnt[v]++;
//step2: 排序
multimap<int,int,greater<int> > fre;
for(auto &f:frenCnt)
{
fre.insert(make_pair(f.second,f.first));
}
//step3:保存
vector<int> res;
for(auto it=fre.begin();it!=fre.end()&&k;k--,it++)
{
res.push_back(it->second);
}
return res;
}
};
using用法:为一个模板库定义一个别名
make_heap 建立堆(传递参数lambada表达式),压数据进堆 push_heap 弹数据出堆 pop_heap (其实只是放在vector最后一个)
count_set.push_back(x.second);
push_heap(count_set.begin(), count_set.end(), greater<int>());
(3,27,19,20,424,215,424,347)
参考:
队列、堆栈与数组、链表的区别与联系:https://blog.youkuaiyun.com/guo97/article/details/109500873
leetcode19: https://blog.youkuaiyun.com/vitodew/article/details/113706313
双指针:https://blog.youkuaiyun.com/weixin_44302602/article/details/113539766
Map按照键值排序:https://blog.youkuaiyun.com/iicy266/article/details/11906189