刷题散记
【1、C malloc】
result=(int*)malloc(sizeof(int)*2)
malloc 是 c 语言中的动态分配内存,malloc 函数返回的是 void\* 型,所以要强制类型转换成 int,在前面加上 (int *),才能给整型赋值,后面 (sizeof(int)*2) 的意思是分配两个 int 大小的空间;
========
【2、哈希表】
哈希表,又名散列表。一种数据结构。
实现方式:数组+链表 / 数组+二叉树
本质上是以数组方式存储key-value键值对
存储方式:key ——》经过哈希/散列函数计算得出哈希值 ——》以哈希值为索引存储键值对
(在哈希表中,a映射到b,a就叫做键值,b叫做a的哈希值)
当计算得出的哈希值重复,成为哈希冲突/哈希碰撞
处理哈希冲突:1、开放寻址法 2、拉链法
开放寻址法:使用冲突位置的后一位置进行存储,如果也已被占用,则以此类推(如java中的ThreadLocal)
拉链法:将数组变为链表+数组的存储方式。将冲突键值对存储在已有键值对的后方,使用next指针指向后者,形成一个链表
<关于拉链法>以java的HashMap为例,若链表元素个数>=8,链表自动转化为树结构,若长度<=6,则转回链表(7作为差值,来避免频繁的进行树和链表的转换)
哈希表的扩容:若占用率超过增长因子(又名负载因子,即已占用位置和总位置的百分比),比如HashMap增长因子为75%,则触发扩容。
扩容——新创建一个数组是原来的2倍,然后把原数组的所有键值对都重新Hash一遍放到新的数组。
哈希表读取:通过Key利用哈希函数得出key的哈希值(即地址),通过哈希值去访问对应链表。若第一个键值对的key值与查询key不符,则查找下一位,直到找到。
哈希表的核心:哈希函数
参考文章:https://zhuanlan.zhihu.com/p/95156642
有关问题:
1、为什么HashMap不是线程安全的?
考虑两方面:1.有没有写2.有没有写同一个对象(共享资源)
看成链表一样,会有多个线程对数据进行修改,在插入的过程包括扩容中是使数据出现错误。
2、冲突可能消除吗?
数组的长度 << 数据的个数,所有数据放在有限个数组元素中,必然会发生冲突
3、冲突不能消除的情况,并且不想要冲突,怎么尽可能避免冲突?
<1>hash的函数设计:理想情况下,key是符合均匀分布的。经过hash函数,得到的hash值也尽可能的均匀分布。
<2>负载因子=key的个数/数组的长度。假设数组长度不变,key越多,越容易发生冲突。可通过控制负载因子来控制冲突
降低负载因子的方法:增加数组的长度(为什么需要扩容:通过增加数组长度,降低负载因子,降低冲突率)
java中负载因子为什么默认0.75?经过计算+经验值,超多阈值的情况下,冲突率太高了。
4、真冲突了怎么解决?
闭散列:此处不留爷,自有留爷处
开散列/哈希桶: 利用一个其他的数据结构存储所有冲突的key,之所以选择链表是:一般情况下冲突的个数不会太多,链表就够用了。
单链表每有一个数据就保留一个数据,顺序表是需要预留空间。HashMap是采用这种方式。
参考文章:https://blog.csdn.net/weixin_45285317/article/details/104483749
=========
【3、C++ 中的set、map 和 unordered_set、unordered_map】
【set和unordered_set区别】
set基于红黑树实现,红黑树具有自动排序的功能,因此map内部所有的数据,在任何时候,都是有序的。
(C++11新容器)unordered_set基于哈希表,使用键值对存储,对于冲突采用链地址法,把具有同一关键码的数据组成一个链表。数据插入和查找的时间复杂度很低,几乎是常数时间,而代价是消耗比较多的内存,无自动排序功能。
【set】
st.insert(num)
【map】
键值对容器,里面的数据成对出现。key只能在map中出现一次。map内部所有的数据按key排序。
声明:
1、map<int, string> ID_Name;
// 使用{}赋值是从c++11开始的,因此编译器版本过低时会报错,如visual studio 2012
2、map<int, string> ID_Name = { { 2015, "Jim" },{ 2016, "Tom" },{ 2017, "Bob" } };
插入操作:
1、[ ]进行单个插入
// 如果已经存在键值2015,则会作赋值修改操作,如果没有则插入
ID_Name[2015] = "Tom";
2、insert进行单个or多个插入(insert共有4个重载函数)
pair<iterator,bool> insert (const value_type& val);// 插入单个键值对,并返回插入位置和成功标志,插入位置已经存在值时,插入失败
iterator insert (const_iterator position, const value_type& val);//在指定位置插入,在不同位置插入效率是不一样的,因为涉及到重排
void insert (InputIterator first, InputIterator last);// 插入多个
void insert (initializer_list<value_type> il);//c++11开始支持,使用列表插入多个
3、删除
// 删除键为bfff指向的元素
cmap.erase("bfff");
// 删除迭代器 key所指向的元素
map<string,int>::iterator key = cmap.find("mykey");
if(key!=cmap.end()) cmap.erase(key);
// 删除所有元素
cmap.erase(cmap.begin(),cmap.end())
4、map容器的迭代器first、second用法
例:
map<string, int> m_stlmap;
m_stlmap[“xiaomi”] = 88;
auto mpit = m_stlmap.begin();
first会得到Map中key的有效值,
second会得到Map中value的有效值。
所以
mpit ->first; // 得到是 string 值是 “xiaomi”
mpit ->second; //得到是 int 值是 88
==========
【4、C++ 中的vector<int>】
向量(Vector)是一个封装了动态大小数组的顺序容器。可以简单的认为,向量是一个能够存放任意类型的动态数组。
【vector<int> a 和 int a[]的区别】
数组使用前要实例化,实例化了,长度就固定了,而Vector实例化不会固定长度,想添加还可以添加内容
vector<int>不需要动态new内存,当然也不用delete
1、初始化
std::vector<std::wstring> v1; //创建一个空的wstring类型的vector
std::vector<std::wstring> v2(3, L"c"); //创建一个容量为3,全部初始化L"c"
std::vector<int> v3(5); //创建容量为5,数据类型为int的vector
std::vector<int> v4(v3); //创建一个从v3拷贝过来的vector
2、插入元素
vector<int> vec;
vec.push_back("3"); //在末尾插入一个元素
或 vec.insert(vec.end(), "3"); // 在末尾插入一个元素
vec.insert(vec.begin(), "3"); // 在开头插入一个元素
vec.insert(vec.begin()+4, "3"); // 在指定位置,例如在第五个元素前插入一个元素
3、删除
vec.erase(vec.begin()); //删除开头的元素
vec.erase(pos) // 删除pos位置的数据,传回下一个数据的位置。
vec.erase(vec.begin(),vec.end); //删除[begin,end]区间的元素,传回下一个数据的位置。
vec.pop_back(); //删除最后一个元素
4、查找
vector本身没有find方法,需要使用std::find
vector<int> vec;
vector<int>::iterator it = find(vec.begin(), vec.end(), 6);
if (it != vec.end()) return "找到了!";
else return "没找到!";
5、其他函数
vec.front() // 传回第一个数据。
vec.begin() // 传回迭代器中的第一个数据地址。
vec.end()-1 // 是vec的最后一个元素,
vec.end() // 是最后一个元素的后面一位。
vec.empty() // 判断容器是否为空。
vec.at(idx) // 传回索引idx所指的数据,如果idx越界,抛出out_of_range。
vec.size() // 获取nums数组长度
vec.count(值) // 统计某个指定的值出现次数
---
6、关于字符数组
vector<string>& strs = [];
if(strs.empty()) return string(); // *string() 返回空值
==========
【5、关于C++ 中string类型】
1、获取字符串元素(可使用下标访问string)
str[index] 或 str.at(index)
2、字符串查找
str.find(s1) //查找str中第一次出现s1的位置,并返回(包括0)
str.rfind(s1) //查找str中最后次出现s1的位置,并返回(包括0)
s.find_first_of(s1) //查找在s1中任意一个字符在s中第一次出现的位置,并返回(包括0)
s.find_last_of(s1) //查找在s1中任意一个字符在s中最后一次出现的位置,并返回(包括0)
s.fin_first_not_of(s1) //查找s中第一个不属于s1中的字符的位置,并返回(包括0)
s.fin_last_not_of(s1) //查找s中最后一个不属于s1中的字符的位置,并返回(包括0)
※※ 查找字符串a是否包含子串b,不是用strA.find(strB) > 0而是strA.find(strB) != string:npos
string::size_type pos = strA.find(strB);
if(pos != string::npos){
// 代码块
}
3、字符串截取
str.substr(pos, n) //截取str中从pos开始(包括0)的n个字符的子串,并返回
str.substr(pos) //截取str中从从pos开始(包括0)到末尾的所有字符的子串,并返回
str.replace(pos, n, s1) //替换:用s1替换str中从pos开始(包括0)的n个字符的子串
reverse(str.begin(), str.end()); //逆序:C++标准类库函数reverse。str执行完这句,就已经是逆序结果。
string stringStr = std::to_string(intNum) int转string(std::可以省略)
4、字符串替换
s.replace(pos, n, s1) //用s1替换s中从pos开始(包括0)的n个字符的子串
5、数字与字符串互转
// 数字转字符串
string str = to_string(x);
// 字符串转数字
string str = "7";
int num = stoi(str);
7、"str.find(i) != str.end()"和"str.find(i) = str.end()"到底啥意思
标准库里迭代器部分的内容。即用find函数,去找str这个序列中的i元素,元素不存在,就会返回end()。
str.find(i) != str.end() //说明找到了
str.find(i) == str.end() //说明没到
==========
【6、C++ char* 和 char区别】
1、储存内容不同:char*是定义一个字符串,存储的是一段如“abcd”的数据,而且最后还有一个结束符'\0';char 是定义一个字符,存储一个字符,占一个字节。
2、数据类型不同:char*是指针类型,是一个用来保存一个内存地址的计算机语言中的变量。char 是字符类型,用来存储字符值。
==========
【7、c++ stack栈】
栈(Stack)是一种线性存储结构,先进后出(First In Last Out),只能在栈顶进行插入和删除操作。
stack<char> st;
st.push('a') // 入栈
st.pop() // 出栈,删除栈顶元素。
st.top() // 返回栈顶元素, 但不删除该元素
st.empty() // 如果栈为空则返回true, 否则返回false;
st.size(); // 返回栈中元素的个数
==========
【8、cout<<" "与cout<<' '有什么区别】
cout<<" "表示输出字符串变量值。
cout<<' '表示是字符变量值,字母的本质也是数字。
如:
int a=10;
cout<<"a";//输出为 字符a;
cout<<a;//输出为10;
cout<<'a' ;//输出为65;
* cout << var << endl; // endl的作用:输出一个换行符,并立即刷新缓冲区
==========
【9、count 和 count_if用法】
1)count(first,last,value):first是容器的首迭代器,last是容器的末迭代器,value是询问的元素,整个函数返回int型。count函数的功能是:统计容器中等于value元素的个数。
2)count_if(first,last,comp) (在comp=true的情况下计数)
或者 count_if(first,last,value,comp)(这个是在comp=true的情况下统计容器中等于value的元素):
first为首迭代器,last为末迭代器,value为要查询的元素,comp为比较bool函数,为true则计数,函数返回型是int。
【统计字符串中某个字符的个数?】
方法一:count(str.begin(), str.end(), 'a'); // 查找str中字符a的个数
count_if(list.begin(),list.end(),判断条件,int 返回个数)
count(list.begin(),list.end(),满足条件的参数,int 返回个数)
==========
【C++ 中的循环】
1、switch循环
switch case()的case传参不可以传string类型
2、foreach循环
vector<int> nums;
for(int x: nums){
// 代码块
}
==========