C++——C++ Primer与LetCode经典笔试

目录

一、前言

二、C++ Primer知识回顾

2.1 C++基础

2.2C++顺序容器

三、let's coding

2.1 无重复字符的最长子串

2.2 删除链表的倒数第 N 个结点

2.3 移除元素

2.4 有效的括号

2.5 替换后的最长重复字符


一、前言

以前不管是在学校还是项目/笔试上都看过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)堆和栈:

  1. 栈具有数据结构中栈的特点,后进先出,所有存放在它里面的数据都是生命周期很明确(当然要求它不能存放太久,占有的空间确定而且占用空间小),能够快速反应的

  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)选择容器原则:

  1. 除非你有很好的理由选择其他容器,否则应使用vector
  2. 如果你的程序有很多小的元素,且空间的额外开销很重要(),则不要使用list或者forward_list。
  3. 如果要求随机访问,使用vector或者deque
  4. 如果要求在容器中间插入或者删除元素,应使用list或者forward_list
  5. 如果程序要求在头尾位置插入或者 删除元素,不会在中间操作,应该使用deque
  6. 如果在读取输入时需要在容器中间位置插入元素,随后需要随机访问,那么先用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.csdn.net/guo97/article/details/109500873

leetcode19: https://blog.csdn.net/vitodew/article/details/113706313

双指针:https://blog.csdn.net/weixin_44302602/article/details/113539766

Map按照键值排序:https://blog.csdn.net/iicy266/article/details/11906189

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值