C++中的数据结构(笔记)

随手笔记,仅供参考

C++中的数据结构

需要导入的头文件

#include <iostream>
#include <vector>		// vector
#include <algorithm>	// find, swap, sort, unique, reverse, lower_bound, min, max, max_element
#include <functional>	// less, greater
#include <queue>		// priority_queue, queue
#include <stack>		// stack
#include <unordered_map> // unordered_map
#include <unordered_set> // unordered_set
#include <map> 			// map, multimap
#include <set> 			// set, multiset
#inlcude <utility> 		// pair, swap(exchange values of two objects)
#include <tuple> 		// tuple (get)
#include <string>		// string
using namespace std;

两数之和

输入描述

输入数据有多组, 每行表示一组输入数据。
每行不定有n个整数,空格隔开。(1 <= n <= 100)

输出描述

每组数据输出求和的结果

示例:
输入:

1 2 3
4 5
0 0 0 0 0

输出:

6
9
0

代码:

#include <iostream>
#include <vector>
using namespace std;

using ll = long long;
int solution(vector<int>& nums){
    int n = nums.size();
    ll sum = 0;
    for(int i=0;i<n;i++){
        sum += nums[i];  
    }
    return sum;
}
void submit(int ans){
    cout << ans << endl;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    int n;
    vector<int> nums;
    while(cin >> n){
        nums.push_back(n);
        if(cin.get() == '\n'){
            int ans = solution(nums);
            submit(ans);
            nums.clear();
        }
    }
    return 0;
}
// 64 位输出请用 printf("%lld")

对输入的字符串进行排序

输入描述

多个测试用例,每个测试用例一行。
每行通过,隔开,有n个字符,n<100

输出描述

对于每组用例输出一行排序后的字符串,用','隔开,无结尾空格

示例
输入:

a,c,bb
f,dddd
nowcoder

输出:

a,bb,c
dddd,f
nowcoder

代码:

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

using ll = long long;
vector<string> solution(vector<string>& nums){
    int n = nums.size();
    sort(nums.begin(), nums.end());
    return nums;
}
void submit(vector<string>& ans){
    int n = ans.size();
    for(int i=0;i<n;i++){
        cout << ans[i];
        if(i != n-1){
            cout << ",";
        }
    }
    cout << endl;
}
void split(string& s, vector<string>& tokens, const string& delimiters=" "){
    auto last_pos = s.find_first_not_of(delimiters, 0);
    auto pos = s.find_first_of(delimiters, last_pos);
    while(pos!=string::npos || last_pos !=string::npos){
        tokens.push_back(s.substr(last_pos, pos-last_pos));
        last_pos = s.find_first_not_of(delimiters, pos);
        pos = s.find_first_of(delimiters, last_pos);
    }
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    string a;
    while(getline(cin, a)){
        vector<string> nums;
        split(a, nums, ",");
        auto ans = solution(nums);
        submit(ans);
    }
    return 0;
}
// 64 位输出请用 printf("%lld")

数组(向量)-- vector

#include <vector>

对象定义法

//一维vector
vector<int> *v = new vector<int>();
//二维vector
vector<vector<int>> *v2 = new vector<vector<int>*>();
遍历
//打印输出(二维数组)
for(int i=0;i<v2->size();i++){
    for(int j=0;j<v2->at(0)->size();j++){
        cout << v2->at(i)->at(j) << endl;
    }
}

普通定义法

//声明R行C列的数组,赋初值为0
vector<vector<int>> flag(R, vector<int>(C, 0));

维度为(n,2)vector的声明

第一种:

vector<vector<int>> cp(n,vector<int>(2,0));
for(int i=0;i<n;i++){
    cp[i][0] = capital[i];
    cp[i][1] = profits[i];
}

第二种:

vector<vector<int>> cp(n);
for(int i=0;i<n;i++){
    // cp[i].emplace_back(capital[i]);
    // cp[i].emplace_back(profits[i]);

    cp[i].push_back(capital[i]);
    cp[i].push_back(profits[i]);
}

理论上emplace_back()快一点。
本例中push_back()稍微比emplace_back()快一点。
第三种:

vector<pair<int,int>> cp;
for(int i=0;i<n;i++){
    // cp[i] = {capital[i],profits[i]};
    cp.push_back({capital[i],profits[i]});
}

第三种方法最快,第二种次之,第一种最慢。
vector<pair<int,int>>vector<vector<int>>快一倍。


vector操作

push_back在尾部插入

void push_back(const T& x):向量尾部增加一个元素X

emplace_back在尾部构造

push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素)
emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程

pop_back在尾部删除

void pop_back():删除向量中最后一个元素

back()获得尾部元素
v.back()
front()获得头部元素
v.front()
size元素个数

int size() const:返回向量中元素的个数

insert(i,x)在i前插入
iterator insert(iterator i,x)
A.insert(A.begin()+k,x)
插入–合并vector
//在A的后面插入
A.insert(A.end(),B,begin(),B.end());
删除–erase
iterator erase (iterator position);
iterator erase (iterator first, iterator last);
获得首元素引用front,尾元素引用back
reference front():返回首元素的引用
reference back():返回尾元素的引用
resize(n, value) 修改大小

修改容器大小,变大的部分用value修改为默认值

for (int i=1;i<10;i++) myvector.push_back(i);
myvector.resize(5);
myvector.resize(8,100);
myvector.resize(12);
//myvector包含:1 2 3 4 5 100 100 100 0 0 0 0
数组翻转

使用<algorthm>中的reverse()

#include <algorithm>
reverse(A.begin(),A.end())
复制vector

初始化构造时拷贝

vector<int> tem(list);

assign

vector<int> temlist;
temlist.assign(list.begin(), list.end());

Algorithm

unique

unique移除相邻的重复元素,如果想要移除所有(包括不相邻的)元素,必须先将序列排序,使所有重复元素相邻。

unique返回,最后一个被保留元素的下一个iterator

vector<int> myvector = {1,2,2,2,3,3,2,2,1}

vector<int>::iterator it = unique(myvector.begin(),myvecotr.end())
#out:1,2,3,2,1,?,?,?,?

myvector.erase(it,myvector.end())
#out:1,2,3,2,1

sort排序

//升序
sort(A.begin(),A.end(),less<int>());
//降序
sort(A.begin(),A.end(),greater<int>());

自定义比较函数

【sort】 与 【堆/优先队列】生成的顺序刚好相反。
都是默认less<>()比较函数,但是【sort】之后元素升序排列;【堆/优先队列】生成大顶堆

注:注意比较函数里面绝对不能写>=或者<=,比较函数要求如果两个元素相等,应返回false。你这里两个元素相等返回了true,导致stl的sort异常执行,导致段错误。

传入比较函数

匿名函数写法

struct Edge {
    int len, x, y;
    Edge(int len, int x, int y) : len(len), x(x), y(y) {
    }
};
sort(edges.begin(), edges.end(), [](Edge a, Edge b) -> int { return a.len < b.len; });
重载操作符

priority_queue的比较结果是【相反】的

class Point{
    public:
        int x;
        int y;
        int dist;
        Point(int x,int y,int dist){
            this->x = x;
            this->y = y;
            this->dist = dist;
        }
        //根据【希望】的情况进行返回
        bool operator<(const Point &p) const{
            return dist>p.dist;
        }
};
priority_queue<Point> q;

max_element

vector<int> arr = {1,2,3,4};
int maxNum = *max_element(arr.begin(),arr.end());
cout << maxNum << endl; // 4

队列 — queue

#include <queue>
queue<int> q;

队列操作

获得队首
q.front();		//获得队列最前面一个元素引用
获得队尾
q.back();		//返回队列最后一个元素引用
判空
q.empty();   //检查是否为空的方法 
入队
q.push();		//在队列尾添加一个数据
出队

返回void

q.pop();      //删除队列头的一个数据,返回void

size()队列中元素个数

栈 —Stack

#include <stack>
stack<int> mystack;

栈操作

判空操作
s.empty();         //如果栈为空则返回true, 否则返回false;
获取大小
s.size();          //返回栈中元素的个数
获取栈顶元素
s.top();           //返回栈顶元素, 但不删除该元素
出栈
s.pop();           //弹出栈顶元素, 但不返回其值
进栈
s.push();       //将元素压入栈顶
s.emplace();    //构造并插入元素

堆不是容器,而是组织容器元素的一种特别方式。

堆操作

创建堆
vector<int> A = {};
//构造 小顶堆
mack_heap(A.begin(),A.end(),greater<>());
//构建 大顶堆(默认)
mack_heap(A.begin(),A.end(),less<>());
插入堆
A.push_back(x);
//greater/less必须与创建堆使用的相同
push_heap(A.begin(),A.end(),greater<>());

push_heap() 会因此认为最后一个元素是新元素,为了保持堆结构,会重新排列序列。

删除堆顶堆
pop_heap(A.begin(),A.end());
A.pop_back();

pop_head将第一个元素移动到最后

检查是否是堆
is_heap(A.begin(),A.end());
堆排序
//大顶堆
make_heap(A.begin(), A.end());//{12 10 3.5 6.5 8 2.5 1.5 6}
//升序排列
sort_heap(A.begin(), A.end());//{1.5 2.5 3.5 6 6.5 8 10 12}
//小顶堆
make_heap(A.begin(), A.end(),greater<>());// {1.5 6 2.5 6.5 8 12 3.5 10}
//降序排列
sort_heap(A.begin(), A.end(),greater<>());//{12 10 8 6.5 6 3.5 2.5 1.5}

优先队列

pair的比较,先比较第一个元素,第一个相等比较第二个。

//pair<x,y>键值对
priority_queue<pair<int, int>> q;
//降序队列,大顶堆,默认
priority_queue <int,vector<int>,less<int> >q;
//升序队列,小顶堆
priority_queue <int,vector<int>,greater<int> > q;
插入
//构造插入
q.emplace(dist, i);
push();
删除
q.pop();  //无返回值
获得顶部
q.top();

自定义比较函数

例1

匿名函数+decltype推断类型+构造方法

auto cmp = [](const pair<string, int>& a, const pair<string, int>& b) {
	return a.second == b.second ? a.first < b.first : a.second > b.second;
};
priority_queue<pair<string, int>, vector<pair<string, int>>, decltype(cmp)> queue(cmp);

例2

结构体内比较函数+传入比较函数泛型

struct cmp {
    bool operator ()(pair<string, int>& a, pair<string, int>& b) {
        return a.second == b.second ? a.first < b.first : a.second > b.second;
    }
};
priority_queue<pair<string, int>, vector<pair<string, int>>, cmp> queue;

哈希表

map升降序

map<int,int,less<int>> mmap;  //默认升序
map<int,int,greater<int>> mmap;  //降序

哈希表操作

#include <unordered_map>
unordered_map<string,int> map,
插入
map.insert (x);                        		  // 复制插入
map.insert (make_pair<string,double>("x",y)); // 移动插入
map.emplace(7, "456");						  //构造插入
删除
map.erase(map.begin()+x);  //通过位置
map.erase("x");   		   //通过key
清空
map.clear();
查找
if(map.find("x") != map.end()){
	//查找成功
}else{
	//查找失败
}
if (map.count("x")) {
	//查找成功
}
int val = map["x"]; 
//直接使用key值访问键值对,如果没有访问到,返回0
遍历
map里面的数据类型是pair<T,P> p = {key,value}
//first 是map的key
//second 是map的value
for (auto& [key,value]: map)
cout << key << " "<< value << endl;

for(auto it=map.begin();it!=map.end();it++){

}

集合

c++ std中set与unordered_set区别和map与unordered_map区别类似:

set基于红黑树实现,红黑树具有自动排序的功能,因此map内部所有的数据,在任何时候,都是有序的。

unordered_set基于哈希表,数据插入和查找的时间复杂度很低,几乎是常数时间,而代价是消耗比较多的内存,无自动排序功能。底层实现上,使用一个下标范围比较大的数组来存储元素,形成很多的桶,利用hash函数对key进行映射到不同区域进行保存。

unordered_set<int,int> set;
插入
emplace
insert
push_back()
append()
删除
erase
pop_back()
查找
count
find
遍历
for (auto& x: set)
cout << x << endl;
自定义set排序

【StackOverflow】Using custom std::set comparator

Modern C++20

auto cmp = [](int a, int b) { return ... };
set<int, decltype(cmp)> sset;

Modern C++11
lambda表示式需要传入构造函数。

auto cmp = [](int a, int b) { return ... };
std::set<int, decltype(cmp)> sset(cmp);

Old solution使用struct的operator()
函数后面加const表示常量函数(const member function)。

关于C++ 的constant Member function 的讲解

  • const member function内不能修改成员变量(data member)。
  • const object只能调用const member function
struct cmp {
    bool operator() (int a, int b) const {
        return ...
    }
};
set<int, cmp> sset;

for循环

for-each

//for-each
for(auto c:S){
    ascii[c]++;
}

数组赋值memset

memset(A,0,sizeof(A));

键值对 pair

创建pair
//创建一个空的pair对象(使用默认构造),它的两个元素分别是T1和T2类型,采用值初始化。
pair<T1, T2> p1;
//创建一个pair对象,它的两个元素分别是T1和T2类型,其中first成员初始化为v1,second成员初始化为v2。
pair<T1, T2> p2(v1, v2);
新建pair
// 以v1和v2的值创建一个新的pair对象,其元素类型分别是v1和v2的类型。
pair<T1, T2> p3 = make_pair(v1, v2);
or
pair<T1, T2> p3 = {v1,v2};
访问pair
// 返回对象p1中名为first的公有数据成员
p.first;
// 返回对象p1中名为second的公有数据成员
p.second;
例子
//二维数组
vector<vector<int>> m;
queue<pair<int, int>> q;
q.push({ r0, c0 });
//m[i][0] = r0,m[i][1]= c0
//或者说 插入一个[r0,c0] , 或者说 插入一个 一维数组
//此时二维数组元素 [ [r0,c0] ]
m.push_back({ r0, c0 });

pair<int, int> cur = q.front();

//pair访问
int x1 = cur.first + x[j];
int y1 = cur.second + y[j];

tuple 元组

tuple的使用

vector<tuple<int, int, int, int>> ans;
ans.emplace_back(steps, grid[i][j], i, j);

创建一个tuple

# 1.
tuple<int,flaot,vector<int>> t;

# 2.
tuple<string,vector<double>,int> someVal("tuple",{2.14,3.15},100);

# 3.
tuple<int,int,double> someVal{2,3,3.15};

访问tuple

auto item = make_tuple("string",3,20.01);
auto book = get<1>(item);

查询tuple成员数量

auto sz = tuple_size<decltype(item)>::value;
//sz = 3

查询tuple成员类型

tuple_element<1,decltype(item)>::type ctype;
//ctype的类型为int

string 字符串

string 末尾不用加上’\0’

string::npos 表示 字符串末尾
基本操作
string s;
1)  s.empty();  // s为空串 返回true
2)  s.size();s.length();  // 返回s中字符个数 类型应为:string::size_type
3)  s[n];  // 从0开始相当于下标访问
4)  s1+s2;  // 把s1和s2连接成新串 返回新串 
5)  s1=s2;  // 把s1替换为s2的副本
6)  v1==v2;  // 比较,相等返回true
7)  `!=, <, <=, >, >=`  惯有操作 任何一个大写字母都小于任意的小写字母
8) s.push_back() //其他操作类似于vector
大小写转换
string str = 'Abc';
//转大写
transform(str.begin(), str.end(), str.begin(), toupper);
>>> str = 'ABC'
//转小写
transform(strA.begin(), strA.end(), strA.begin(), tolower);
substr求子串

pos:
Position of the first character to be copied as a substring.
If this is equal to the string length, the function returns an empty string.
If this is greater than the string length, it throws out_of_range.
Note: The first character is denoted by a value of 0 (not 1).

  • 返回的是原字符串的copy。
  • 从0开始数
  • 如果pos刚好【等于】字符串长度,返回空串。
  • 如果pos【大于】字符串长度,抛出out_of_rang异常。

len:
Number of characters to include in the substring (if the string is shorter, as many characters as possible are used).
A value of string::npos indicates all characters until the end of the string.

  • 如果len的长度【大于】剩下子串的长度,就全取。
  • string::npos表示 取剩下子串的全部。

size_t:
size_t is an unsigned integral type (the same as member type string::size_type).

  • 无符号整数。
string substr(size_t pos,size_t len) const
//第一个参数是index,第二个参数是子串长度
stinrg str = s.substr(index,len);
string => const char*
string str = "Hello World";
const char *ch1 = str.c_str();
const char *ch2 = str.data();
字符删除 erase
c++98
sequence (1)	string& erase (size_t pos = 0, size_t len = npos);
character (2)	iterator erase (iterator p);
range (3)	    iterator erase (iterator first, iterator last);
字符操作
//替换字符


字符串分割
s为原字符串,tokens为分割后字符串(需要传入),delimiters为分割符
void split(const string& s, vector<string>& tokens, const string& delimiters = " "){
    string::size_type lastPos = s.find_first_not_of(delimiters, 0);
    string::size_type pos = s.find_first_of(delimiters, lastPos);
    while (string::npos != pos || string::npos != lastPos) {
        tokens.push_back(s.substr(lastPos, pos - lastPos));//use emplace_back after C++11
        lastPos = s.find_first_not_of(delimiters, pos);
        pos = s.find_first_of(delimiters, lastPos);
    }
}
//示例:
string s = "a,b,c";
vector<string> tokens;
split(s,tokens,",");
for(auto &t:tokens){
	cout << t << " ";
}
>>> a b c
stringstream分词(单个空格)

c++ 之 std::move 原理实现与用法总结
move()它唯一的功能是将一个左值强制转化为右值引用,继而可以通过右值引用使用该值,以用于移动语义。

884. 两句话中的不常见单词

vector<string> uncommonFromSentences(string s1, string s2) {
	unordered_map<string, int> freq;
	
	//分割空格
	auto insert = [&](const string& str){
		stringstream ss(str); // ss << s;
		string buffer;
		while(ss >> buffer){
			++freq[move(buffer)];
		}
	}
	
	insert(s1);
	insert(s2);
	...
}

字符查找

C++ string查找子串位置

//返回str在字符串中第一次出现的位置(从index开始查找),如果没找到则返回string::npos
//string
size_type find( const basic_string &str, size_type index=0 );

//c-string
size_type find( const char *str, size_type index=0 ); // 同上

//返回str在字符串中第一次出现的位置(从index开始查找,长度为length),如果没找到就返回string::npos
//buffer
size_type find( const char *str, size_type index, size_type length );

// 返回字符ch在字符串中第一次出现的位置(从index开始查找),如果没找到就返回string::npos
//character 
size_type find( char ch, size_type index=0 );
字符串比较 compare
unsigned int val = a.compare(b);

//string (1)	
int compare (const string& str) const;

//substrings (2)	
int compare (size_t pos, size_t len, const string& str) const;
int compare (size_t pos, size_t len, const string& str,size_t subpos, size_t sublen) const;

//c-string (3)	
int compare (const char* s) const;
int compare (size_t pos, size_t len, const char* s) const;

//buffer (4)	
int compare (size_t pos, size_t len, const char* s, size_t n) const;
char* => int
const char *str1 = "3.14159";
int num1 = std::atoi(str1);
string => int, double, long, long long …
string s = "123";
int num1 = stoi(s);
double num2 = stod(s);
long num3 = stol(s);
long long num4 = stoll(s);
stod(string str,size_t idx)

str
String类型对象。

idx
size_t类型的指针。如果idx不为空,则
将idx设置为【数字字符串】之后的下一个字符。
所以,idx应该传指针类型,或者引用类型。


例子:

string orbits("365.24 29.53");
size_t sz;   //unsigned int64
double earth = stod(orbits,&sz);
cout << sz << orbits[sz] << 1 << endl;
double moon = stod(orbits.substr(sz));
cout << "The moon completes " << (earth/moon) << " orbits per Earth year.\n";

out:
6 1
The moon completes 12.3684 orbits per Earth year.
int => string
int num = 123;
string str = to_string(num);
char=>string
char ch = 'a'
string a(1, ch);
字符流 stringstream
#include <sstream>
stringstream stream;
string sint="1";
string sfloat="1.1";
string sdouble="1.2";
int dint;
float dfloat;
double ddouble;
//string to int
stream << sint;
stream >> dint;
//string to float
stream.clear(); //注意需要清空对象
stream << sfloat;
stream >> dfloat;
//string to double
stream.clear(); //注意需要清空对象
stream << sdouble;
stream >> ddouble;
判断字符串是否为字母 isalpha
isalpha(s)

大小写字母,返回true,其他返回false。

匿名函数

C++11提供了对匿名函数的支持,称为Lambda函数(也叫Lambda表达式). Lambda表达式具体形式如下:
     [capture](parameters)->return-type{body}
如果没有参数,空的圆括号()可以省略.
     [capture]->return-type{body}
如果没有返回值,返回值也可以省略,
     [capture](parameters){body}

//例子
[](int x, int y) -> int { int z = x + y; return z; }

Lambda函数可以引用在它之外声明的变量. 这些变量的集合叫做一个闭包. 闭包被定义在Lambda表达式声明中的方括号[]内. 这个机制允许这些变量被按值或按引用捕获.下面这些例子就是:

[]        //未定义变量.试图在Lambda内使用任何外部变量都是错误的.
[x, &y]   //x 按值捕获, y 按引用捕获.
[&]       //用到的任何外部变量都隐式按引用捕获
[=]       //用到的任何外部变量都隐式按值捕获
[&, x]    //x显式地按值捕获. 其它变量按引用捕获
[=, &z]   //z按引用捕获. 其它变量按值捕获

decltype关键字

有时我们希望从表达式的类型推断出要定义的变量类型,但是不想用该表达式的值初始化变量(如果要初始化就用auto了)。为了满足这一需求,C++11新标准引入了decltype类型说明符,它的作用是选择并返回操作数的数据类型,在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。

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;
}

decltype和auto都可以用来推断类型,但是二者有几处明显的差异:

  1. auto忽略顶层const,decltype保留顶层const;
  2. 对引用操作,auto推断出原有类型,decltype推断出引用;
  3. 对解引用操作,auto推断出原有类型,decltype推断出引用;
  4. auto推断时会实际执行,decltype不会执行,只做分析。

总之在使用中过程中和const、引用和指针结合时需要特别小心。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值