【c++】编程考试基础(上)

哈希表

一篇文章搞懂C++实现哈希算法
哈希表利用哈希函数将键(key)映射到表中一个位置上,以存储相应的值(value)。这允许快速的数据插入、查找和删除。理想情况下,哈希函数应该将键均匀分布在哈希表中,从而最小化键之间的冲突。
在C++中,std::unordered_map是一个基于哈希表实现的关联容器,它存储元素形成键值对。下面是如何使用std::unordered_map的一个简单例子:

#include <iostream>
#include <unordered_map>
#include <string>
 
int main() {
    // 创建一个unordered_map,键类型为std::string,值类型为int
    std::unordered_map<std::string, int> ageMap;
 
    // 插入数据
    ageMap["Alice"] = 30;
    ageMap["Bob"] = 25;
    ageMap["Charlie"] = 35;
 
    // 访问和输出Bob的年龄
    std::cout << "Bob's age is: " << ageMap["Bob"] << std::endl;
 
    // 检查一个键是否存在
    std::string key = "Dave";
    if (ageMap.find(key) == ageMap.end()) {
        std::cout << key << " not found in the map." << std::endl;
    } else {
        std::cout << key << "'s age is: " << ageMap[key] << std::endl;
    }
 
    // 删除一个元素
    ageMap.erase("Alice");
 
    // 遍历哈希表中的所有元素
    std::cout << "Current contents of the map:" << std::endl;
    for (const auto& pair : ageMap) {
        std::cout << pair.first << " is " << pair.second << " years old." << std::endl;
    }
 
    return 0;
}

使用哈希表的优势:

  • 高效性:哈希表的插入、删除和查找操作通常在常数时间内完成,适用于需要快速访问数据的应用场景。
  • 灵活性:哈希表可以存储各种类型的键值对,适应不同的数据处理需求。
  • 自动管理:哈希表会自动处理哈希函数调用和冲突解决,不需要手动管理。
  • 内置函数:std::unordered_map提供了丰富的成员函数,支持多种操作如查找、插入、删除、遍历等,简化了开发过程。

集合set

set容器中只能存储键,是单纯的键的集合,其中键是不能重复的。set支持大部分的map的操作。但两者也有区别。
1)set不支持下标的操作,而且没有定义mapped_type类型。
2)set的迭代器是const的,不允许修改元素的值;map允许修改value,但不允许修改key。set不能直接改变元素值,因为那样会打乱原本正确的顺序,要改变元素值必须先删除旧元素,则插入新元素;
3) set所得元素的只有key没有value,value就是key;map中的元素是key-value(关键字—值)对:关键字起到索引的作用;
set的基本操作总结

列表std::list

C++ STL标准库: std::list使用介绍、用法详解
模板类list是一个容器,list是由双向链表来实现的,每个节点存储1个元素。list支持前后两种移动方向。
优势: 任何位置执行插入和删除动作都非常快

list与vector的区别:

  • 在list的任何位置执行插入和移除都非常快.插入和删除动作不影响指向其它元素的指针,引用,迭代器,不会造成失效;
  • list不支持随机存取,不提供下标操作符和at()函数;
  • list没有提供容量,空间重新分配等操作函数,每个元素都有自己的内存;
  • list也提供了特殊成员函数,专门用于移动元素.

常用操作:

// 1、头文件
#include <list>   

// 2、常用定义
list<A> listname;
list<A> listname(size);
list<A> listname(size,value);
list<A> listname(elselist);
list<A> listname(first, last);

// 3、插入和删除
void push_front(const T& x);	// 头部添加
void push_back(const T& x);		// 尾部添加
void pop_front();		// 头部删除
void pop_back();		// 尾部删除

 // 在列表末尾插入元素
 mylist.push_back(1);

 // 在列表头部插入元素
 mylist.push_front(0);

 // 在第二个元素之后插入一个元素
 auto it = mylist.begin();
 std::advance(it, 2);  advance 的意思是增加的意思,就是相当于 it=it+2
 mylist.insert(it, 5);

 // 删除第二个元素
 it = mylist.begin();
 std::advance(it, 1);
 mylist.erase(it);

 // 删除所有值为3的元素
 mylist.remove(3);

// 4、遍历
begin()		// 返回指向容器中第一个元素的双向迭代器。
end()		// 返回指向容器中最后一个元素所在位置的下一个位置的双向迭代器。
rbegin()	// 返回指向最后一个元素的反向双向迭代器。
rend()		// 返回指向第一个元素所在位置前一个位置的反向双向迭代器。
cbegin()	// 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
cend()		// 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crbegin()	// 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crend()		// 	和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
//法一:
// 打印列表的元素
for (int n : mylist) {
    std::cout << n << '\n'; // 应该打印出 0, 1, 5
}
// 法二:
// 遍历显示
list<int>::iterator it = lst.begin();
for (it = lst.begin(); it != lst.end(); it++) {
	cout << *it << " "; // 输出:3 4 5 6
	cout << endl;
}


// 5、交换
list_One.swap(list_Two);  //交换两个容器的内容

// 6、排序
L1.sort();  //默认按从小到大排序
L1.sort(greater<int>()); //所有元素自动按从大到小排序
L1.reverse();  //反序排列

// 7、去重
L1.unique(); //去重

// 8、拼接
L1.merge(L2);  //list合并之后,所有元素自动按从小到大排序
void splice ( iterator position, list<T,Allocator>& x ); //将 list x 中的元素全都移到position处
void splice ( iterator position, list<T,Allocator>& x, iterator it ); //将 list x 中的由迭代器it指向的元素移到position处
void splice ( iterator position, list<T,Allocator>& x, iterator first, iterator last ); /将 list x 中的从迭代器 first 到迭代器 last 这一段元素移动到position处

// 9、重置
void assign(const_iterator first, const_iterator last);
void assign(size_type n, const T& x=T());
// 示例:
list_Two.assign(5,5.6);  //将list_Two的值设置为5个5.6
list_Three.assign(list_One.begin(),list_One.end());  // 将list_Three的值重置为list_One

C++ list容器的splice方法

注意!:list型容器不提供成员函数at()和操作符operator[],可以使用迭代器进行元素的访问.

#include <iostream>
#include <list>
#include <algorithm>
using namespace std;

// 打印队列所有元素
void print(list<double>& mylist)
{
	list<double>::iterator Iter;
	//mylist.reverse();
	for(Iter=mylist.begin();Iter!=mylist.end();Iter++)
	{   
		cout<<*Iter<<",  ";
	}
	cout<<endl;
}

void print(double& Ele)
{
	cout<<Ele<<" ,";
}

void main()
{
	// 初始化list
	list<double>mylist;
	mylist.push_back(11.1);
	mylist.push_back(21.5);
	mylist.push_back(31.6);
	mylist.push_back(41.7);

	int count=mylist.size();	// 获取大小
	for_each(mylist.begin(),mylist.end(),print); // 遍历打印
}

数组std::vector

C++ STL标准库:std::vector 使用详解
vector 是表示可以改变大小的数组的序列容器。

1.push_back 在数组的最后添加一个数据  //区别于list,没有首位的添加删除操作

2.pop_back 去掉数组的最后一个数据

3.at 得到编号位置的数据   // 此处区别于list

4.begin 得到数组头的指针

5.end 得到数组的最后一个单元+1的指针

6.front 得到数组头的引用

7.back 得到数组的最后一个单元的引用

8.max_size 得到vector最大可以是多大

9.capacity 当前vector分配的大小

10.size 当前使用数据的大小

11.resize 改变当前使用数据的大小,如果它比当前使用的大,者填充默认值

12.reserve 改变当前vecotr所分配空间的大小

13.erase 删除指针指向的数据项

14.clear 清空当前的vector

15.rbegin 将vector反转后的开始指针返回(其实就是原来的end-1)

16.rend 将vector反转构的结束指针返回(其实就是原来的begin-1)

17.empty 判断vector是否为空

18.swap 与另一个vector交换数据

vector的几种遍历方法如下:

#include <vector>
#include <iostream>
 
using namespace std;
 
struct Point
{
	double x;
	double y;
	Point()
	{
		x = 0;
		y = 0;
	}
};
 
 
int main()
{
	vector<Point> m_testPoint;
	m_testPoint.clear();
	m_testPoint.shrink_to_fit();
 
	for (int i = 0; i<10; ++i)
	{
		Point temp;
		temp.x = i*i;
		temp.y = i*i;
		m_testPoint.push_back(temp);
	}
 
	//第一种遍历方式,下标
	cout << "第一种遍历方式,下标访问" << endl;
	for (int i = 0; i<m_testPoint.size(); ++i)
	{
 
		cout << m_testPoint[i].x << "	" << m_testPoint[i].y << endl;
	}
 
	//第二种遍历方式,迭代器
	cout << "第二种遍历方式,迭代器访问" << endl;
	for (vector<Point>::iterator iter = m_testPoint.begin(); iter != m_testPoint.end(); iter++)
	{
		cout << (*iter).x << "	" << (*iter).y << endl;
	}
 
	//第三种遍历方式,auto关键字
	cout << "C++11,第三种遍历方式,auto关键字" << endl;
	for (auto iter = m_testPoint.begin(); iter != m_testPoint.end(); iter++)
	{
		cout << (*iter).x << "	" << (*iter).y << endl;
	}
 
	//第四种遍历方式,auto关键字的另一种方式
	cout << "C++11,第四种遍历方式,auto关键字" << endl;
	for (auto i : m_testPoint)
	{
		cout << i.x << "	" << i.y << endl;
	}
 
	return 0;
}

vector和list有什么区别?分别在什么场景下应用?

1、Vector:顺序表
优点:和数组类似开辟一段连续的空间,并且支持随机访问,所以它的查找效率高其时间复杂度O(1)。
缺点:由于开辟一段连续的空间,所以插入删除会需要对数据进行移动比较麻烦,时间复杂度O(n),另外当空间不足时还需要进行扩容。
2、List:链表
优点:底层实现是循环双链表,当对大量数据进行插入删除时,其时间复杂度O(1)
缺点:底层没有连续的空间,只能通过指针来访问,所以查找数据需要遍历其时间复杂度O(n),没有提供[]操作符的重载。
3、应用场景
vector拥有一段连续的内存空间,因此支持随机访问,如果需要高效的随即访问,而不在乎插入和删除的效率,使用vector。
list拥有一段不连续的内存空间,如果需要高效的插入和删除,而不关心随机访问,则应使用list。

sort的使用

sort(begin, end, cmp);

其中begin为指向待sort()的数组的第一个元素的指针,end为指向待sort()的数组的最后一个元素的下一个位置的指针,cmp参数为排序准则,如果没有的话,默认以非降序排序。
1.注意 sort 需要头文件 #include
2.如果想 sort 来降序,可重写 sort

#include <algorithm>  // sort 需要该头文件

bool compare(int a,int b) 
{ 
    return a< b; //升序排列,如果改为return a>b,则为降序 
} 
int a[20]={2,4,1,23,5,76,0,43,24,65},i; 
for(i=0;i<20;i++) 
    cout<< a[i]<< endl; 
sort(a,a+20,compare);

多关键字排序

可思考如下:
1、可用sort自定义比较函数实现;
2、set + pair实现;
3、优先队列+pair实现;

优先队列

c++优先队列(priority_queue)用法详解

//对于基础类型 默认是大顶堆,等同于下面priority_queue <int,vector<int>,less<int> >q;
priority_queue<int> a; 
//降序队列,大顶堆
priority_queue <int,vector<int>,less<int> >q;
//升序队列,小顶堆
priority_queue <int,vector<int>,greater<int> > q;

适用于以下问题:
1、求最大or最小值;
2、首、次优先级排序;// priority_queue<pair<int, int> > a;
3、自定义排序类型;

元组tuple(待补充)

tuple是一个固定大小的不同类型值的集合,是泛化的std::pair。我们也可以把他当做一个通用的结构体来用,不需要创建结构体又获取结构体的特征,在某些情况下可以取代结构体使程序更简洁,直观。std::tuple理论上可以有无数个任意类型的成员变量,而std::pair只能是2个成员,因此在需要保存3个及以上的数据时就需要使用tuple元组了。

链表

C++实现链表基本操作

字符串

字符串常用操作

参考链接

s.insert(pos, args)  	//在 pos 之前插入 args 指定的字符
s.erase(pos, len)  	//删除从 pos 开始的 len 个字符。如果 len 省略,则删除 pos 开始的后面所有字符。返回一个指向 s 的引用。
s.assign(args)  	//将 s 中的字符替换为 args 指定的字符。返回一个指向 s 的引用。
s.append(args)  	//将 args 追加到 s 。返回一个指向 s 的引用。args 必须是双引号字符串
s.replace(range, args) 	//将 s 中范围为 range 内的字符替换为 args 指定的字符
s.find(args) 	//查找 s 中 args 第一次出现的位置
s.rfind(args) 	//查找 s 中 args 最后一次出现的位置
to_string(val)	//将数值 val 转换为 string 并返回。val 可以是任何算术类型(int、浮点型等)
stoi(s) / atoi(c)	//字符串/字符 转换为整数并返回
stof(s) / atof(s)	//字符串/字符 转换为浮点数并返回
s.substr(pos, n)	//从索引 pos 开始,提取连续的 n 个字符,包括 pos 位置的字符
reverse(s2.begin(), s2.end())	//反转 string 定义的字符串 s2 (加头文件 <algorithm> )
 int toupper(int c)  //把小写字母转换为大写字母。

wstring和string区别

wstring是宽字符,占用2个字节的大小,针对UNICODE编码格式,用于对中文汉字的定义和赋值。wstring跟string区别为:字节不同、编码格式不同、使用不同。
一、字节不同
1、wstring:wstring是宽字符,占用2个字节的大小,即16bit。
2、string:string是窄字符,占用1个字节的大小,即8bit。
也就是说,宽字符,每表示一个字符其实是占了16bit,即2个char的大小。而汉字就是需要16bit来表示。

二、编码格式不同
1、wstring:wstring一般针对UNICODE编码格式,一个单元两个char。
2、string:string一般针对ASCII编码格式,一个单元一个char。

三、使用不同
1、wstring:在使用中文汉字时,使用wstring来定义变量进行赋值。
2、string:在使用英文汉字时,使用string来定义变量进行赋值。
参考链接

实战

判断宽字符串含有多个字串,获取其子串所有的索引位置

#include <iostream>
#include <string>
#include <vector>

int main() {
    std::wstring str = L"Hello, world! This is a test string.";
    std::wstring subStr = L"is";
    std::vector<int> indices;

    size_t pos = str.find(subStr);
    while (pos != std::wstring::npos) {
        indices.push_back(pos);
        pos = str.find(subStr, pos + 1);
    }

    std::cout << "Indices of \"" << subStr << "\" in \"" << str << "\": ";
    for (int i : indices) {
        std::cout << i << " ";
    }
    std::cout << std::endl;

    return 0;
}

计算纯文本长度,不包含换行符和空格

#include <iostream>
#include <string>

uint32_t GetSelectLengthOnlyText(const std::wstring& content)
{
    uint32_t length = 0;
    for (uint32_t i = 0; i < content.length(); i++) {
        if (content[i] != L' ' && content[i] != L'\n') {
            length++;
        }
    }
    return length;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值