考研算法实现.md
王道数据结构算法实现
第一章
顺序表
1 |
|
链表
单链表
单链表可以想象成铁链,一环扣一环,下面是代码实现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
using namespace std;
typedef struct listnode{
int data;
listnode *next;
}lnode,*linklist;//lnode 表示结点,linklist表示链表
//头插法创建链表
linklist list_head_insert(linklist& head) //常规头插法构建链表顺序与输入顺序相反
{
cout<<"使用头插法构建数组"<<endl;
lnode* s;
int n = 1;
head = new lnode;
head->next = nullptr;
cout<<"请输入链表的第"<<n<<"个值:";
int x;
cin>>x;
while(x!=-1)
{
n++;
s =new lnode;
s->data = x;
s->next = head->next; //新节点下一个指向原本的开端使其成为新的开端
head->next = s; //头节点再指向新节点完成添加
cout<<"请输入链表的第"<<n<<"个值:";
cin>>x; //保持循环
}
return head;
}
//尾插法构建链表
linklist list_tail_insert(linklist &a)
{
cout<<"使用尾插法构建数组"<<endl;
int x,n =1;
a = new lnode;
lnode *s,*r = a;//r是尾指针方便插入
cout<<"请输入链表的第"<<n<<"个值:";
cin>>x;
while(x!=-1)
{
n++;
s = new lnode;
s->data = x;
r->next =s;
r = s;//r指向新表尾节点
cout<<"请输入链表的第"<<n<<"个值:";
cin>>x;
}
r->next = nullptr;
return a;
}
//打印链表
void print_list(linklist cur)
{
if(!cur->next)
{
cout<<"链表为空"<<endl;
return;
}
cout<<"当前链表为:";
while (cur->next)
{
cout<<cur->next->data<<" ";
cur = cur->next;
}
cout << endl;
}
//按序号查找节点
void getelem_byid(linklist a)
{
cout<<"输入要查找的序号:";
int x;
cin>>x;
if(x<1)
{
cout<<"错误";
return;
}
int j =1;
lnode *p = a->next;
cout<<j<<"号元素:"<<p->data<<endl;
while(p&&j<x)
{
p = p->next;
j++;
}
cout<<"找到了"<<j<<"号元素:"<<p->data<<endl;
}
//按值查找元素
void getelem_byval(linklist l)
{
cout<<"输入要查找的值:";
int x;
cin>>x;
int cnt =0;
lnode *p=l->next;
while(p)
{
cnt++;
if(p->data==x)
{
cout<<"第"<<cnt<<"号元素"<<p->data<<endl;
break;
}else
{
p = p->next;
}
}
}
//插入节点
void insert(linklist &l)
{
cout<<"请输入插入位置:";
int x;
cin>>x;
if(x<1)//查询值的操作可以封装但是为了方便有很多输出的语句,避免太乱就再写一次
{
cout<<"error";
return;
}
lnode* p = l->next;
int cnt =1;
while(cnt<x) //查找插入位置,由于是往后插入所以插在第i个位置要找到第i-1个节点
{
p = p->next;
cnt++;
}
lnode* s= new lnode;
cout<<"插入的值:";
cin>>s->data;
s->next = p->next;//插入的关键,画图好理解,不能先让p下一个指向s否则会让原本p后的数据丢失
p->next = s;
print_list(l);
}
//删除节点
void delete_node(linklist &l)
{
cout<<"请输入删除位置:";
int x;
cin>>x;
if(x<1)
{
cout<<"error";
return;
}
lnode* p = l->next;
int cnt =1;
while(cnt<x)
{
p = p->next;
cnt++;
}
lnode* q = p->next; //找到要删除的节点q
p->next = q->next; //让q的前驱节点直接指向q的下一个节点完成删除
delete(q);//别忘了释放q
print_list(l);
}
//求表长
void list_len(linklist l)
{
int cnt =0;
while(l->next)
{
l = l->next;
cnt++;
}
cout<<"表长:"<<cnt<<endl;
}
int main()
{
linklist a;
a =list_head_insert(a);
print_list(a);
//cout<<a->data<<endl;
//getelem_byval(a);
//insert(a);
//delete_node(a);
list_len(a);
delete(a);
return 0;
}
双链表
这里就直接用csdn上一位大神的了,具体和单链表比较就只有插入和删除需要注意。
1 | /* |
链表习题实现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
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478class exercises{
public:
//1,简单递归
void one(linklist &l,int x)
{
lnode* p;
if(l==nullptr) return;
if(l->data==x)
{
p =l;
l=l->next;
delete(p);
one(l,x);
}else one(l->next,x);//l-》data不是x的情况
}
//2循环删除值直至末尾
void two(linklist& l,int x)
{
lnode* p,*pre = l,*q;//pre是p的前驱节点
p=l->next;
while(p)
{
if(p->data==x)
{
q = p;
p = p->next;
pre->next =p;//此时前驱节点是q的前驱节点,直接指向p删除来q
delete q;
}else
{
pre = p;
p = p->next;
}
}
}
//3,方法很多,遍历链表用容器如栈等储存在输出即可,也可以反转链表再来输出,王道上使用了递归
void three(linklist l)
{
if(l->next)
three(l->next);//递归会先执行这里,直至不符合条件再从内向外输出,就像无限套娃一样直到最后一层再一次返回输出每一层结果
if(l)
cout<<l->data<<" ";
}
void three_ignore_head(linklist l)
{
if(l->next) three(l->next);
}
//4,用节点记录最小值的前驱节点,遍历链表找到后删除
void four(linklist &l)
{
lnode *pre=l,*p =pre->next;
lnode *min_pre =pre,*minp = p;//存储最小前驱和节点
while(p)
{
if(p->data<minp->data)
{
minp = p;
min_pre = pre;
}
pre = p;//不断更新
p = p->next;
}
min_pre->next = minp->next;
delete minp;//完成节点删除
}
//5法一是将头节点摘下再将原本的节点依次尾插头节点后
void five_1(linklist& l)
{
lnode *p,*r;
p = l->next;
l->next = nullptr;//摘下头节点
while(p)
{
r = p->next;//取到插入节点
p->next = l->next;//将节点插入头节点后
l->next = p;//完成插入连接
p =r;//更新下一个节点
}
}
//法2,记录前驱节点,让p指向前驱节点同时让r = p->next 记录下一个处理的节点
void five_2(linklist& l)
{
lnode* pre,*p = l->next,*r = p->next;
p = nullptr;
while(r)
{
pre = p;
p =r;
r =r->next;
p->next = pre;
}
l->next = p;//处理最后一个节点
}
// 6 贪心,对每次节点选择插入位置
void six(linklist &l)
{
lnode *p = l->next,*pre;
lnode *r = p->next; //保证不断链
p->next = nullptr;
p = r;
while(p)
{
r = p->next;
pre = l;
while(pre->next&&pre->next->data<p->data)
pre = pre->next;//遍历查找需要插入*p的前驱节点
p->next = pre->next;
pre->next = p;
p =r;
}
}
//7 类似删除特定接节点
void seven(linklist& l,int a,int b)
{
lnode *p = l,*r = l->next; //p是前驱节点,r是处理节点
while(p)
{
if(r->data>=a&&r->data<=b)
{
p->next = r->next;
delete r;
r = p->next;
}else
{
p = r;
r = r->next;
}
}
}
//8可以暴力,王道上用数学思想,计算两条链表长度只差,再让较长的一段链表遍历先走长度之差次,然后一起遍历如果末尾相同则表明有共同域。
//共同域是指只要某一节点相同则后所有节点都相同。
int length(linklist&a)
{
int cnt =0;
while(a)
{
cnt++;
a = a->next;
}
return cnt;
}
bool eight(linklist&a,linklist&b)
{
int dist;
int len1 = length(a),len2 =length(b);//计算长度
linklist long_list,short_list;
if(len1>len2)
{
long_list = a->next,short_list = b->next;
dist = len1-len2;
}
else
{
long_list = b->next,short_list = a->next;
dist = len2-len1;
}
while(dist--)
{
long_list = long_list->next;//让长的遍历到剩余结点和短的一样
}
while(long_list)
{
if(long_list==short_list)
return true;
else{
long_list = long_list->next;
short_list = short_list->next;
}
}
return false;
}
//9,直接暴力
void nine(linklist&l)
{
while(l->next)
{
lnode *pre = l;
lnode *p = pre->next;
lnode *u;//记录要删除的节点
while(p->next)
{
if(p->next->data<pre->next->data)
pre = p;
p = p->next;
}
cout<<pre->next->data;
u = pre->next;
pre->next = u->next; //删除节点
delete u;
}
delete l;//释放头节点
}
//10,以前力扣上好像做过原题,双指针
void ten(linklist &a)
{
int i =0;
linklist b = new lnode;
b->next = nullptr;
lnode* ra = a,*rb = b ,*p;//p是工作指针
p = a->next;
a->next = nullptr; //清空a表
while(p)
{
i++;
if(i%2==0)
{
rb->next = p;
rb = p;
}else
{
ra->next = p;
ra = p;
}
p = p->next;
}
ra->next = nullptr;
rb->next = nullptr;
}
//11,可以用10题的思想,区别就是对b用头插法
void eleven(linklist& a)
{
linklist b =new lnode;
b->next = nullptr;
lnode *p = a->next,*q;
lnode* ra = a;
while(p)
{
ra->next = p;
ra = p;
if(p)
{
q = p->next;//头插法会使p断链,因此使用q记录
p->next = b->next;
b->next = p;
p = q;
}
}
ra->next = nullptr;
}
//12,有序表去重,思路就是下一个节点的值和当前节点相同就删除
void twelve(linklist &l)
{
lnode *p =l->next,*q;
if(!p) return;
while(p->next)
{
q = p->next;
if(p->data==q->data)
{
p->next = q->next;
delete q;
}
else p = p->next;
}
}
//13 将两条递增链表递减归并,用头插法
void num13(linklist&la,linklist&lb)
{
lnode *r,*pa = la->next,*pb = lb->next;
la->next = nullptr;
while(pa&&pb)
{
if(pa->data<=pb->data)
{
r = pa->next; //暂存插入的节点的后续,头插法会导致断链
pa->next = la->next;
la->next = pa;
pa =r;//恢复
}
else
{
r = pb->next;
pb->next = la->next;
la->next = pb;
pb =r;
}
}
if(pa)
pb = pa;//剩下只有一条时,直接插入即可
while(pb)
{
r = pb->next;
pb->next = la->next;
la->next = pb;
pb =r;
}
delete lb;
}
//14还是类似双指针,较小指针往前移动
void num14(linklist& a,linklist&b)
{
lnode *p=a->next,*q = b->next,*r,*s;
linklist c =new lnode;
r = c;
while(p&&q)
{
if(p->data<q->data)
p = p->next;
else if(p->data>q->data)
q = q->next;
else//等于的情况
{
s = new lnode;
s->data = p->data;
r->next = s;
r = s;
p = p->next;
q = q->next;
}
}
r->next = nullptr;
}
//15,思路类似上题,归并双指针
void num15(linklist &la,linklist&lb)
{
lnode* pa =la->next,*pb = lb->next;
lnode* u ,*pc = la;
while(pa&&pb)
{
if(pa->data==pb->data)
{
pc->next = pa;
pc = pa; // 往后移动pc指向
pa = pa->next;
u = pb;
pb = pb->next;
delete u;
}
else if(pa->data<pb->data)
{
u = pa;
pa = pa->next;
delete u;
}
else
{
u = pb;
pb = pb->next;
delete pb;
}
while(pa)
{
u = pa;
pa = pa->next;
delete u;
}
while(pb)
{
u = pb;
pb = pb->next;
delete u;
}
pc->next = nullptr;
delete lb;
}
}
//16 找到 a中等于b1的元素开始判断,如果直至b末尾都相同则是子链
bool num16(linklist a,linklist b)
{
lnode *p = a;
lnode *pre = p,*q = b;
while(p&&q)
{
if(p->data==q->data)
{
p = p->next;
q = q->next;
}
else
{
pre = pre->next;
p = pre; //p是记录a中即将匹配的新节点
q = b; //q从头开始
}
if(!q)
return true;
else return false;
}
}
//17,双链表一个往前遍历一个往后遍历
bool num17(dlinklist l)
{
dnode *p = l->next,*q = l->prior;
while(p!=q&&q->next!=p)//切记不能写成p->next !=q,这会导致匹配最后一个时出错
{
if(p->data==q->data)
{
p = p->next;
q = q->next;
}
else return false;
}
return true;
}
//18画图理解方便,先让第一个链表尾指针指与第二个链表头节点项链再使其循环
void num18(linklist &h1,linklist &h2)
{
lnode *p,*q;
p = h1;
while(p->next!=h1)
p= p->next;//找到尾节点
q = h2;
while(q->next!=h2)
q = q->next;
p->next = h2; //第一个链表的尾巴连接到第二个头结点上
q->next = h1; //第二个链表尾巴连到第一个头上
}
//19,暴力
void num19(linklist &l)
{
lnode *p,*pre,*minp,*minpre;
while(l->next!=l)
{
p = l->next;pre = l;
minp = p;minpre = pre;
while(p!=l)
{
if(p->data<minp->data)
{
minp = p;
minpre = pre;
}
pre = p;
p = p->next;
}
cout<<minp->data;
minpre->next = minp->next;
delete minp;
}
delete l;//结束删除头节点
}
//20 多加了个频度元素不好写,就不写了
//21 力扣上好像写过,快慢指针,一个每次前进两步,一个每次前进一步。如果不含有环,跑得快的那个指针最终会遇到 null,说明链表不含环;如果含有环,快指针最终会超慢指针一圈,和慢指针相遇,说明链表含有环。
lnode* num21(linklist &head)
{
lnode *fast = head,*slow = head;
while(fast&&fast->next)
{
slow=slow->next;
fast = fast->next->next;//快的走两步
if(slow==fast) break;
}
if(!fast||!fast->next) //没有环的情况
{
return;
}
lnode *p1 =head,*p2 = slow;
while(p1!=p2)
{
p1 =p1->next;
p2 = p2->next;
}
return p1;
}
};
第二章
栈
普通栈的简单实现
1 |
|
链栈
和头插法的单链表差不多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//链栈的结构
typedef struct linkstack
{
int data;
struct linkstack* next;
}stacknode, * lstack;
//链栈功能实现
class link_stack
{
public:
void output(lstack l)
{
stacknode* p = l;
if (!p)
{
cout << "链栈为空!" << endl;
return;
}
cout << "链栈从顶往下为:";
while (p)
{
cout << p->data << " ";
p = p->next;
}
cout << endl;
}
void init(lstack &l)
{
l=NULL;//不能为l = nullptr
}
//链栈的入栈
void push(lstack& l)
{ //在栈顶插入元素e,链栈不需要判断栈满
stacknode* p = new stacknode;
cout<<"输入要插入的值:"<<endl;
int x;
cin>>x;
p->data = x;
p->next = l;
l = p;
}
//构建链栈
void create(lstack &l)
{
init(l);
cout<<"输入创建长度:";
int n;
cin>>n;
for(int i =0;i<n;i++)
{
push(l);
}
output(l);
}
//出栈元素
void drop_top(lstack &l)
{
if(!l)
{
cout<<"为空"<<endl;
return;
}
//直接删除就好了
stacknode *p;
p = l;
l = l->next;
cout<<"出栈,删除栈顶元素:"<<p->data<<endl;
delete p;
output(l);
}
//查询栈顶元素
void get_top(lstack l)
{
if(!l)
{
cout<<"为空"<<endl;
return;
}
cout<<"当前栈顶元素:"<<l->data<<endl;
}
};
课后习题
1 | //练习题 |
队列
循环队列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
using namespace std;
//队列结构
const int max_size = 10;
typedef struct {
int data[max_size];
int front,rear;//头指针和尾指针
int length;//长度,不一定会用
}queue;
void init(queue &q)
{
q.front = 0,q.rear = 0;
}
//判断队空
bool empty(queue q)
{
return (q.front==q.rear);
}
//打印队列
void print_queue(queue q)
{
if(empty(q))
{
cout<<"队列为空"<<endl;
return;
}
cout<<"队列元素:";
while(q.rear-q.front>0)
{
cout<<q.data[q.front]<<" ";
q.front++;
}
cout<<endl;
}
//入队
void push(queue &q)
{
if((q.rear+1)%max_size == q.front)
{
cout<<"队满"<<endl;
return ;
}
cout<<"输入要插入的元素:";
int x;
cin>>x;
q.data[q.rear] = x;
q.rear = (q.rear+1)%max_size;
}
//出队
void pop(queue &q)
{
if(empty(q))
{
cout<<"队列为空"<<endl;
return;
}
cout<<"删除队头元素:"<<q.data[q.front]<<endl;
q.front = (q.front+1)%max_size;
}
int main()
{
queue q;
init(q);
cout<<"输入要创建的数量";
int n;
cin>>n;
for(int i =0;i<n;i++)
{
push(q);
}
print_queue(q);
pop(q);
print_queue(q);
return 0;
}
链队实现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//链队
typedef struct linkqueue
{
int data;
struct linkqueue *next;
}qnode;
//存放指针
typedef struct
{
linkqueue *front,*rear;
}*lqueue;
class lq
{
public:
void init(lqueue &a)
{
a->front = a->rear = new linkqueue;
a->front->next = NULL;
}
//判断空
bool empty(lqueue a)
{
return (a->front==NULL&&a->rear==NULL);
}
//打印节点
void print_queue(lqueue a)
{
if(empty(a))
{
cout<<"链队为空"<<endl;
return;
}
qnode *p = a->front->next;
cout<<"链队为:";
while(p)
{
cout<<p->data<<" ";
p = p->next;
}
cout << endl;
}
//入队
void push(lqueue &a)
{
qnode *p = new linkqueue;
cout<<"输入入队的值:";
int x;
cin>>x;
p->data = x;
p->next = nullptr;
a->rear->next = p;
a->rear = p;
}
//出队
void pop(lqueue &a)
{
if(empty(a))
{
cout<<"链队为空"<<endl;
return;
}
qnode *p = a->front->next;
cout<<"删除队头:"<<p->data<<endl;
a->front->next = p->next;//像链表一样删除
//注意删除是尾节点的时候
if(a->rear == p)
a->rear = a->front;
delete p;
}
//清空队列释放空间
void delete_q(lqueue &a)
{
while(a->front)
{
a->rear = a->front;
delete a->front;
a->front = a->rear;
}
}
};
队列课后习题
1 | class exercise |
栈的应用
中缀转后缀计算机运算思想:
从左到右开始扫描中缀表达式
遇到数字, 直接输出
遇到运算符
a.若为“(” 直接入栈
b.若为“)” 将符号栈中的元素依次出栈并输出, 直到 “(“, “(“只出栈, 不输出
c.若为其他符号, 将符号栈中的元素依次出栈并输出, 直到遇到比当前符号优先级更低的符号或者”(“。 将当前符号入栈。
扫描完后, 将栈中剩余符号依次输出
1 |
|
中缀转后缀的运算就和Acwing基础课的栈里差不多了.
第三章
串
串的具体实现不在考纲里,实现代码就抄其他大神的了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
using namespace std;
//定长字符串
struct staticString {
char str[MAXSIZE + 1];
int length;
};
//考研常考变长字符串
struct variableString {
char *str;
int length;
};
//串赋值
bool variableStringAssign(variableString &variableStr, char *ch);
//获取串长度
int getLength(variableString variableString);
//串比较
int stringCompare(variableString variableString1, variableString variableString2);
//串拼接
bool concat(variableString &result, variableString variableString1, variableString variableString2);
//取子串
bool subString(variableString &result, variableString variableString1, int from, int length);
//清空串
bool clean(variableString &variableString1);
int main() {
variableString string1{};
//串赋值
char ch[MAXSIZE] = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\0'};
variableStringAssign(string1, ch);
for (int i = 0; i < string1.length; ++i) {
cout << string1.str[i];
}
cout << endl;
//串比较
variableString string2{};
string2.str = ch;
string2.length = string1.length;
//向string1中添加字符进行测试
// string1.str[string1.length] = 'a';
// string1.str[++string1.length] = '\0';
if (stringCompare(string1, string2) == 0) {
cout << "string1 equals string2" << endl;
} else if (stringCompare(string1, string2) > 0) {
cout << "string1 is bigger than string2";
} else {
cout << "string1 is smaller than string2";
}
//串拼接
variableString result1{};
concat(result1, string1, string2);
while (*result1.str != '\0') {
cout << *result1.str++;
}
cout << endl;
//取子串
variableString result2{};
subString(result2, string1, 2, 4);
while (*result2.str != '\0') {
cout << *result2.str++;
}
cout << endl;
if (clean(string1)) {
cout << "string1 have been emptied!" << endl;
}
}
bool variableStringAssign(variableString &variableStr, char *ch) {
delete variableStr.str;
int length = 0;
char *c = ch;
while (*c != '\0') {
length++;
c++;
}
if (length == 0) {
variableStr.str = nullptr;
variableStr.length = 0;
return true;
} else {
variableStr.str = new char[length + 1];
if (variableStr.str == nullptr) {
return false;
} else {
c = ch;
for (int i = 0; i <= length; ++i, ++c) {
variableStr.str[i] = *c;
}
variableStr.length = length;
return true;
}
}
}
int getLength(variableString variableString) {
return variableString.length;
}
int stringCompare(variableString variableString1, variableString variableString2) {
for (int i = 0; i < variableString1.length && i < variableString2.length; ++i) {
if (variableString1.str[i] != variableString2.str[i]) {
return variableString1.str[i] - variableString2.str[i];
}
}
//前边都一样,比较字符串的长度
return variableString1.length - variableString2.length;
}
bool concat(variableString &result, variableString variableString1, variableString variableString2) {
delete result.str;
result.str = nullptr;
result.str = new char[variableString1.length + variableString2.length + 1];
if (result.str == nullptr) {
cerr << "Your memory is not enough" << endl;
return false;
}
int i = 0;
while (i < variableString1.length) {
result.str[i] = variableString1.str[i];
i++;
}
int j = 0;
while (j <= variableString2.length) {
result.str[i + j] = variableString2.str[j];
j++;
}
result.length = variableString1.length + variableString2.length;
return true;
}
bool subString(variableString &result, variableString variableString1, int from, int length) {
if (from < 0 || from > variableString1.length || length < 0 || length > variableString1.length - from) {
cerr << "Your parameters is wrong, please check";
return false;
}
delete result.str;
result.str = nullptr;
if (length == 0) {
result.str = nullptr;
result.length = 0;
return true;
} else {
result.str = new char[length + 1];
int i = from;
int j = 0;
while (i < from + length) {
result.str[j++] = variableString1.str[i++];
}
result.str[j] = '\0';
result.length = length;
return true;
}
}
bool clean(variableString &variableString1) {
delete variableString1.str;
variableString1.str = nullptr;
variableString1.length = 0;
return true;
}
串的模式匹配
1 |
|
但是王道上的kmp的nex数组的构建代码似乎有点小问题,还是建议像acwing上的一样从j的下一位匹配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
//#include<bits/stdc++.h>//万能头文件
using namespace std;
const int N = 1000010,M = N*30;
void getNext(const string& p,int next[])
{
int len = (int)p.size();
next[0] = -1;
int k = -1;
int j = 0;
while(j < len)
{
if(k == -1 || p[j] == p[k])
{
++j;
++k;
next[j] = k;
}
else
{
k = next[k];
}
}
for(int x =1;x<=len;x++)//这里的next中的值是前后缀相同的数目,王道中的是相同数目+1即下一次对比的坐标
cout<<next[x];
cout<<endl;
}
int kmp(const string& s, const string& pattern)
{
int n = (int)s.size();
int ans = -1;
int i = 0;
int j = 0;
int patternLen = (int)pattern.size();
int next[patternLen] = {0};
getNext(pattern,next);
while(i < n)
{
if(j == -1 || s[i] == pattern[j])
{
++i;++j;
}
else
{
j = next[j];
}
if(j == patternLen)
{
ans = i - patternLen;
break;
}
}
return ans;
}
int main()
{
string s = "abbbadabaabcabadba";
string pattern = "abaabcaba";
cout << kmp(s,pattern) << endl;
return 0;
}
第四章
二叉树
这章节内容很多啊,考的东西也多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
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
using namespace std;
typedef struct bitnode
{
int data;
struct bitnode *l_child,*r_child;
}*bitree;
typedef struct Node//用于记录非递归后序
{
bitree btnode;
bool isfirst;//提供后序非递归操作标记
}Node,*node;
//线索二叉树
typedef struct threadnode
{
int data;
struct threadnode *lchild,*rchild;
int ltag,rtag;//线索标记
}threadnode,*threadtree;
//普通二叉树
class bi_tree
{
public:
bitree t;
//二叉树的建立
//思路就是用一个节点从头往下最后一个子树的位置(当且节点大于待插入节点就往左找,反之往右),
//找到插入位置后大于根节点的放在右边,小于根节点的放在左边
void creat_tree(int a[],int n,bitree &root)
{
bitnode *c,*pa=NULL,*p;
int i;
root = new bitnode;
root->data = a[0];
root->l_child = root->r_child = NULL;
for(i = 1;i<n;i++)
{
p = new bitnode;
p->l_child = p->r_child = NULL;
p->data = a[i];
c = root;
while(c)
{
pa = c;
if(c->data<p->data)
c = c->r_child;//如果根节点小于待插入节点就往右遍历
else c = c->l_child;
}
if(pa->data<p->data)
pa->r_child = p;//右放
else pa->l_child = p;
}
cout<<"二叉树建立完成"<<endl;
}
//前序遍历
void nlr(bitree root)
{
if(root)
{
cout<<root->data<<" ";
nlr(root->l_child);
nlr(root->r_child);
}
}
//中序
void lnr(bitree root)
{
if(root)
{
lnr(root->l_child);
cout<<root->data<<" ";
lnr(root->r_child);
}
}
//后序
void lrn(bitree root)
{
if(root)
{
lrn(root->l_child);
lrn(root->r_child);
cout<<root->data<<" ";
}
}
//层序遍历
void ceng(bitree root)
{
if(!root) return;
queue<bitree> q;
bitree p = root;
q.push(p);
while(!q.empty())
{
p = q.front();
cout<<p->data<<" ";
q.pop();
if(p->l_child) q.push(p->l_child);
if(p->r_child) q.push(p->r_child);
}
}
//非递归实现
//非递归先序
void nlr2(bitree root)
{
if(!root) return;
stack<bitree> s;
bitree p = root;
while(p||!s.empty())
{
if(p)
{
cout<<p->data<<" ";
s.push(p);
p = p->l_child;
}else
{
p = s.top();
s.pop();
p = p->r_child;
}
}
}
//中序非递归
void lnr2(bitree root)
{
if(!root) return;
stack<bitree> s;//懒得写栈了,直接用容器
bitree p = root;
while(p||!s.empty())
{
if(p)
{
s.push(p);
p = p->l_child;
}
else
{
p = s.top();
cout<<p->data<<" ";
s.pop();
p = p->r_child;
}
}
}
//非递归后序
void lrn2(bitree root)
{
if(!root) return;
stack<node> s;
bitree p = root;
node tmp;
while(p||!s.empty())
{
while(p)//一定是while,非递归其他也可以用while,但是这里不能用if
{
node btn = new Node;
btn->btnode = p;
btn->isfirst = true;
s.push(btn);
p = p->l_child;
}
if(!s.empty())
{
tmp = s.top();
s.pop();
if(tmp->isfirst==true)//第一次出现在栈顶
{
tmp->isfirst = false;
s.push(tmp);
p = tmp->btnode->r_child;
}else
{
cout<<tmp->btnode->data<<" ";
p = NULL;
}
}
}
}
//实现操作
void test()
{
int a[8] = { 5, 3, 7, 2, 4, 6, 8, 9 };
creat_tree(a,8,t);
cout<<"前序遍历:";
nlr(t);
cout<<endl;
cout<<"中序遍历:";
lnr(t);
cout<<endl;
cout<<"后序遍历:";
lrn(t);
cout<<endl;
cout<<"层序遍历:";
ceng(t);
cout<<endl;
cout<<"非递归先序遍历:";
nlr2(t);
cout<<endl;
cout<<"非递归中序遍历:";
lnr2(t);
cout<<endl;
cout<<"非递归后序遍历:";
lrn2(t);
cout<<endl;
}
};
//线索二叉树类
class xian_tree
{
public:
threadtree x,z,h;
void creat_tree(int a[],int n,threadtree &root)
{
threadnode *c,*pa=NULL,*p;
int i;
root = new threadnode;
root->data = a[0];
root->lchild = root->rchild = NULL;
for(i = 1;i<n;i++)
{
p = new threadnode;
p->lchild = p->rchild = NULL;
p->data = a[i];
c = root;
while(c)
{
pa = c;
if(c->data<p->data)
c = c->rchild;//如果根节点小于待插入节点就往右遍历
else c = c->lchild;
}
if(pa->data<p->data)
pa->rchild = p;//右放
else pa->lchild = p;
}
//cout<<"中序线索化二叉树建立完成"<<endl;
}
//中序遍历线索化
void inthread(threadtree &p,threadtree &pre)
{
if(p)
{
inthread(p->lchild,pre);//中序先处理左子树
if(!p->lchild)
{
p->lchild = pre;//建立前驱索引
p->ltag = 1;
}
if(pre&&!pre->rchild)//若前驱无右子树就让当前p为右子树,建立前驱的后继节点
{
pre->rchild = p;
pre->rtag = 1;
}
pre = p;//更新节点
inthread(p->rchild,pre);
}
}
//建立中序线索二叉树
void creat_inthread(threadtree t)
{
threadtree pre = NULL;
if(t!=NULL)
{
inthread(t,pre);
pre->rchild = NULL;//处理最后一个节点
pre->rtag = 1;
}
}
//求中序第一个节点
threadnode *firstnode(threadnode *p)
{
while(!p->ltag)//中序根据左根右的顺序第一个节点是最左下一个节点
p = p->lchild;
return p;
}
//求中序最后一个节点
threadnode *finalnode(threadnode *p)
{
while(!p->rtag)//中序根据左根右的顺序最后一个节点是最右下一个节点
p = p->rchild;
return p;
}
//求后继节点
threadnode *nextnode(threadnode *p)
{
if(!p->rtag)
return firstnode(p->rchild);//有右子树说明右子树中第一个节点就是后继节点
else return p->rchild;//否则下一个节点就是线索化后指的节点
}
//求前驱节点
threadnode *lastnode(threadnode *p)
{
if(!p->ltag)
return finalnode(p->lchild);
else return p->lchild;
}
//中序遍历
void lnr(threadnode *t)
{
for(threadnode *p=firstnode(t);p!=NULL;p = nextnode(p))
{
cout<<p->data<<" ";
}
cout<<endl;
}
//先序线索化
void prethread(threadtree &p,threadtree &pre)//根左右
{
if(p)
{
if(!p->lchild)//没有左子树,处理节点的前驱
{
p->lchild = pre;
p->ltag = 1;
}
if(pre&&!pre->rchild)//pre存在并且pre右子树不存在,处理前驱节点的后继
{
pre->rchild = p;
pre->rtag =1;
}
pre = p;
if(p->ltag==0)//避免循环,易错点,没有左子树说明此时p->lchild指向了p的前驱,因此不能再次回到遍历p的前驱节点
prethread(p->lchild,pre);//遍历左子树
prethread(p->rchild,pre);//遍历右子树
}
//cout<<"先序线索化二叉树建立完成"<<endl;
}
//建立先序线索二叉树
void creat_prethread(threadtree t)
{
threadtree pre = NULL;
if(t)
{
prethread(t,pre);
if(!pre->rchild)
pre->rchild = NULL;//处理最后一个节点
pre->rtag =1;
}
}
//先序遍历
void nlr(threadtree root)
{
for(threadnode *p = root;p!=nullptr;p = next_prenode(p))
{
cout<<p->data<<" ";
}
cout<<endl;
}
//下一个先序节点
threadnode* next_prenode(threadtree p)//根左右
{
if(p->rtag==1) return p->rchild;//有线索就直接按线索走
else
{
if(p->lchild&&p->ltag ==0) return p->lchild;//否则按照根左右的顺序有左孩子下一个就是左孩子,不然就找右孩子
else return p->rchild;
}
}
//后序构造线索二叉树
void postthread(threadtree root,threadtree pre)
{
if(root!=nullptr)
{
postthread(root->lchild,pre);
postthread(root->rchild,pre);
//建立前驱线索
if(!root->lchild)
{
root->ltag = 1;
root->lchild = pre;
}
//连接后继
if(pre&&!pre->rchild)
{
pre->rtag =1;
pre->rchild = root;
}
pre = root;
}
}
void creat_postthread(threadtree root)
{
threadnode* pre;
if(root)
{
postthread(root,pre);
pre->rchild =nullptr;
pre->rtag = 1;
}
}
//后序遍历需要三叉链表,有点复杂了就懒得写那么细了
//只要学会原理都可以推。
//测试
void test()
{
int a[8] = { 5, 3, 7, 2, 4, 6, 8, 9 };
creat_tree(a,8,z);
creat_inthread(z);
cout<<"中序线索二叉树建立完成-------------------"<<endl;
cout<<"遍历:";
lnr(z);
cout<<endl;
creat_tree(a,8,x);
creat_prethread(x);
cout<<"先序线索二叉树建立完成-------------------"<<endl;
cout<<"遍历:";
nlr(x);
cout<<endl;
}
};
//课后练习
class exercises
{
public:
//3后序非递归在上面实现了,比起其他两个就是要注意设定一个标记为来判断左右指针访问过了
//4,实现就是用额外空间存储,在反向输出即可,力扣上写过更难的
void four(bitree t)
{
queue<bitree> q;
stack<bitree> s;
q.push(t);
while(!q.empty())
{
bitnode *p = q.front();
q.pop();
s.push(p);
if(p->l_child) q.push(p->l_child);
if(p->r_child) q.push(p->r_child);
}
while(!s.empty())
{
cout<<s.top()<<" ";
s.pop();
}
}
//5,非递归写法用类似层次遍历,再用两个指针分别指向下一层和这层的最右或最左的子树,上层往下走等于另一个时层数就加一
int five(bitree t)
{
int res =0;
if(!t) return res;
int front =-1, rear = -1;
int last = 0;
bitree q[100];//用数组模拟队列
q[++rear] = t;
bitree p;
while(front<rear)//但队列不为空
{
p = q[++front];
if(p->l_child) q[++rear] = p->l_child;
if(p->r_child) q[++rear] = p->r_child;
if(front==last)
{
res++;
last = rear;//last更新
}
}
return res;
}
//6,两个数组中分别找关系一个一个排
bitree num6(int a[],int b[],int l1,int h1,int l2,int h2)//l1h1是先序的第一个和最后一个下标,另外两个是中序的
{
bitree root = new bitnode;
root->data = a[l1];//建立根节点
int l_len,r_len;
int i;
for(i =l2;b[i]!=root->data;i++);//找到中序中根节点下标
l_len = i-12;//左子树长度
r_len = h2-i;
if(l_len)
root->l_child =num6(a,b,l1+1,l1+l_len,l2,l2+l_len-1);
else root->l_child = NULL;
if(r_len)
root->r_child =num6(a,b,h1-r_len+1,h1,h2-r_len+1,h2);
}
//7用层序遍历的思想判断,插入所有的节点,遇到NULL检查后面的是否还有非空节点,有就不是
bool num7(bitree t)
{
if(!t) return false;
queue<bitree>q;
q.push(t);
while(!q.empty())
{
bitree p = q.front();
q.pop();
if(p)
{
q.push(p->l_child);
q.push(p->r_child);
}
else//发现null
{
while(!q.empty())
{
if(q.front()) return false;//发现非空元素
q.pop();//别忘了检查完后
}
}
return true;
}
}
//8
//用递归的思想判断
int num8(bitree t)
{
if(!t) return 0;
else if(t->l_child&&t->r_child)
return num8(t->l_child)+num8(t->r_child) +1;//出现双分支总数就加1
else return num8(t->l_child)+num8(t->r_child);//返回值不会改变
}
//9
//还是递归的思路
void num9(bitree t)
{
if(t)
{
num9(t->l_child);
num9(t->r_child);
auto tmp = t->l_child;
t->l_child = t->r_child;
t->r_child = tmp;
}
}
//10
//经典递归
int i_10 =1;//遍历序号
int num10(bitree b,int k)
{
if(b==nullptr) return '-1';//设定空标记为-1
if(i_10==k) return b->data;
i_10++;
int ch = num10(b->l_child,k);//往左子树找
if(ch!=-1) return ch;
ch = num10(b->r_child,k);//找不到就在右子树找
return ch;
}
//11
//只是删除节点的子树所以要遍历所有节点,用层序
void num_11_1(bitree &t)//删除操作,删除二叉树要递归删除,否则只是删除了根结点的,子树上的还会存留
{
if(t)
{
num_11_1(t->l_child);
num_11_1(t->r_child);
delete t;
}
}
void num_11_2(bitree bt,int x)
{
queue<bitree> q;
if(bt)
{
if(bt->data ==x)//根节点就是x的情况
{
num_11_1(bt);
return;
}
}
q.push(bt);
while(!q.empty())
{
bitree p = q.front();
q.pop();
if(p->l_child)//处理左子树
{
if(p->l_child->data==x)
{
num_11_1(p->l_child);
p->l_child =NULL;
}
} else q.push(p->l_child);
if(p->r_child)//处理右子树
{
if(p->r_child->data==x)
{
num_11_1(p->r_child);
p->r_child =NULL;
}
} else q.push(p->r_child);
}
}
//12
//非递归后序实现,因为访问到x时,栈内节点都是x的祖先
struct stack_12
{
bitree t;
bool tag;//判断标志
};
void num12(bitree bt,int x)
{
stack_12 s[100];
int top = 0;
while(bt||top>0)
{
while(bt&&bt->data!=x)//左子树节点入栈
{
s[++top].t =bt;
s[top].tag = false;
bt = bt->l_child;
}
if(bt&&bt->data==x)
{
cout<<"找到查找结点"<<endl;
for(int i =1;i<=top;i++)
cout<<s[i].t->data<<" ";
exit(1);//结束
}
while(top&&s[top].tag==1)//将空结点退栈
{
top--;
}
if(top)
{
s[top].tag =1;
bt=s[top].t->r_child;
}
}
}
//13
//后序非递归遍历p时保存此时的栈内所有p的祖先,再遍历q,用两个不同祖先一次对比,第一个重复元素即为最近公共祖先
stack_12 s[100],s1[100];//和12题一样
int top,top1;
bitree num13(bitree root,bitnode *p,bitnode*q)
{
top = 0;
bitree bt = root;
while(bt!=NULL||top>0)
{
while(bt!=NULL)
{
s[++top].t = bt;
s[top].tag =0;
bt=bt->l_child;//存放左子树
}
while(top!=0&&s[top].tag==1)
{
if(s[top].t==p)
{
for(int i = 1;i<=top;i++)//遇到p结点时此时栈内元素都是p的祖先,保存起来
{
s1[i] = s[i];
top1=top;
}
}
if(s[top].t==q)
{
for(int i =top;i>0;i--)//就是暴力去找,嵌套循环
{
for(int j =top1;j>0;j--)
{
if(s1[j].t==s[i].t)
return s[i].t;//找到祖先
}
top--;
}
}
if(top!=0)
{
s[top].tag=1;
bt = s[top].t->r_child;
}
}
}
return NULL;//无祖先
}
//14
//层序遍历顺便记录结点的层号,再统计每层的宽度,这种题好像可以用bfs和dfs做
int num14(bitree b)
{
queue<pair<int,bitree>> q;//int用来存储每个节点的层号,这个方式和书上结构体效果差不多
vector<pair<int,bitree>> v;//存储答案
int idx = 1;
q.push({idx,b});//插入第一层
while(!q.empty())
{
auto p = q.front();
q.pop();
v.push_back(p);
idx = p.first;//idx更新为出队的层数,因为每一层出后下一层进来
if(p.second->l_child)
{
q.push({idx+1,p.second->l_child});
}
if(p.second->r_child)
{
q.push({idx+1,p.second->r_child});
}
//idx++;层号++这样或许也可以
}
//在v中查找
int max = 0;idx = 1;
for(int i =0;i<v.size();i++)
{
int n = 0;//统计每层的宽度
while(v[i].first==idx)
{
n++;i++;
}
idx = v[i].first;//结束上面循环后i来到了下一层,此时更新层号
if(n>max) max = n;
}
return max;
}
//15
//其他类型数不能由先序得到后序,满二叉树可以,用递归的思想,先找到根节点,剩下的对半,前一半为左子树,后一半为右子树
void num15(int pre[],int l1,int h1,int post[],int l2,int h2)
{
int half;
if(h1>=l1)
{
post[h2] = pre[11];
half = h1-l1/2;
num15(pre,l1+1,l1+half,post,l2,l2+half-1);
num15(pre,l1+half+1,h1,post,l2+half,h2-1);
}
}
//16
//用中序遍历,遍历到叶节点时就插入链表即可,其他遍历方式因该也可以
vector<bitree>n;//用vector代替链表,会有些许差异
void num16(bitree b)
{
if(b)
{
num16(b->l_child);
if(b->l_child==NULL&&b->r_child==NULL)
n.push_back(b);//将叶结点按顺序插入n
num16(b->r_child);
}
}
//再按n来创建链表即可
//17
//经典递归
bool num17(bitree t1,bitree t2)
{
bool flag1,flag2;
if(t1==NULL&&t2==NULL)
return true;
else if(t1==NULL||t2==NULL)
return false;
else
{
return num17(t1->l_child,t2->l_child)&&num17(t1->r_child,t2->r_child);//简写了
}
}
//18
//不好说,看解析
void num18(threadtree t,threadtree p)
{
threadtree q;
if(p->rtag==0)//有右子树,右子树为前驱
q = p->rchild;
else if(p->ltag==0)//无右但有左子树,左为前驱
q = p->lchild;
else if(p->lchild==NULL)
q=NULL;//p是中序第一个结点,无后序前驱
else//否则往上找线索
{
while(p->ltag==1&&p->lchild)
{
p = p->lchild;//往上找到有左子树的第一个p的祖宗结点,那么依据左右根的思路p的前驱就是左子树的根结点
}
if(p->ltag==0)
q = p->lchild;
else q=NULL;//仅有单支树的情况
}
return q;
}
};
int main()
{
bi_tree tree;
tree.test();
// xian_tree t;//不知道为什么一起两个类一起用线索二叉树会缺漏
// t.test();
return 0;
}
并查集
王道上写法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//并查集
int f[100];//采用双亲表示法存放数据
int h[100];//优化后根节点的绝对值是该树中结点的总树
//初始化 n个元素
void Init(int n) {
//使每个元素的根节点是其本身
//即初始时每个元素都是单独的
for(int i=1; i<=n; i++) f[i]=i;
}
void init_pro(int n)
{
for(int i=1; i<=n; i++) f[i]=i,h[i]=0;
}
//查询操作,本质就是向上查找父节点,直至查到根节点即可表示该点属于哪个树了
int Find(int i)
{
while(f[i] >= 0) //直到元素的父节点<=0,表示已经查询到了树的根,这是王道上的写法
i = f[i];
return i; //返回根节点对应的元素
}
//查找优化(路径压缩
int find_pro(int i)
{
int root = i;
while(f[root]>=0) root=f[root];//循环找到结点
while(i!=root)//没有直接指向根节点就压缩,顺便将往上走的所有父节点都压缩,不断减少树的高度
{
int t = f[i];
f[i] = root;
i =t;
}
return root;
}
//合并,本质就是将一个树的根结点修改为另一个树的根节点,象征着合并
void Union(int a, int b) {
//先找到两个元素对应的根对应的元素
int fa = Find(a);
int fb = Find(b);
if(fa==fb) return;
else f[fa]=fb; //否则令元素 a的根指向元素 b的根
}
//优化思路就是小树合并到大树里,这样高度更低
void union_pro(int a, int b)
{
int fa = Find(a);
int fb = Find(b);
if(fa != fb) {
if(h[fa] < h[fb]) {
f[fa] = fb;
} else {
f[fb] = fa;
if(h[fa] == h[fb]) h[fa]++;
}
}
}
Acwing写法
网上主流根节点是存储数组的下标和存储的数据相等的点。王道上是根节点存储的是负数,绝对值表示树中结点数目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(1)朴素并查集:
int p[N]; //存储每个点的祖宗节点
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);//自带了路径压缩,递归写法。借用了除根结点外其他结点下标位置上的数据都是该节点的父节点下标的特性,不断往上直至查询到相同的时候表明找到了根节点,再将原本查询元素存放的数据更改为根节点下标,实现路径压缩。
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ ) p[i] = i;
// 合并a和b所在的两个集合:
p[find(a)] = find(b);
(2)维护size的并查集:
int p[N], size[N];
//p[]存储每个点的祖宗节点, size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ )
{
p[i] = i;
size[i] = 1;
}
// 合并a和b所在的两个集合:
size[find(b)] += size[find(a)];
p[find(a)] = find(b);
(3)维护到祖宗节点距离的并查集:
int p[N], d[N];
//p[]存储每个点的祖宗节点, d[x]存储x到p[x]的距离
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x)
{
int u = find(p[x]);
d[x] += d[p[x]];
p[x] = u;
}
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ )
{
p[i] = i;
d[i] = 0;
}
// 合并a和b所在的两个集合:
p[find(a)] = find(b);
d[find(a)] = distance; // 根据具体问题,初始化find(a)的偏移量
第五章
图
1 |
|
prim找最小生成树
acwing写法
1 |
|
Kruskal算法求最小生成树
acwing写法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
using namespace std;
const int N = 100010;
int p[N];//保存并查集
int n,m;
struct e{
int a;
int b;
int w;
bool operator < (const e& rhs){//通过边长进行排序
return this->w < rhs.w;
}
}edg[N * 2];
int res = 0;
int cnt = 0;
int find(int a)
{
if(p[a]!=a) p[a] = find(p[a]);//并查集找根结点+压缩操作
return p[a];
}
void klskr()
{
for(int i =1;i<=m;i++)//每条边都找
{
int pa = find(edg[i].a);//a所在的集合
int pb = find(edg[i].b);
if(pa!=pb)
{
res+=edg[i].w;
p[pa] = pb;//合并a,b
cnt++;
}
}
}
int main()
{
cin>>n>>m;
for(int i =1;i<=n;i++) p[i] =i;//初始化并查集
for(int i =1;i<=m;i++)
{
int a,b,c;
cin>>a>>b>>c;
edg[i] = {a,b,c,};
}
sort(edg+1,edg+m+1);//按边长排序
klskr();
if(cnt<n-1)
cout<<"impossible";
else cout<<res;
return 0;
}
Dijkstra求最短路
1 |
|
Floyd求最短路
1 |
|
第六章
查找
顺序查询
1 | void print_arry(vector<int> a) |
二分查找
1 | //二分查找 |
二叉排序树
1 | //二叉排序树构造 |
二叉平衡树
内容有点多,直接抄大佬的了,实现逻辑可以参考另一位大佬的解释及其旋转_快乐江湖的博客-CSDN博客](https://blog.csdn.net/qq_39183034/article/details/121445755))
1 | /* |
练习题目
1 | //练习题目 |
哈希表
Acwing上开放寻址法和拉链法都有讲解过
开放寻址法
1 |
|
拉连法
1 | /* |
排序
插入排序
- 从第一个元素开始,该元素可以认为已经被排序;
- 取出下一个元素,在已经排序的元素序列中从后向前扫描;
- 如果该元素(已排序)大于新元素,将该元素移到下一位置;
重复步骤 3,直到找到已排序的元素小于或者等于新元素的位置; - 将新元素插入到该位置;
重复步骤 2~5。
1 | //插入排序 |
希尔排序
类似插入排序,先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
1 | //希尔排序 |
冒泡排序
像吐泡泡一样,从第一个开始和后一个两两比较交换,让符合条件的逐渐移动到查找范围的末端,在缩小一格查找范围,直至无交换说明完成了排序。
1 | //冒泡排序 |
快速排序
找一个基准值,让比基准值大的都在右边,比基准值小的在基准值左边。在分别对基准值左右区间重复此操作。
1 | //快速排序 |
选择排序
遍历找到最小或最大值,和最终位置交换后范围减一。
1 | //选择排序 |
堆排序
构造大根堆或小根堆实现排序
1 | /堆排序 |
归并排序
它的基本思想是把待排序的元素分解成两个规模大致相等的子序列。如果不易分解,将得到的子序列继续分解,直到子序列中包含的元素个数为1。因为单个元素的序列本身就是有序的,此时便可以进行合并,从而得到一个完整的有序序列。
1 | //归并排序 |
练习题
1 | //练习题 |
代码总览
1 |
|
后记
2023年3月7日,数据结构一轮复习,完!