速成c++
假期参加了一个考试,可以选择各种语言作答。
我怕c语言太难了,怎么说呢,比如leetcode第一题,(对我就做过第一题)。c语言版本的题解长这样。
struct hashTable {
int key;
int val;
UT_hash_handle hh;
};
struct hashTable* hashtable;
struct hashTable* find(int ikey) {
struct hashTable* tmp;
HASH_FIND_INT(hashtable, &ikey, tmp);
return tmp;
}
void insert(int ikey, int ival) {
struct hashTable* it = find(ikey);
if (it == NULL) {
struct hashTable* tmp = malloc(sizeof(struct hashTable));
tmp->key = ikey, tmp->val = ival;
HASH_ADD_INT(hashtable, key, tmp);
} else {
it->val = ival;
}
}
int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
hashtable = NULL;
for (int i = 0; i < numsSize; i++) {
struct hashTable* it = find(target - nums[i]);
if (it != NULL) {
int* ret = malloc(sizeof(int) * 2);
ret[0] = it->val, ret[1] = i;
*returnSize = 2;
return ret;
}
insert(nums[i], i);
}
*returnSize = 0;
return NULL;
}
c++长这样
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> hashtable;
for (int i = 0; i < nums.size(); ++i) {
auto it = hashtable.find(target - nums[i]);
if (it != hashtable.end()) {
return {it->second, i};
}
hashtable[nums[i]] = i;
}
return {};
}
};
我知道不能给我自己算法不行找这种理由,但是这不影响我晚上考试,下午开始速成c++。
因为是速成,所以什么都没深究,能用就行,能看懂就行。比如说如果你能看懂我的速成笔记,你就能看懂上面这题解。
有些内容记得引入一下头文件,因为是速成,所以我用的万能头。
using namespace std
跟python的import差不多,写了using namespace std
就可以直接用cin , cout。
std是一个命名空间,这两个函数在这个命名空间里,如果不引入命名空间,就找不到这两个函数是什么意思了。
不写也不是不能用,就是得写std::cin std::cout,相当于直接一个个写出来“cin这个函数在std命名空间里找” “cout这个函数在std命名空间里找”这样也能用。
但是using namespace std
就相当于”找不到的函数都在std命名空间里找“,更方便一些。
cin cout
输入和输出。
cin连>> cout连<<
string类
string a="asdf";
就跟你char a[5]="asdf"
差不多。
本质上其实是不同的数据类型,string是一个类,这个类里有很多方法,比如a.length()。
定义string的时候不需要写容量5,在内存中是怎么保存的呢。
这个类里有一个属性是这个string的字符串的头指针_Ptr,
当我们向string变量赋值时,它是通过动态分配内存给_Ptr来存储字符串,分配到的内存容量存到类的另一个属性cap里。
对这个string重新赋值的时候,先检查cap能不能放下新的值,不够的话重新分内存,copy过去。
有可以reverse的方法,也有类似于strstr的方法,用的时候再百度吧。
引用
引用,相当于起别名。
int a=4;
int &b=a;
这以后b和a就完全相同了,他们的内存地址一样,里面存的都是一样的4,
不是说b是个指针指向了a,而是说b就是a。
此后写a=5;或者b=5;他们都变成了5,因为他们就是一个东西。
可以试着打印一下b,a,&b,&a会发现它们都一样。
引用传递是一种不同于值传递的参数传递方式,改变形参就是改变实参。
void yinyongchuandi(int &q)
//这是引用传递,一种不同于值传递的参数传递方式。可以直接改变实参
{
q = 2;
}
int main()
{
int a=1;
yinyongchuandi(a);
//a=2
}
很适合想要返回多个值的场合。
vector
也是一个类,动态数组。采用的数据结构为线性连续空间。
一开始不用像静态数组一样一定要说好容量,且不能扩充,vector的容量可以扩充。
它的时间复杂度和静态数组一样,修改和pushback都是O(1),insert什么的是O(n)。
class vector {
...
protected:
iterator start; // 表示目前使用空间的头
iterator finish; // 表示目前使用空间的尾
iterator end_of_storage; // 表示可用空间的尾
...};
我们在使用 vector 时,最常使用的操作恐怕就是插入操作了(push_back),那么当执行该操作时,该函数都做了哪些工作呢?
该函数首先检查是否还有备用空间,如果有就直接在备用空间上构造元素,并调整迭代器 finish,使 vector 变大。如果没有备用空间了,就扩充空间,重新配置、移动数据,释放原空间。
迭代器是个指针,begin相当于指向a[0],end指向最后一个元素的下一个位置。
vector<int>::iterator g = f.begin();
//这就是定义了一个迭代器,和f.begin()一样
while (g != f.end())
{
cout << *g << " ";
g++;
}
当对迭代器对应的容器进行任何添加或删除操作(不包括修改容器内元素值),则其所有与该容器相关联的迭代器就会失效,需要重新赋值。
set
set<string> l;
l.insert("banana");
l.insert("peach");
l.insert("fox");
l.erase("fox");
//无序集合,插入这几个元素
cout << l.count("fox") << " " << l.size() << " " << (l.find("peach") == (++l.begin())) << endl;
//fox有0个 集合里元素有2个 find peach返回的迭代器是第二个,
//count() 用来查找set中某个某个键值出现的次数。这个函数在set并不是很实用,因为一个键值在set只可能出现0或1次,这样就变成了判断某一键值是否在set出现过了。
if (l.find("fox") == l.end())
//为什么是 == l.end()呢,首先要知道,end指向最后一个元素的后一个位置,比如数组的长度为5,有5个元素,f.end()指向的就是不存在的“第六个元素”,然后,find方法会从一个元素一个个向后寻找,看哪个等于要找的东西,等到所有的都看完了(迭代器停在“第六个元素”那里了)还没找到,find方法就会停下来
{
cout << "fox not found\n";
}
map
我好困
map储存键值对,键值的类型都可以自己确定,可以直接通过键取下标。
如果存在就返回值,不存在就返回0.
遍历使用迭代器,自己会按着键排序。
能进行算术运算的迭代器只有随机访问迭代器,要求容器元素存储在连续内存空间内,即vector、string、deque的迭代器是有加减法的;而map、set、multimap、multiset、list的迭代器是没有加减法的。他们仅支持++itr、–itr这些操作(下面的代码块用了一次)。
(他们内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也成为RB树(Red-Black Tree)。RB树的统计性能要好于一般平衡二叉树,所以被STL选择作为了关联容器的内部结构。)
map<string, int> n;
n["chicken"] = 12;
n["dog"] = -6;
cout << n["cat"] << " " << n["dog"] << endl;
//光是访问n["cat"]就相当于添加了这个键,值默认为0
if (n.find("chicken") != n.end())
{
cout << "chicken exists" << endl;
}
if (n.count("chicken") != 0)
{
cout << "chicken exists" << endl;
}
for (map<string, int>::iterator iter = n.begin();
iter != n.end(); ++iter) {
cout << iter->first << " " << iter->second << endl;
}
unordered
unordered map unordered set就是不带排序的map set
可以省时间
因为map set内部自带排序,也就是你不按顺序往里面insert一堆东西,最后你用迭代器遍历一遍一会发现是按顺序输出的。可以试试下面的代码。
#include <iostream>
#include <set>
using namespace std;
int main()
{
set<int> st;
set<int>::iterator it;
st.insert(11);
st.insert(122);
st.insert(13);
st.insert(1);
for (it = st.begin(); it != st.end(); it++)
{
cout << *it << endl;
}
set<string> st1;
set<string>::iterator it1;
st1.insert("abc");
st1.insert("bca");
st1.insert("ab");
st1.insert("a");
for (it1 = st1.begin(); it1 != st1.end(); it1++)
{
cout << *it1 << endl;
}
return 0;
}
所以因为这个“自动排序”肯定会花一些时间,在不需要它的自动排序的场合,可以用unordered set代替set。
说到自动排序,当数据元素增多时,set的插入和搜索速度变化如何?
在set中查找是使用二分查找,所以不用担心效率。
map也一样。
stack
stack<int> o;
o.push(1);
o.push(2);
o.pop();
cout << o.top() << " " << o.size() << " " << o.empty() << endl;
也就只能做这些了,stack只能操作栈顶,不能便利,你用一个迭代器,或者数组下标o[1] o[2]访问是不行的,只能用o.top()方法看栈顶是什么。
如果想访问所有元素,那就得用vector模拟栈,push_back, pop_back是一样的。
queue
queue<int>p;
for (int i = 0;i < 5;i++)
{
p.push(i);
}
p.pop();
cout << "front:" << p.front() << " end:" << p.back() << endl;
只能访问首尾,要是用vector模拟队列,出队会很慢(弹出队首是O(n)好像)。
//升序队列
priority_queue <int,vector,greater > q;
//降序队列
priority_queue <int,vector,less >q;
优先队列放进去就自动排序了!
sort
int q[5] = { 2,3,7,8,5 };
sort(q, q + 5);
//sort可以用于数组和vector,vector的参数是迭代器,如begin,end,比如
//sort(f.begin(), f.end());
//数组的参数是指针
for (int i = 0;i < 5;i++)
{
cout << q[i] << " ";
}cout << endl;
int r[5] = { 2,3,7,8,5 };
sort(r, r + 5, cmp);
//sort可以用于数组和vector,vector的参数是迭代器,如begin,end,数组的参数是指针
for (int i = 0;i < 5;i++)
{
cout << r[i] << " ";
}cout << endl;
bool cmp(int a, int b)
{
return a > b;//如果返回真,第一个参数在第二个参数前面
}
二分
自带,快捷啊,一行就完事了。
int a[100]= {4,10,11,30,69,70,96,100};
int b=binary_search(a,a+9,4);//查找成功,返回1
cout<<"在数组中查找元素4,结果为:"<<b<<endl;
int c=binary_search(a,a+9,40);//查找失败,返回0
cout<<"在数组中查找元素40,结果为:"<<c<<endl;
int d=lower_bound(a,a+9,10)-a;
cout<<"在数组中查找第一个大于等于10的元素位置,结果为:"<<d<<endl;
int e=lower_bound(a,a+9,101)-a;
cout<<"在数组中查找第一个大于等于101的元素位置,结果为:"<<e<<endl;
int f=upper_bound(a,a+9,10)-a;
cout<<"在数组中查找第一个大于10的元素位置,结果为:"<<f<<endl;
int g=upper_bound(a,a+9,101)-a;
cout<<"在数组中查找第一个大于101的元素位置,结果为:"<<g<<endl;
我速成的时候敲的代码
#include <bits/stdc++.h>
using namespace std;
void yinyongchuandi(int& q)
//这是引用传递,一种不同于值传递的参数传递方式。可以直接改变实参
{
q = 2;
}
bool cmp(int a, int b)
{
return a > b;//如果返回真,第一个参数在第二个参数前面
}
template <typename T>//照葫芦画瓢用了模板,调用下面的函数方便
void bianli(vector<T> f)
{
typename vector<T>::iterator g;
g = f.begin();
//auto g = f.begin();//自动确定数据类型 这玩意特别方便,根据右值的类型就知道左值的类型了,但是只有c11能用
while (g != f.end())
{
cout << *g << " ";
g++;
}
cout << endl;
}
struct Node
{
int value;
struct Node* next;
};
int main()
{
string a = "world";
// cin >> a; 这个相当于scanf%s,到空格就停,忽略前面的空白符
// getline(cin, a); 这个相当于gets,一行有啥是啥,从缓冲区取回车并忽略,字符串里没回车
cout << a << " " << a.length() << endl;
string b = "hello";
cout << a + b << endl;//自带strcat
cout << a[2] << endl;
cout << (a > b) << endl;//自带strcmp
int c = 1;
int& d = c;
printf("%p %p\n", &c, &d);
cout << typeid(d).name() << typeid(c).name() << endl;
//这说明d和c地址一样,类型都是int,d并不是指针或者别的类型,就是c的别名
yinyongchuandi(c);
cout << "c=" << c << endl;
Node e; // c++还有个细微的不同 不用必须加struct了
e.value = 1;
vector<int> f; // f(10)初始化了10个0,接下来pushback就是第十一个
vector<int> h(3, 6);//初始化三个元素,每个都是6
f.reserve(20);
//如果vector的容量不够了怎么办,首先不用担心,你一直insert编译器就会默默一直给你增加长度
//如果你想自己定这玩意的长度,可以用reserve方法和resize方法
//如果用resize 就是20个0了,接下来pushback就是第21个
//用reserve的话 相当于预约了20个空间 但是没有值
for (int i = 0; i < 5; i++)
{
f.push_back(i);
// f.pop_back();
}
// 0 1 2 3 4
cout << f.size() << endl;
f[0] = 3;
// 3 1 2 3 4
f.insert(f.begin() + 2, 9); //在a[2]插入了9 ->3 1 9 2 3 4
f.erase(f.begin() + 3); //删除a[3]->3 1 9 3 4
bianli<int>(f);
f.insert(f.begin(), h.begin(), h.end()); // rbegin rend ++反向迭代 我没细说 可以查查这个
bianli<int>(f); // 6 6 6 3 1 9 3 4
sort(f.begin(), f.end()); //从小到大排序,这是个algorithm库里面的方法
bianli<int>(f);
reverse(f.begin(), f.end()); //从大到小
// system("pause");
//二维数组
vector<vector<int> > k(5, vector<int>(4)); //五行四列
//千万千万注意,这必须↑有空格,比较旧的c++版本会把两个连着的>>当成位运算
for (int i = 0; i < k.size(); i++) //输出二维动态数组
{
for (int j = 0; j < k[i].size(); j++)
{
cout << k[i][j] << " ";
}
cout << "\n";
}
set<string> l;
l.insert("banana");
l.insert("peach");
l.insert("fox");
l.erase("fox");
cout << l.count("fox") << " " << l.size() << " " << (l.find("peach") == (++l.begin())) << endl;
//fox有0个 集合里元素有2个 find peach返回的迭代器是第二个,
if (l.find("fox") == l.end())
{
cout << "fox not found\n";
}
vector <double> m(5);//这里是为了测试template写的对不对
for (int i = 0; i < 5; i++)
{
m.push_back(i);
// f.pop_back();
}
bianli<double>(m);//0 0 0 0 0 0 1 2 3 4
map<string, int> n;
n["chicken"] = 12;
n["dog"] = -6;
cout << n["cat"] << " " << n["dog"] << endl;
//光是访问n["cat"]就相当于添加了这个键,值默认为0
if (n.find("chicken") != n.end())
{
cout << "chicken exists" << endl;
}
if (n.count("chicken") != 0)
{
cout << "chicken exists" << endl;
}
for (map<string, int>::iterator iter = n.begin();
iter != n.end(); ++iter) {
cout << iter->first << " " << iter->second << endl;
}
stack<int> o;
o.push(1);
o.push(2);
o.pop();
cout << o.top() << " " << o.size() << " " << o.empty() << endl;
queue<int>p;
for (int i = 0;i < 5;i++)
{
p.push(i);
}
p.pop();
cout << "front:" << p.front() << " end:" << p.back() << endl;
int q[5] = { 2,3,7,8,5 };
sort(q, q + 5);
//sort可以用于数组和vector,vector的参数是迭代器,如begin,end,数组的参数是指针
for (int i = 0;i < 5;i++)
{
cout << q[i] << " ";
}cout << endl;
int r[5] = { 2,3,7,8,5 };
sort(r, r + 5, cmp);
//sort可以用于数组和vector,vector的参数是迭代器,如begin,end,数组的参数是指针
for (int i = 0;i < 5;i++)
{
cout << r[i] << " ";
}cout << endl;
int A[100] = { 4,10,11,30,69,70,96,100 };
int B = binary_search(A, A + 9, 4);//查找成功,返回1
cout << B << endl;
int C = binary_search(A, A + 9, 40);//查找失败,返回0
cout << C << endl;
int D = lower_bound(A, A + 9, 10) - A;
cout << D << endl;
int E = lower_bound(A, A + 9, 101) - A;
cout << E << endl;
int F = upper_bound(A, A + 9, 10) - A;
cout << F << endl;
int G = upper_bound(A, A + 9, 101) - A;
cout << G << endl;
}