提示:由于篇幅较长,将会将大部分内容存入摘要里
一.fill函数
摘要
1、基本概念
fill函数是c++标准库algorithm头文件中的一个函数,用于填充一个区间为指定的值。
2、应用场景
1). 初始化数组
使用fill函数可以快速地初始化数组中的元素,例如:
int arr[10];
fill(arr, arr+10, 0); //将整个数组初始化为0
2). 填充容器
除了数组以外,fill函数还可以用于填充其他的容器,例如vector、list等。例如:
vector<int> vec(5); //创建一个含有5个元素的vector
fill(vec.begin(), vec.end(), 1); //将vector中的所有元素都赋值为1
3). 填充二维数组
fill函数也可以应用于填充二维数组,可以先使用数组指针指向二维数组,然后再使用fill函数进行填充。例如:
int arr[2][3];
int (*pArr)[3] = arr; //定义指向二维数组的指针
fill(pArr[0], pArr[0]+6, 0); //将整个二维数组初始化为0
3、注意事项
在使用fill函数时需要注意以下几点:
1. 该区间必须可修改
使用fill函数改变元素的值,必须确保该区间的元素是可修改的,否则将会引发未定义的行为。
2). 循环次数不要过大
使用fill_n函数填充较大区间时,循环次数可能会非常多,因此需要注意程序的效率。
3). 参数类型需匹配
使用fill函数时,传入的值和元素类型需匹配,否则可能会导致值截断或其他不可预期的错误。
4、示例代码
下面是一个使用fill函数初始化数组的示例代码:
void test_fill() {
int arr[10];
fill(arr, arr+10, 1); //将整个数组初始化为1
for(int i=0; i<10; i++) {
cout << arr[i] << " "; //输出1 1 1 1 1 1 1 1 1 1
}
}
下面是一个使用fill函数填充vector的示例代码:
void test_fill_vector() {
vector<int> vec(5);
fill(vec.begin(), vec.end(), 0); //将vector中的所有元素都赋值为0
for(int i=0; i<vec.size(); i++) {
cout << vec[i] << " "; //输出0 0 0 0 0
}
}
下面是一个使用fill_n函数填充数组的示例代码:
void test_fill_n() {
int arr[5];
fill_n(arr, 5, 3); //将整个数组初始化为3
for(int i=0; i<5; i++) {
cout << arr[i] << " "; //输出3 3 3 3 3
}
}
二.memset函数
摘要
memset简介
memset是一个初始化函数,作用是将某一块内存中的全部设置为指定的值
memset是一个初始化函数,作用是将某一块内存中的全部设置为指定的值。
1 void * memset ( void *s, int c, size_t n);
- s指向要填充的内存块。
- c是要被设置的值。
- n是要被设置该值的字符数。
- 返回类型是一个指向存储区s的指针。
需要说明的几个地方
一、不能任意赋值
memset函数是按照字节对内存块进行初始化,所以不能用它将int数组出初始化为0和-1之外的其他值(除非该值高字节和低字节相同)。
其实c的实际范围应该在0~255,因为memset函数只能取c的后八位给所输入范围的每个字节。也就是说无论c多大只有后八位二进制是有效的。
====================================================================
对于int a[4];
memset(a, -1, sizeof(a)) 与 memset(a, 511, sizeof(a)) 所赋值的结果一样都为-1:
因为 -1 的二进制码为(11111111 11111111 11111111 11111111);511 的二进制码为(00000000 00000000 00000001 11111111);
后八位均为(11111111),所以数组中的每个字节都被赋值为(11111111)。
注意int占四个字节,例如a[0]的四个字节都被赋值为(11111111),那么a[0](11111111 11111111 11111111 11111111),即a[0] = -1。
二、注意所要赋值的数组的元素类型
先来看两个例子:
例一:对char类型的数组a初始化,设置元素全为’1’
int main(){
char a[4];
memset (a, '1' ,4);
for ( int i=0; i<4; i++){
cout<<a[i]<< " " ;
}
return 0;
}

例二:对int类型的数组a初始化,设置元素值全为1
int main(){
int a[4];
memset (a,1, sizeof (a));
for ( int i=0; i<4; i++){
cout<<a[i]<< " " ;
}
return 0;
}

1、首先要说明的第一点
对于第二个程序,数组a是整型的,一般int所占内存空间为4个字节,所以在使用memset赋值时,下面的语句是错误的:
1
2 int a[4];
memset (a,1,4);
由于memset函数是以字节为单位进行赋值的,所以上述代码是为数组a的前4个字节进行赋值,那么所得到的执行结果就只能是:

正确的memset语句应为:
1
2 memset (a,1,16); //int所占内存为4字节的情况
memset (a,1, sizeof (a));
至于为什么不是预期得到的1,将在下面的第二点进行说明。
当然,不同的机器上int的大小可能不同,所以最好用sizeof()函数。
2、为什么第一个程序可以正确赋值1而第二个不可以?
这就又回到了刚刚说的第一个问题,memset函数中只能取c的后八位赋给每个字节。
- 第一个程序中,数组a是字符型的,字符型占据的内存大小就是1Byte,而memset函数也是以字节为单位进行赋值的,所以输出正确。
- 第二个程序中,数组a是整型的,整型占据的内存大小为4Byte,而memset函数还是按照字节为单位进行赋值,将1(00000001)赋给每一个字节。那么对于a[0]来说,其值为(00000001 00000001 00000001 00000001),即十进制的16843009。
关于所要赋值的字符数的写法
先来看一个示例:
#include<bits/stdc++.h>
using namespace std;
void fun1( int a[]){
memset (a,-1, sizeof (a));
}
int main(){
int a[6];
fun1(a);
for ( int i=0; i<6; i++){
cout<<a[i]<< " " ;
}
return 0;
}
当数组作为参数传递时,其传递的实际上是一个指针,这个指针指向数组的首地址,如果用sizeof(a)函数得到的只是指针的长度,而不是数组的长度。
解决方案:
在函数中加入数组长度参数,在传递前先获取数组长度,然后将数组长度作为参数传递进去。 #include<bits/stdc++.h>
using namespace std;
void fun1( int a[], int len){
memset (a,-1,len);
}
int main(){
int a[6];
int len = sizeof (a);
fun1(a,len);
for ( int i=0; i<6; i++){
cout<<a[i]<< " " ;
}
return 0;
}
具体用法实例
初始化数组
char str[100];
memset (str,0,100);
清空结构体类型的变量
typedef struct Stu{
char name[20];
int cno;
}Stu;
Stu stu1;
memset (&stu1, 0 , sizeof (Stu));
Stu stu2[10]; //数组
memset (stu2, 0, sizeof (Stu)*10);
此外,如果结构体中有数组的话还是需要对数组单独进行初始化处理的。
总结
memset函数在初始化处理时非常方便,但也有其局限性,比如要注意初始化数值,要注意字节数等等。当然,直接选择用for循环或while循环来进行初始化也是可以的,只不过memset更快捷一些。
还有亿些小细节:
摘要
部分转载于CSDN
话说刚开始使用memset的时候一直以为memset是对每一个int赋值的,心里想有了memset还要for循环对数组进行初始化干嘛。
但其实memset这个函数的作用是将数字以单个字节逐个拷贝的方式放到指定的内存中去
memset(dp,0,sizeof(dp));
int类型的变量一般占用4个字节,对每一个字节赋值0的话就变成了“00000000 00000000 000000000 00000000” (即10进制数中的0)
赋值为-1的话,放的是 “11111111 11111111 11111111 11111111 ”(十进制的-1)
这样你可能以为如果你赋值1的话会让整个dp数组里的每一个int变成1,其实不然。
memset(dp,1,sizeof(dp));
以上代码执行后,dp数组的内容为 00000001 00000001 00000001 00000001 转化为十进制后不为1
我们在很多程序中都会看到memset(a,127,sizeof(a));这样的代码
127是什么特别的数字呢?通过基础的进制转换可以得知127的二进制表示是01111111,那么在dp数组里放的内容就是“01111111 01111111 01111111 01111111”,(10进制的2139062143)
这样就实现了将数组里的全部元素初始化为一个很大的数的目的了,在最短路径问题以及其他很多算法中都是需要用到的。值得注意的是,int类型的范围为2^31-1,大约是2147483647的样子(如果我没有记错的话),所以初始化int类型的数组也可以使用127这个数值。
如果是128呢?因为128的二进制是10000000,那么放的内容就是10000000 10000000 10000000 10000000,经过计算可得这个数是-2139062144。这样就可以将数组初始化为一个很小的数了。
memset的正规用法是只能用来初始化char类型的数组的,也就是说,它只接受0x00-0xFF的赋值。
因为char是1字节,memset是按照字节赋值的,相当于把每个字节都设为那个数,所以char型的数组可赋任意值;
而对于也常用的int类型,int是4个字节,当memset(,1,sizeof());时,1相当于ASSCII码的1,1转为二进制00000001,当做一字节,一字节8位,int为4字节,所以初始化完每个数为00000001000000010000000100000001 = 16843009;
memset(,0xff,sizeof()),0xff转为二进制11111111,int为4字节所以最后为11111111111111111111111111111111为-1。(化为二进制补位,然后再赋值)。
可以全赋值为0,0的二进制位000000000000000000000000000000000,还可以是-1,-1的二进制就是11111111111111111111111111111111,所以memset可以直接初始化(0,-1);
例如:0xff转为二进制位11111111,正好是一位,0x1f小于0xff,而0x59也小于0xff,所以这些都可以用来初始化,只要能填满8位的二进制,就可以了。
如果你想初始最大化,第一位为符号位,不能为1,剩下全是1,也就是7个1,1111111化为十六进制正好为0x7f,所以memset(,0x7f,sizeof());就可以了
Memset中无穷大常量的设定技巧
如果问题中各数据的范围明确,那么无穷大的设定不是问题,在不明确的情况下,很多程序员都取0x7fffffff作为无穷大,因为这是32-bit int的最大值。
如果这个无穷大只用于一般的比较(比如求最小值时min变量的初值),那么0x7fffffff确实是一个完美的选择,但是在更多的情况下,0x7fffffff并不是一个好的选择。
很多时候我们并不只是单纯拿无穷大来作比较,而是会运算后再做比较,例如在大部分最短路径算法中都会使用的松弛操作:
if (d[u]+w[u][v]<d[v]) d[v]=d[u]+w[u][v];
我们知道如果u,v之间没有边,那么w[u][v]=INF,如果我们的INF取0x7fffffff,那么d[u]+w[u][v]会溢出而变成负数,我们的松弛操作便出错了,更一般的说,0x7fffffff不能满足“无穷大加一个有穷的数依然是无穷大”,它变成了一个很小的负数。
除了要满足加上一个常数依然是无穷大之外,我们的常量还应该满足“无穷大加无穷大依然是无穷大”,至少两个无穷大相加不应该出现灾难性的错误,这一点上0x7fffffff依然不能满足我们。
所以我们需要一个更好的家伙来顶替0x7fffffff,最严谨的办法当然是对无穷大进行特别处理而不是找一个很大很大的常量来代替它(或者说模拟它),但是这样会让我们的编程过程变得很麻烦。在我读过的代码中,最精巧的无穷大常量取值是0x3f3f3f3f,
0x3f3f3f3f的十进制是1061109567,也就是10^9级别的(和0x7fffffff一个数量级),而一般场合下的数据都是小于10^9的,所以它可以作为无穷大使用而不致出现数据大于无穷大的情形。
另一方面,由于一般的数据都不会大于10^9,所以当我们把无穷大加上一个数据时,它并不会溢出(这就满足了“无穷大加一个有穷的数依然是无穷大”),事实上0x3f3f3f3f+0x3f3f3f3f=2122219134,这非常大但却没有超过32-bit int的表示范围,所以0x3f3f3f3f还满足了我们“无穷大加无穷大还是无穷大”的需求。
最后,0x3f3f3f3f还能给我们带来一个意想不到的额外好处:如果我们想要将某个数组清零,我们通常会使用memset(a,0,sizeof(a))这样的代码来实现(方便而高效)
但是当我们想将某个数组全部赋值为无穷大时(例如解决图论问题时邻接矩阵的初始化),就不能使用memset函数而得自己写循环了(写这些不重要的代码真的很痛苦),我们知道这是因为memset是按字节操作的,它能够对数组清零是因为0的每个字节都是0。
现在好了,如果我们将无穷大设为0x3f3f3f3f,那么奇迹就发生了,0x3f3f3f3f的每个字节都是0x3f!所以要把一段内存全部置为无穷大,我们只需要memset(a,0x3f,sizeof(a))。
所以在通常的场合下,0x3f3f3f3f真的是一个非常棒的选择。
其他赋值:
memset(arr,0x7F,sizeof(arr)); //它将arr中的值全部赋为2139062143,这是用memset对int赋值所能达到的最大值
类似的还有:
memset(arr,0x80,sizeof(arr)); //set int to -2139062144
memset(arr,0x7F,sizeof(arr)); //set double to 1.38242e+306
memset(arr,0xFE,sizeof(arr)); //set double to -5.31401e+303
三.sort函数
摘要
版权声明:本文为CSDN博主「zhangbw~」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:C++ sort函数详解:使用、原理及案例解析-CSDN博客
C++ Sort函数详解
前言 :sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使用stable_sort函数,这里不过多介绍。
一、sort函数调用的两种方式
方式一(默认) void sort (a first, a last);
方式二(自定义) void sort (a first, a last, Compare comp);
默认: 两个参数first,last,将[first, last)区间内元素升序排列。【注意区间为左闭右开】
自定义排序: 需用户指定排序规则Compare comp,将 [first, last)区间内的元素按照用户指定的顺序排列。
二、sort函数使用场景
由于在排序过程中涉及到元素交换等操作,所以sort函数仅支持可随机访问的容器,如数组, string、vector、deque等。
三、sort函数排序原理
sort()并非只是普通的快速排序,除了对普通的快速排序进行优化,它还结合了插入排序和堆排序。
根据不同的数量级别以及不同情况,能自动选用合适的排序方法。当数据量较大时采用快速排序,分段递归。
一旦分段后的数据量小于某个阀值,为避免递归调用带来过大的额外负荷,便会改用插入排序。
而如果递归层次过深,有出现最坏情况的倾向,还会改用堆排序。
所以无论元素初始时为何种状态,sort()的平均排序复杂度为均为O(N*log2(N)) ,具有不错的的性能,在刷算法题时,可以直接使用sort()来对数据进行排序,而不需手动编写排序函数。
四、sort函数使用案例
1.升序排列
sort函数如果不传入第三个参数,则默认是升序排列。
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main() {
// 方式一、使用数组
int a[10] = {9, 6, 3, 8, 5, 2, 7, 4, 1, 0};
sort(a, a + 10); // 10为元素个数
for (int i = 0; i < 10; i++) cout << a[i] << ' '; // 输出排序后数组
cout << endl;
// 方式二、使用 vector
vector<int> arr = {9, 6, 3, 8, 5, 2, 7, 4, 1, 0};
sort(arr.begin(), arr.end()); // 10为元素个数
for (int i = 0; i < 10; i++) cout << arr[i] << ' '; // 输出排序后数组
return 0;
}
2.降序排列
实现方式1
实现降序排列,需传入第三个参数–比较函数,greater(),这里的元素为int 类型,即函数为 greater(); 如果是其他基本数据类型如float、double、long等也是同理。
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main() {
// 方式一、使用数组
int a[10] = {9, 6, 3, 8, 5, 2, 7, 4, 1, 0};
sort(a, a + 10, greater<int>()); // 10为元素个数
for (int i = 0; i < 10; i++) cout << a[i] << ' '; // 输出排序后数组
cout << endl; // 输出 9 8 7 6 5 4 3 2 1 0
// 方式二、使用 vector
vector<int> arr = {9, 6, 3, 8, 5, 2, 7, 4, 1, 0};
sort(arr.begin(), arr.end(), greater<int>());
for (int i = 0; i < 10; i++) cout << arr[i] << ' '; // 输出排序后数组
return 0;
}
实现方式2
我们也可以使用自定义的比较函数,函数的返回值为bool类型, 例如
bool cmp(int num1, int num2) {
return num1 > num2; // 可以简单理解为 > 降序排列; < 升序排列
}
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
bool cmp(int num1, int num2) {
return num1 > num2; // 可以简单理解为 >: 降序排列; < : 升序排列
}
int main() {
// 一、使用数组
int a[10] = {9, 6, 3, 8, 5, 2, 7, 4, 1, 0};
sort(a, a + 10, cmp); // 使用自定义排序函数
for (int i = 0; i < 10; i++) cout << a[i] << ' '; // 输出排序后数组
cout << endl; // 输出 9 8 7 6 5 4 3 2 1 0
// 二、使用 vector
vector<int> arr = {9, 6, 3, 8, 5, 2, 7, 4, 1, 0};
sort(arr.begin(), arr.end(), cmp); // 使用自定义排序函数
for (int i = 0; i < 10; i++) cout << arr[i] << ' '; // 输出排序后数组
return 0;
}
3.结构体排序(自定义比较函数)
要对元素进行排序,前提是元素之间可以进行比较,即谁大谁小。 基本数据类型可直接进行大小比较, 但结构体元素之间的大小关系需要我们自己指定,如果不指定,则结构体之间大小关系就不确定,则不能够排序。
结构体排序案例1: 对学生信息进行排序
学生有姓名,分数两个属性,
struct Student { // 学生结构体
string name; // 学生姓名
int grade; // 学生分数
Student(); // 无参数构造函数
Student(string name, int grade) : name(name), grade(grade) {}; // 有参数构造函数
};
需求: 对一个班级内的学生成绩进行排序,首先按成绩进行排序降序排列,若成绩相同,则按照姓名字典顺序升序排列。
自定义排序函数
bool cmp(Student s1, Student s2) { // 自定义排序
if (s1.grade != s2.grade) { // 如果学生成绩不相同
return s1.grade > s2.grade; // 则按照成绩降序排列
}
return s1.name < s2.name; // 否则按照姓名升序排列
}
排序代码
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct Student { // 学生结构体
string name; // 学生姓名
int grade; // 学生分数
Student(); // 无参数构造函数
Student(string name, int grade) : name(name), grade(grade) {}; // 有参数构造函数
};
bool cmp(Student s1, Student s2) { // 自定义排序
if (s1.grade != s2.grade) { // 如果学生成绩不同
return s1.grade > s2.grade; // 则按照成绩降序排列
}
return s1.name < s2.name; // 否则按照姓名升序排列
}
int main() {
vector<Student> studs;
studs.emplace_back("Bob", 80);
studs.emplace_back("Ali", 90);
studs.emplace_back("Ann", 85);
studs.emplace_back("Liming", 90);
studs.emplace_back("Trump", 79);
studs.emplace_back("Fury", 58);
studs.emplace_back("Jam", 62);
studs.emplace_back("Lucy", 89);
sort(studs.begin(), studs.end(), cmp); // 排序
for (int i = 0; i < studs.size(); i++) { // 输出结果
cout << studs[i].name << "\t" << studs[i].grade << endl;
}
return 0;
}
五、自定义comp函数返回true或false作用
bool cmp(int num1, int num2) { // 实现降序排列
return
num1 > num2; // num1大于num2时返回true,否则返回false
}
自定义函数返回值为bool类型
若返回true,则表示num1 与num2应该交换顺序;
若返回false, 则num1 与num2 保持原有顺序;
下面举例说明自定义比较函数的执行过程。
对 2, 5, 1, 3, 4 降序排列
调用cmp函数时,将5赋值给num1, 2赋值给num2 (注意顺序)
5 > 2, 返回true,num1 与 num2需进行交换;即5应该在2的前面
数组变为 5, 2, 1, 3, 4
第二次 将3赋值给num1, 1赋值给num2,
3 > 1, 返回true,num1 与 num2需进行交换;即3应该在1的前面
数组变为 5, 2, 3, 1, 4
之后经过数次的比较与交换最终排序完成。
最终得到 5 4 3 2 1
只是蒟蒻随便做的一个总结,将会持续更新
有一部分内容转载于其他网站 (勿喷
如有错误或者要补充的
欢迎各位Dalao在下方发表意见(悬赏一个解决方案哦
制作不易给蒟蒻点个赞吧
