1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
|
三种方法,这里总结如下:
1、**operator<() 仅适用于自定义结构体(operator()()重载后形参必须要有结构体)。operator<() 函数的添加可以从容修改自带排序功能的容器(set, priority_queue等)的比较规则,在定义该容器时只需set<T>或priority_queue<T>即可,不需要添加其他参数。在使用sort()函数时也不用指定比较函数。**
2、定义比较函数,可用于STL里sort()的第三个参数。
3、**operator()() 则适用于内建类型与自定义结构体(operator()()形参可以是内建数据类型)以及sort(),但需要类似这样定义容器set<T, cmp>或priority_queue<T, vector<T>, cmp>,其中cmp为仅包含operator()()函数的结构体。
4. 结构体,sort函数中的less算子和greater算子,优先队列的重载运算符
```
一、对于sotr函数等:
sort函数中默认是从小到大排序
1、自定义bool函数形式1(自定义比较函数)
bool cmp(const int &a,const int &b)
//此时重载<运算符,return false,所以a(小)排在b(大)的后面,为从小到大
{//此时a为别名
return a>b;//此时顺序为从大到小
} //自定义比较函数的形式同样也适用于pair类型数据排序。
此时sort函数里这么写sort(v.begin(),v.end(),cmp);
系统默认a<b时返回真,于是从小到大排,而我的cmp函数设定为a>b时返回为真,
那么最终得到的排序结果也相应的从小到大变成从大到小。
2、自定义bool函数形式2(自定义比较函数)
struct node{
int x, y;
};
struct cmp1{
bool operator()(node a, node b){
return a.x > b.x; //从大到小,优先队列中从小到大出队
}
}
struct cmp2{
bool operator()(node a, node b){//重载()相当于重载<
return a.x < b.x; //从小到大,优先队列中从大到小
}
}
int main(){
priority_queue<node, vector<node>, cmp1> que1; //小顶堆
priority_queue<node, vector<node>, cmp2> que2; //大顶堆
return 0;
}
二、对于结构体:
1、可以通过结构体外定义全局自定义比较函数进行比较,如:
struct ss
{
int a,b;
};
bool cmp(const ss &a,const ss &b)
{
return a.a<b.a; //最终结构体按照a的值从小到大排列
} // 修改为 return a.a>b.a; 就会从大到小排列
2、重载运算符<(这种方法不能应用于pair类型的,只能应用于结构体或类对象排序)
struct node{
int x, y;
};
bool operator <(const node& a, const node& b){
//const node &a不能少 ,此时()外不需要加const
return a.x > b.x; //重载为降序排列
}
三、对于priority_queue,****从队尾出队:
1、
priority_queue<int> q; //默认为less,小的优先级高,但是队尾出队,所以按照元素从大到小的顺序出队
priority_queue<int,vector<int>, greater<int> > q; //通过操作,按照元素从小到大的顺序出队
2、重载运算符< (运算符重载函数放到结构体外,同样重载运算符的操作不能用于pair类型数据的排序,
只能作用于结构体或类对象。)
struct node{
int x, y;
};
bool operator <(const node& a, const node& b){
return a.x > b.x;//这其实就是但会false
//less默认小顶堆,从小到大排,但是队尾先出队,所以从大到小出队,
//然后翻转为a.x > b.x(operator后面为<即为less),改为大顶堆,大的靠前(按从小到大的顺序)
}
//bool operator >(const node& a, const node& b){
// return a.x < b.x //greater默认小顶堆,改为大顶堆(从大到小)
//}
int main(){
priority_queue<node, vector<node>, less<node> > que;//因为less就是operator后面的<,从小到大出队
//priority_queue<node, vector<node>, greater<node> > que;//从大到小出队
return 0;
}
3、重载运算符< (运算符重载函数放到结构体内)
struct node{
int x, y;
bool operator <(const node& a) const { //必须加const
return x > a.x;//改为从小到大 出队 ,若放到结构体中就为从大到小排序
}
//bool operator >(const node& a) const { //必须加const
// return x < a.x; //从大到小顺序出队
//}
};
int main(){
priority_queue<node, vector<node>, less<node> > que;//从小到大
//priority_queue<node, vector<node>, greater<node> > que;//从大到小
return 0;
}
set、map的自定义比较函数和重载运算符与优先队列priority_queue类似。
set<int, greater<int> > st; //按照从大到小,默认是less<int>
typedef pair <int,int> P;
set<P> st; //按照pair的第一个元素来排,第一个相等的话按第二个来排
set<P, greater<P> > st;//按照从大到小
struct cow{
int x;
int y;
cow(int x,int y):x(x),y(y){}
bool operator<(const cow &b)const{
//bool operator<(const cow&a,const cow &b)const
//不能写为上面这一句,
if(x==b.x){
if(y<b.y){
return false;
}else{
return true;
}
}
else if(x<b.x){
return false;
}
else return true;
}
};
综述:类外定义自定义比较函数
bool cmp(const ss &a,const ss &b)
bool operator <(const node& a, const node& b){
//因为operator后面为<,所以默认为小顶推(从小到大)
都是a.x<b.x就为从小到大
a.x>b.x就为从大到小
}
优先队列
greater从大到小
less 从小到大(优先队列默认为less,sort函数等默认为less,但是队尾出队,所以大的先出队) 这个为小顶推
集合(queue,set等等)试图这两个元素x和y代入比较运算符(对于less算子(operator后面的<,返回结果为真,
就从小到大排列),调用x < y,对于greater算子(operator后面的>,返回结果为真就是从大到小)
,调用x > y),若返回结果为真,则x排在y前面
bool operator <(const node& a, const node& b){
//因为operator后面为< 所以为重载less算子
return a.x > b.x; //从大到小排列,优先队列中小值优先(从小到大出队)
return a.x < b.x; //从小到大排列,优先队列中大值优先(从大到小出队)
}
bool operator >(const node& a, const node& b){
//因为operator后面为>,所以为重载greater算子
return a.x < b.x //从小到大排列,优先队列中大值优先(从大到小出队)
return a.x > b.x //从大到小排列,优先队列中小值优先(从小到大出队)
}
bool operator <(const node& a) const { //因为在结构体内,必须加const
return x > a.x;//从大到小排列,优先队列中从小到大出队
}
struct cmp1{
bool operator()(node a, node b){//重载()相当于重载<
return a.x > b.x; //从大到小排列,优先队列中小值优先(从小到大出队)
return a.x < b.x; //从小到大排列,优先队列中大值优先(从大到小出队)
}
}
最大堆(大顶堆)-less算子 小的往前排(重载函数名中为<)-从小到大排; 优先队列从队尾出队,所以大的先出队
最小堆(小顶堆)-greater 大的往前排(重载函数名中为>) - 从大到小排
```
<h3 id="3"></h3>
##### 三,c语言的malloc和free函数
![upload successful](\aoyue\images\pasted-93.png)
![upload successful](\aoyue\images\pasted-95.png)
![upload successful](\aoyue\images\pasted-94.png)
-----
<h3 id="4"></h3>
在程序中数据是按字节的形式存储,在计算机底层,数据是按二进制的形式存储在计算机的物理设备上。
例: 字符串 ‘B’ 在内存中的存储形式为:
01111010
按照计算机编码规则,字符B首先需要转换为ASCII码,然后再转换为二进制,不足位数前面需补零。
**我们把二进制的运算称之为位运算。**
由于c语言拥有位运算的特性,所以我们可以采用位运算的特性从底层来操作计算机。
c语言位运算包括以下几种运算方式:
1: &(按位与) —注意逻辑与为两个&&
2: |(按位或) —注意逻辑或为两个||
3: ^(按位异或)
4: >>(二进制右移)
5: <<(二进制左移)
6: ~(按位取反) 参加位运算的类型必须为整型或字符型,如果所输入的类型不是这些类型,那么将遵循c语言一般转换规则
一、按位与计算
按位与同逻辑与有着类似之处,当两个位数都是1时,则得1 ,否则结果为0
例:
```
int a1=0x84;int a2=ox90;int a3;
a3 =a1&a2;
a3 =0x80;
将a1 a2 转换为二进制
a1
10000100 &
a2
10010000 =
a3
10000000
```
按位取与可以将两个数之间,只有一个为1的位数据清零。
二、按位或运算
按位或的功能就是当两个都为0,结果位0,否则为1;
例:
```
int a1=0x84;int a2=ox90;int a3;
a3 =a1|a2;
a3 =0x94;
将a1 a2 转换为二进制
a1
10000100
a2
10010000
a3
10010100
```
按位取或常用于:
加入我们需要保证一个数字的低4位为1时,我们可以采用一个低四位都位1的数同这个数字进行位或计算,就可以保证低四位全部为1。
三、按位异或
按位异或的功能就是,两个数的二进制位进行异或运算时,此时检测 一个为1 一个为0 得到结果为1,否则为0(即:两个数相同为1,不同值就为0)。
例:
```
int a1=0x84;int a2=ox90;int a3;
a3 =a1^a2;
a3 =0x14;
将a1 a2 转换为二进制
a1
10000100
a2
10010000
a3
00010100
```
按位异或可以用于按位取反操作。
a1 =0x84; a2=0x0F; a3=0x8C;
**根据按位异或的属性,我们可以看出一个数同0异或得到本身,与自身异或得到0;
在我们交换变量时**,我们就可以采用
int a1=0x85;int a2=0x90;
a2=(a1^a1)^a2;
a1=(a2^a2)^a1;
四、二进制右移运算
当二进制进行右移运算时,遵循以下规则:
1 对于无符号位的数据右移时,左侧的新位一律补0
2 对于有符号位的数据右移时,左侧的新位一律补1
对于移动一位相当于整除一个2,移动2位相当于整除两个2, 依次类推
五、二进制左移运算
二进制左移运算就是把数据向左移动,右侧新增加的位添0
例:
int a=8; a<<2 =32; 二进制 8 = 0000 1000;移动两位 :0010 0000 ; 从上例可以看出,向左移动多少位就是 当前数乘以2的多少位次方所得到的结果。
六、位运算项目应用
**6.1 验证奇偶**
能被2整除的数为偶数,反之则为奇数: 利用这一规则 我们可以验证 if(a&1){a为奇数}
**6.2 数据互换**
int swap(){
a=a^b;
a=a^a;
b=a^b;
}
![upload successful](\aoyue\images\pasted-111.png)
![upload successful](\aoyue\images\pasted-108.png)
-----
<h4 id="5">数据类型的范围</h4>
unsigned int 0~42,9496,7295
int(即int32,与long类型一样大小) -21,4748,3648~21,4748,3647 (21亿 1e9)
unsigned long 0~42,9496,7295 (42亿 1e9)
long -21,4748,3648~21,4748,3647
long long的最大值:922,3372,0368,5477,5807 (1e18)
long long的最小值:-922,3372,0368,5477,5808
unsigned long long的最大值:184,4674,4073,7095,5161
__int64的最大值:922,3372,0368,5477,5807 (1e18)
__int64的最小值:-922,3372,0368,5477,5808
unsigned __int64的最大值:1844,6744,0737,0955,1615
关于__int128的介绍:
128位的int类型的数,官方上写了GCC提供了两种128位整数类型,分别是__int128_t和__uint128_t,分别用于声明有符号整数变量和无符号整数变量。
由于这种大整数无法使用函数printf()输出其值,所以自己做了一个整数转字符串函数myitoa(),用于实现128位整数的输出。
详见大整数运算这篇文章
------------------------------------------------------------------
<h4 id="6">*p++ 和 ++*p</h4>
|