' a( t X+ C5 d! E0 z从数学基础、输入输出、数据类型、循环、数组、指针、函数、位运算、结构体、排序 等几个方面,总结出的具有概括性的例题 100 道 《C语言入门100例》,目前还在更新中。* y B, j! F! P
这里可以列举几个例子: 4 A8 w+ p) }' K. H8 {. ^5 h; [1、例题1:交换变量的值 " X* V& y% a+ M% V一、题目描述: Y4 w( m1 `' D! {. d* Q I
循环输入,每输入两个数 a aa 和 b bb,交换两者的值后输出 a aa 和 b bb。当没有任何输入时,结束程序。! }7 W F# F/ F
- i+ b! Y6 j+ W o* {
& t# E, N# K, N0 B4 V# H' \0 l. B2 i2 K2 _ F4 j" ^
/ J/ }2 O5 z# ~& n0 g
二、解题思路 $ Y6 n' { B4 P% Y4 |2 F5 ]0 h难度:🔴⚪⚪⚪⚪ . r7 w7 m1 P8 i* |; u3 x0 Q 3 @; h+ x g- r3 Z- F6 F# [- d6 c- I# c- b, A( O% l* X2 A& _
这个题的核心是考察如何交换两个变量的值,不像 python,我们可以直接写出下面这样的代码就实现了变量的交换。, i6 \& [) {$ w$ V" W
a, b = b, a * |* d$ d$ P# m1$ k) { k {( i
在C语言里,这个语法是错误的。 W, d" j' m, |) }7 I9 H
我们可以这么理解,你有两个杯子 a aa 和 b bb,两个杯子里都盛满了水,现在想把两个杯子里的水交换一下,那么第一个想到的方法是什么? ' h6 u: e, S9 p当然是再找来一个临时杯子: 7 G& R5 T6 ~. i H6 k9 D 1)先把 a aa 杯子的水倒进这个临时的杯子里;. H$ V& b' i: n2 K) w
2)再把 b bb 杯子的水倒进 a aa 杯子里;5 H' V' K$ _) ?# b
3)最后把临时杯子里的水倒进 b bb 杯子;4 f% ]2 m( P! P4 P- n
4 p2 Z. z6 z& ]6 G* P# M, J9 v- _# `/ j4 J H" n+ c
这种就是临时变量法,那么当然,还有很多很多的方法,接下来就让我们来见识一下吧。+ V- m% l! Y- S
) _7 j! n6 R( f9 B5 |9 A; F
% b2 o, s0 s6 X- H5 w& I三、代码详解; r: H4 z4 A! g5 o. E
1、正确解法1:引入临时变量6 L! M7 p" B/ k5 `" u% S- X5 T
#include <stdio.h>0 R, f8 O- U2 ?! ?3 m6 s3 ^
int main() { 6 s+ R3 {4 Y4 ]1 A9 t int a, b, tmp; O. a8 b( Z% J0 Z' u) I while (scanf("%d %d", &a, &b) != EOF) { 3 G8 ~) ^. N, Z' T0 G8 {5 w' J tmp = a; // (1) , y# A* ^3 R3 j: v# W x a = b; // (2)3 r' w* x( `, g2 a( D
b = tmp; // (3) . ]) S- E! D1 P. U, m* e; R printf("%d %d\n", a, b); + T, R" _4 Z, T+ G) g }. e7 {$ a7 r6 r3 o
return 0;* \8 n" f' J( p$ q( D; j
} $ J4 ]! e3 k) m+ {1 " m9 A" i' S$ p, c: B2 ' g& a1 u; X6 [3 r- p36 o; P- Y% {8 L0 ]" ]
41 \! e) l6 d+ G( R; H' c; G
51 q+ `& G U& ^5 O
6 $ z2 y, ^- A# }/ H( k* b: P7/ `# Q- e8 k- c$ b- Q
8 * d! C8 h1 W, O# c% N: b9 t9 0 w3 X5 A, P9 K" {0 H" B a109 C) ?0 S$ \5 F% ?6 y* `
112 H( k# b; p9 Y, L
( 1 ) (1)(1) tmp = a;表示把 a aa 杯子的水倒进这个临时的杯子里; D- S$ K" n/ r3 K
( 2 ) (2)(2) a = b;表示把 b bb 杯子的水倒进 a aa 杯子里;; N# N+ c; ^9 W4 l- P3 q# D# K
( 3 ) (3)(3) b = tmp;表示把临时杯子里的水倒进 b bb 杯子里; 8 T' N* H3 s! ]2 w这三步,就实现了变量 a aa 和 b bb 的交换。 7 u+ n0 `' q6 K m$ O$ H2、正确解法2:引入算术运算% _! k# ?; L2 Y3 t/ ^* U: q
#include <stdio.h>& @3 p" v- O0 r5 F/ g) k1 s
int main() {. G+ t4 a1 e7 Z' p. }9 c% m
int a, b; 7 D5 p0 D* `4 Y V, E8 ^ while (scanf("%d %d", &a, &b) != EOF) {5 ]# g3 K& N" M8 T
a = a + b; // (1); Q$ a) q# L* d3 t+ B1 G' @6 L
b = a - b; // (2)% ]5 J, p: |$ V$ ^$ u
a = a - b; // (3)$ ~3 U6 O7 C! f, e
printf("%d %d\n", a, b); 8 c4 g& b! L a3 z! g+ J4 Q } , T0 N/ y" y4 s6 u& z6 L return 0;& q" N; F- I- S) \8 ^/ h
} 6 t* J$ |# m i1 6 p7 |; z4 Y% A! x2 ) W; Y% q1 I0 S# z6 h0 u0 u2 ?3 ( y" t+ U" O1 R8 _$ i6 |4' `! A8 P! F" T
5 8 Q8 K: i/ d; t1 [5 E. {% x6 T6: ?, T8 ^" W% W7 |
7" P' Z' m0 X4 p" Z
8 ( D1 H4 C% y _- A. V9 , p7 {# v+ o# o S# n a1 N8 c10 * M5 @+ {1 G- U5 `% T11 9 D. y2 p: \/ T1 J( 1 ) (1)(1) a = a + b;执行完毕后,现在最新的a的值变成原先的a + b的值; 0 `3 M. D! \2 y0 k4 I8 C" D( 2 ) (2)(2) b = a - b;执行完毕后,相当于b的值变成了a + b - b,即原先a的值; : w6 |& i7 d# j% h( 3 ) (3)(3) a = a - b;执行完毕后,相当于a的值变成了a + b - a,即原先b的值; 2 h; r+ S! Z8 i从而实现了变量a和b的交换。/ w5 ] C8 I: C4 p, X: k, V
3、正确解法3:引入异或运算, u2 G& B9 Z$ _
首先,介绍一下C语言中的^符号,代表的是异或。; u# L2 y% A+ a9 x7 ^
二进制的异或,就是两个数转换成二进制表示后,按照位进行以下运算: 2 M# n; j Y' H# g5 n左操作数 右操作数 异或结果" _) [6 ~6 b2 J4 a; W
0 0 0$ V) F7 C0 `( ^# i3 e3 v( L( @! o
1 1 0, B, p+ \1 a# @ D
0 1 1 9 J* ~/ K2 x p2 {* j% G1 0 1 * B* A+ ]- J+ b+ F# P) J# e也就是对于 0 和 1,相同的数异或为 0,不同的数异或为 1。- |8 ^4 \9 ]% f( L; K& z! m
这样就有了三个比较清晰的性质:# T! F! ~. C5 k$ R7 f. i
1)两个相同的十进制数异或的结果一定位零。 - c4 l5 M _3 O* @% Z7 x" U; D2)任何一个数和 0 的异或结果一定是它本身。 0 g" w4 E2 Q7 _" k3)异或运算满足结合律和交换律。! D% F2 v5 t: z2 y
#include <stdio.h> " G& n' c& k9 N4 d8 ~8 d Fint main() {4 S- r; y' u% X% x
int a, b; & H4 U/ H. a K5 F5 U while (scanf("%d %d", &a, &b) != EOF) {7 v3 _, S) R; M8 i: u+ T7 ` |
a = a ^ b; // (1) : x8 w7 ?7 }- c" @5 V- {4 u b = a ^ b; // (2) ' P5 |3 _. w* q a = a ^ b; // (3) $ `$ I% B, N: N( W% O3 E printf("%d %d\n", a, b);4 d# o' M. F% F, A" _' R- ]* W* O& k
} $ U) @+ l0 ~6 c return 0; f" i9 P8 C9 K- l7 i F) D. m
}& y9 _& t- Q- m+ Z! J! Z4 s
1# u1 G) O! G7 I; r1 w1 P6 i
2 % {' Y+ h c* g0 s- b3+ W4 R. n2 b' C0 ~
4 # i7 }) ^/ H5 S5$ P( {/ V9 ~. U: x$ q3 s) @
69 W& F5 d) ?4 M* H: d$ e
78 H, K" W* `( R. N
8$ H7 `1 v6 ^+ ^, }' X- {
94 `+ j7 V: V5 {3 \/ n5 ?
10( n+ s9 U" J- ?
11' r# ]: R( S# z$ |% B7 z
我们直接来看 ( 1 ) (1)(1) 和 ( 2 ) (2)(2) 这两句话,相当于b等于a ^ b ^ b,根据异或的几个性质,我们知道,这时候的b的值已经变成原先a的值了。 " m/ Q. U% l) E7 c; f而再来看最后一句话,相当于a等于a ^ b ^ a,还是根据异或的几个性质,这时候,a的值已经变成了原先b的值。 p+ A$ Q7 w2 ?
从而实现了变量a和b的交换。 ' k8 @0 _" k! ^7 x$ e6 h9 L6 b4 @ S$ H. P! ^
6 E( O+ z9 p8 }
4、正确解法4:奇淫技巧 $ s( ], W( c. Z* d* B当然,由于这个题目问的是交换变量后的输出,所以它是没办法知道我程序中是否真的进行了交换,所以可以干一些神奇的事情。比如这么写: . h' t5 Q: @+ n* b#include <stdio.h> ! M' \" Q/ M* \, `9 Iint main() { " ^* e: B8 Z- b% _, J/ ` ?- V! l int a, b; 4 }" g: Y$ ]3 s/ o$ [ while (scanf("%d %d", &a, &b) != EOF) { 9 i( J* q9 L9 ?$ M: [) e; p printf("%d %d\n", b, a); + [! d0 I5 p l9 B } % w/ L0 ?/ h5 `5 z1 k0 X& t return 0; 6 G& } D6 _ ? A0 J* X& u0 _} 4 C2 }6 y" ~" H) C14 D9 P" }1 J6 ~# v8 u, Q
2 R. g0 x4 \ n' s( X; y) H1 \3 ) w( o/ K( j5 m$ E& C" [4& D8 U& J+ _" S1 s
5 c8 D4 @* t d7 C6 6 S8 X6 n% U3 p G7 \. M7 # i5 o; T4 {; k4 J! b( {8 ( I1 s0 y, v+ n/ l! A你学废了吗 🤣? ; T( k+ v3 w) @0 f, C$ I2、例题2:整数溢出 O H" l2 S- \ H
一、题目描述 1 z& [" z" v; u& p 先输入一个 t ( t ≤ 100 ) t (t \le 100)t(t≤100),然后输入 t tt 组数据。每组输入为 4 个正整数 a , b , c , d ( 0 ≤ a , b , c , d ≤ 2 62 ) a,b,c,d(0 \le a,b,c,d \le 2^{62})a,b,c,d(0≤a,b,c,d≤2 - X$ U" F. Y7 `% T62- r6 ]3 ?4 [" a- [
),输出 a + b + c + d a+b+c+da+b+c+d 的值。& Z; s% e3 q/ I; ], f, z) N: b" J
% H3 o. T4 E) b* Y
! l* Y2 B- q: X/ o; |* `
二、解题思路* ]4 N5 g* P8 p( z8 g
难度:🔴🔴⚪⚪⚪( m$ ?* |( ^/ I2 `; R5 H% n" R" }
0 @5 C/ A, U' B/ \8 [, a% @
' ^6 Y; W" C7 }6 C% Y
这个问题考察的是对补码的理解。# f( X% R* f6 U, c/ C+ p
仔细观察题目给出的四个数的范围:[ 0 , 2 62 ] [0, 2^{62}][0,2 & _6 ?) L4 _; H& q
62 4 W: x0 I! J' i ],这四个数加起来的和最大值为 2 64 2^{64}2 * x/ r& i9 G* n) ^- I
64 9 _8 Q* I7 ?) ^! u$ d$ `4 E 。而C语言中,long long的最大值为:2 63 − 1 2^{63}-12 2 |# h( P$ p' r6 x/ c0 B
63+ x* g: C; ^$ X
−1,就算是unsigned long long,最大值也只有2 64 − 1 2^{64}-12 0 {0 q) u2 j9 I7 _0 U) d) o
64% N7 L# q9 a' L& ?
−1。, ]' \: e* m. B$ i m) e& o8 B' ~# |4 \
但是我们发现,只有当四个数都取得最大值 2 62 2^{62}2 1 I9 o8 V/ ^+ i: K" ?$ G" [" F
628 m4 I U* q2 O; J1 R+ F# {
时,结果才为 2 64 2^{64}2 % a' n; T# ^6 b0 Z; y! I$ S644 H N# h" A! E; m }- t; M/ z
,所以可以对这一种情况进行特殊判断,具体参考代码详解。0 m7 B: `! h% A( O
三、代码详解8 `8 t. q1 d- I9 I! l1 Y
#include <stdio.h>) S+ ?7 T$ d0 H& f9 m
typedef unsigned long long ull; // (1) ) x2 f, c$ M9 L! O4 h l G1 Dconst ull MAX = (((ull)1)<<62); // (2) " J |9 i& h0 O9 P. H# }* q4 C- E * ]9 @) H' ~9 ] x! | $ L1 p' M/ C% `int main() { ( p/ b3 L9 j. ]; i [ int t;7 M; T& d4 z* R' D" h8 g$ @4 X
ull a, b, c, d; ( X& h- K% ?$ S, `% i' J scanf("%d", &t);% n! c& T' n0 y
while (t--) { ' r9 ]. T* L6 x3 V; c/ ? scanf("%llu %llu %llu %llu", &a, &b, &c, &d); // (3)4 @& l% }$ H# ^$ Q% k' d
if (a == MAX && b == MAX && c == MAX && d == MAX) // (4): k) Z0 A9 G' |5 C+ Q' {2 C" q
printf("18446744073709551616\n"); // (5) - \* ]: \4 L$ W- W8 o9 l. r else. F/ b' N9 N& V- u8 {
printf("%llu\n", a + b + c + d); // (6)4 u T" a+ v g; Z
}- X8 e0 C# u6 f, _
return 0; . ~0 W& K. y F% l}4 h, U# `4 m" t+ G/ D P6 c+ c
1 H1 q$ @; l! ?& F2 5 ^& X4 O" j! i8 w5 L' S5 ~: `+ W3 % W' a* a% ?* S% x" \4 ; r+ D+ G1 T4 z0 l0 [5 h0 f% z5 + p& M$ \! n* }/ |/ J61 g9 w( L) t" R( t8 H: P
7 6 `/ E: i2 l# \- K' \8# A. a# D# F/ ^* k0 M! m) b1 H
9 : \7 t5 I* a5 E9 J, b# i: S$ l10 ' V( A7 I. }% Z% `5 x- o11 ' C0 t, H8 {! \# s8 I12 + z; c+ T2 z# x. _13 $ b; {" Z# t- ?7 F14 6 s, t4 y' D3 @: J* q2 F. f/ }153 ^! b# Q8 U) Z& T5 ~
16; l& W" q4 V) ?- W) D! q. ~
17( c' R% e C/ s
( 1 ) (1)(1) 由于这题数据量较大,所有数据都需要用64位无符号整型。ull作为unsigned long long的别名;) m0 Y! N1 J8 ?7 S
( 2 ) (2)(2) 用常量MAX表示 2 62 2^{62}2 8 z3 N1 H) F+ Y8 v8 S62# C; G) |' F. L! g8 _: Z. U6 m# q
,这里采用左移运算符直接实现 2 22 是幂运算; " l, ~& s, g) N数学 C语言 7 H# L* K" v& p3 j; G8 i2 n 2^n2 3 S; K* a s7 Cn & ?% B+ ], m4 b; c# d, }# h 1<<n* }5 }0 E0 \3 L9 F/ e
需要注意的是,由于 1 是int类型,所以需要对 1 进行强制转换。(ull)1等价于(unsigned long long)1; ' ]! m' y" l9 m. k/ q" e( 3 ) (3)(3) %llu是无符号64位整型的输入方式; 8 N) b0 p. M; O, n" C3 B* Z( 4 ) (4)(4) 这里是对所有数都等于最大值的特殊判断,&&运算符的优先级低于==,所以这里不加括号也没事; ' D3 Y# L1 r& @2 J- q! ?2 q0 ~( 5 ) (5)(5) 由于 2 64 2^{64}2 & y2 N3 h; \: C [ @
64 ; e+ r' e6 V5 v" m! l0 C5 Z p' w 是无法用数字的形式输出的,所以我们提前计算机算好以后,用字符串的形式进行输出;8 i* w) |& e" W" V( t7 ~5 i [2 a( {
( 6 ) (6)(6) 其它情况都在 [ 0 , 2 64 − 1 ] [0, 2^{64}-1][0,2 ; u/ t( V. F3 l/ K7 s3 S
64 2 g* x# f5 Q$ d) W+ ~; D −1] 范围内,直接相加输出即可。) {! f" C( p# s3 M/ o C& h
由于这个专栏是付费专栏,可能对学生党不是很友好,所以作者经过再三思考,打算放出 300 张 一折优惠券, 先到先得。只要拿这个图片来找作者即可享受,仅限前 300 名。 o w2 ^7 `+ i为了适当提高一定门槛,你至少需要学会如何下载图片或者截图并且发送到微信里 🤣。 ) Z6 z9 _2 R( e* X 1 L; r; W4 W& X; l4 Q, J+ L 2 r& B1 S1 C2 q D! f) d3 X8 o/ w3、数据结构 7 V4 d" i7 w8 s {1 {5 S. ?: x) o《C语言入门100例》上的例题,如果能理解前面 25 道,那基本C语言的学习就可以告一段落了,接下来就要开始我们的数据结构的学习了。4 @2 e2 u5 a( K* z9 E7 E: \
1、什么是数据结构 $ y7 N4 b4 G! I# H5 M- t你可能听说过 数组、链表、队列、栈、堆、二叉树、图,没错,这些都是数据结构,但是你要问我什么是数据结构,我突然就一脸懵逼了。 ! Y* u. _+ e. P' V# u. a5 z5 t4 i如果一定要给出一个官方的解释,那么它就是:0 J, k" v' M2 @% @0 ?4 M
计算机存储、组织数据的方式。相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率。往往同高效的检索算法和索引技术有关。% I M8 y- o4 k6 J$ B
+ I# ]! u. k; o2 P4 G) h3 C
( Q: z! ]& |0 F5 i# C9 q8 m是不是还不如说它是堆,是栈,是队列呢? " w% w! P) P2 p9 U是这样的,我们学习的过程中,跳过一些不必要的概念,能够节省我们更多的时间,从而达到更好的效果,当你还在理解数据结构是什么的时候,可能人家已经知道了栈有哪些操作了。 , A% L/ K7 R7 L+ V ~) L1 W! p2、数据结构和算法的关系! `/ c" V" c5 S1 t
很多同学搞不明白,数据结构与算法有哪些千丝万缕的关系?甚至有些同学以为算法里本身就包含了数据结构。; Q9 o& P0 \) h/ b
数据结构主要讲解数据的组织形式,比如链表,堆,栈,队列。) m; m: H( w: w! t% y
而算法,则注重的是思想,比如链表的元素怎么插入、删除、查找?堆的元素怎么弹出来的?栈为什么是先进后出?队列又为什么是先进先出? 7 \' t [ m5 W4 t( N讲得直白一点,数据结构是有实体的,算法是虚拟的;数据结构是物质上的,算法是精神上的。当然,物质和精神 缺一不可。6 h; }+ D4 {% d2 G/ {( i. g0 u7 L
3、数据结构概览 2 I% e. m; W# R6 ]周末花了一个下午整理的思维导图,数据结构:& D+ o7 U! I: F: r |9 J. j; h
9 e" M+ k" W* R) ?3 o" y, Y8 F) P( D+ j' i' ~
常用的一些数据结构,各自有各自的优缺点,总结如下:! G; _. e4 G. ?- A' ?& v9 r- D
a、数组 8 K/ `& B# z% d5 X! g! e内存结构:内存空间连续 2 S+ L- s0 |6 Q3 b5 U实现难度:简单- ^4 f" p4 p2 S5 k$ W5 v) b' w& q
下标访问:支持 9 O5 E! P! P3 j& ~1 F! e, Z分类:静态数组、动态数组1 I) X) m# G+ X: {. P5 A
插入时间复杂度:O ( n ) O(n)O(n) 7 z+ Q3 ~; A" x# X6 S& U8 x9 F查找时间复杂度:O ( n ) O(n)O(n)% j0 W9 @( L, O$ d Q) t% K
删除时间复杂度:O ( n ) O(n)O(n) ; Q7 S' p x- C, u ' D1 Y/ S: }( k, }* x$ g+ W. A8 A" i) o7 E. t' I
b、字符串# g2 R! @+ k! S# E1 s$ k
内存结构:内存空间连续,类似字符数组 P% R* A" f3 D8 q4 k$ m
实现难度:简单,一般系统会提供一些方便的字符串操作函数* Y8 r0 r) H* m; T+ V
下标访问:支持4 o3 i+ w& h# [5 z5 n
插入时间复杂度:O ( n ) O(n)O(n)) y7 V t# B+ W9 V* Z/ g* L
查找时间复杂度:O ( n ) O(n)O(n) F1 a9 s: x% O# Z
删除时间复杂度:O ( n ) O(n)O(n)4 l8 P# K# C) H# d; B% J
, _, p4 c, _; N. D) J% I- T J3 R$ O3 T( b; B0 R+ t: {
c、链表0 ^& n7 k; e# H2 c* ]* d. b) G3 n: C
内存结构:内存空间连续不连续,看具体实现 6 \: a5 U" q, x) s6 m) c& A8 A+ ]实现难度:一般3 Q1 O* E( i3 P. i. {6 u/ ~( r
下标访问:不支持 & x& t# b& c" |6 x分类:单向链表、双向链表、循环链表、DancingLinks n/ s: i* ~% y
插入时间复杂度:O ( 1 ) O(1)O(1) / d& z' o3 Y, ?9 k: B o d查找时间复杂度:O ( n ) O(n)O(n) ; `: l3 x6 |- I删除时间复杂度:O ( 1 ) O(1)O(1) 4 e {$ l* @7 X! n( E! s ; Y0 T0 }/ D+ ]" x P0 F# L7 U4 ~4 s% u
d、哈希表 6 ?9 `/ i/ X' T, }1 c5 t/ j' Z内存结构:哈希表本身连续,但是衍生出来的结点逻辑上不连续5 e2 U) Z1 P" f
实现难度:一般$ h- { y/ v \5 q* x9 G
下标访问:不支持# i- F3 @! p( I3 Z$ C; F( @
分类:正数哈希、字符串哈希、滚动哈希 0 j0 ?1 F7 z3 X; R# X$ S: }插入时间复杂度:O ( 1 ) O(1)O(1)* p$ G1 m' M; T6 W. _0 ?2 H
查找时间复杂度:O ( 1 ) O(1)O(1)' h' N7 A' L* A2 ~
删除时间复杂度:O ( 1 ) O(1)O(1)9 V, K- L; o- ~; h) q
6 ^% A$ S2 d0 b7 l9 M6 |