《C程序设计语言》练习3-5
编写函数itob(n,s,b),将整数n转换为以b为底的数,并将转换结果以字符的形式保存到字符串s中,例如,itob(n,s,16)把整数n格式化为十六进制整数保存在s中。
代码:
1 |
|
《C程序设计语言》练习3-5
编写函数itob(n,s,b),将整数n转换为以b为底的数,并将转换结果以字符的形式保存到字符串s中,例如,itob(n,s,16)把整数n格式化为十六进制整数保存在s中。
代码:
1 | #include <stdio.h> |
《C程序设计语言》练习3-3
编写函数expand(s1,s2),将字符串s1中类似于a-z一类的速记符号在字符串s2扩展为等价的完整列表abc…xyz。该函数可以处理大小写字母和数字,并且可以处理a-b-c、a-z0-9与-a-z等类似的情况。作为前导和尾随的-字符原样排印。
思路:
1 | #include <stdio.h> |
思路:在之前代码的前提下,即使最内层的for循环结束,也要记录i的值,便于下次再找到’-‘时接着上次的位置赋值给s2。
1 | #include <stdio.h> |
经过测试,这段代码也解决了a-z0-9的问题。
思路:
1 | #include <stdio.h> |
参考:
插入排序就是在之前排好的有序表中找到合适位置插入。
例如一个乱序的数组:{51,28,38,86,70,90,7,30,40,86,25}
{28,51}{28,38,51}{28,38,51,86}1 | #include <stdio.h> |
例如一个数组:{51,28,38,86,70,90,7,30,40,86,25}
先找到这个数组中最小的数字,也就是7,把它和第一个数字,51交换位置,数组变成了:{(7),28,38,86,70,90,51,30,40,86,25}
再从这个数组的第二个数字开始寻找最小的数字,也就是25,和第二个数字28交换位置:{(7,25),38,86,70,90,51,30,40,86,28}
…
直到排序完成。
1 | #include <stdio.h> |
假设一个数组:{51,28,38,86,70,90,7,30,40,86,25}
51先与28进行比较,51>28,数组变成{28,51,38,86,70,90,7,30,40,86,25}
51再与38比较,51>38,数组变成{28,38,51,86,70,90,7,30,40,86,25}
…
直至第一轮排序完成,数组变为:{28,38,51,70,86,7,30,40,86,25,(90)}
可以看到,经过了第一轮排序,把最大的数字移到数组的末尾,之后不会再对这个数字进行排序。
再次进行第二轮冒泡排序:{28,38,51,70,86,7,30,40,86,25,(90)}
28与38比较,28<38,数组不变。
38再与51比较,38<51,数组不变。
…
86与7比较,86>7,数组改为:{28,38,51,70,7,86,30,40,86,25,(90)}
86与30比较,数组变成:{28,38,51,70,7,30,86,40,86,25,(90)}
86与40比较,数组变成:{28,38,51,70,7,30,40,86,86,25,(90)}
下次比较时,86与86相等,顺序不变,然后86再与25比较:{28,38,51,70,7,30,40,86,25,86,(90)}
经过第二轮冒泡排序,数组变成:{28,38,51,70,7,30,40,86,25,(86,90)}
下次再排序时,后两位不会再修改。
…
直到排序完毕。
1 | #include <stdio.h> |
希尔排序首先需要一个步长,这个步长不是固定的,根据实际情况调整。
在这里我们取步长为d=(5,3,1)。
假设一个数组:{51,28,38,86,70,90,7,30,40,86,25}
步长为5,即51和90为一组,28和7为一组,38和30为一组,86和40为一组,70和86为一组,25单独为一组,本次不对其排序。
进行第一趟排序,不同组进行分别比较:
{51,7,30,40,70,90,28,38,86,86,25}再以步长为3,在第一次排序后的基础上,进行第二趟希尔排序:{51,7,30,40,70,90,28,38,86,86,25}
51和40为一组,7和70为一组,30和90为一组,28和86为一组,38和25为一组,倒数第三个数86单独一组。
{40,7,30,51,70,90,28,25,86,86,38}再以步长为1,进行第三趟希尔排序:
{7,40,30,51,70,90,28,25,86,86,38}{7,30,40,51,70,90,28,25,86,86,38}1 | #include <stdio.h> |
先选出一个节点(取有意义的,或者取第一个),把这个节点放在一个数组的中间,把要排序的数字中小于它的放在它的左侧,右侧都是大于它的数字,最后对两边的数字进行排序。
假设一个数组{51,28,38,86,70,90,7,30,40,86,25}
选取数组中第一个数字作为节点P,最左边为L指针,最右边为R指针。
| 51 | 28 | 38 | 86 | 70 | 90 | 7 | 30 | 40 | 86 | 25 |
|---|---|---|---|---|---|---|---|---|---|---|
| ⬆️P | ||||||||||
| ⬆️L | ⬆️R | |||||||||
P节点在最左边,要先从最右边的R指针开始比较,由于25<51,把25放在左边。⬇️
| 51 | 28 | 38 | 86 | 70 | 90 | 7 | 30 | 40 | 86 | 25 |
|---|---|---|---|---|---|---|---|---|---|---|
| ⬆️P | ||||||||||
| ⬆️L | ⬆️R | |||||||||
| 25 |
接下来L指针右移,由于28<51,把28放在左边,L指针右移。⬇️
| 51 | 28 | 38 | 86 | 70 | 90 | 7 | 30 | 40 | 86 | 25 |
|---|---|---|---|---|---|---|---|---|---|---|
| ⬆️P | ||||||||||
| ⬆️L | ⬆️R | |||||||||
| 25 | 28 |
L指针指向的38<51,把38放在左边,L指针右移。⬇️
| 51 | 28 | 38 | 86 | 70 | 90 | 7 | 30 | 40 | 86 | 25 |
|---|---|---|---|---|---|---|---|---|---|---|
| ⬆️P | ||||||||||
| ⬆️L | ⬆️R | |||||||||
| 25 | 28 | 38 |
L指针指向的86>51,86去R指针所在位置,接下来左移R指针。⬇️
| 51 | 28 | 38 | 86 | 70 | 90 | 7 | 30 | 40 | 86 | 25 |
|---|---|---|---|---|---|---|---|---|---|---|
| ⬆️P | ||||||||||
| ⬆️L | ⬆️R | |||||||||
| 25 | 28 | 38 | 86 |
R指针指向的86>51,86放在右边,R指针左移。⬇️
| 51 | 28 | 38 | 86 | 70 | 90 | 7 | 30 | 40 | 86 | 25 |
|---|---|---|---|---|---|---|---|---|---|---|
| ⬆️P | ||||||||||
| ⬆️L | ⬆️R | |||||||||
| 25 | 28 | 38 | 86 | 86 |
R指针指向的40<51,40移动到L指针所在的位置,接下来右移L指针。⬇️
| 51 | 28 | 38 | 86 | 70 | 90 | 7 | 30 | 40 | 86 | 25 |
|---|---|---|---|---|---|---|---|---|---|---|
| ⬆️P | ||||||||||
| ⬆️L | ⬆️R | |||||||||
| 25 | 28 | 38 | 40 | 86 | 86 |
L指针指向的70>51,70移动到R指针所在的位置,接来下左移R指针。⬇️
| 51 | 28 | 38 | 86 | 70 | 90 | 7 | 30 | 40 | 86 | 25 |
|---|---|---|---|---|---|---|---|---|---|---|
| ⬆️P | ||||||||||
| ⬆️L | ⬆️R | |||||||||
| 25 | 28 | 38 | 40 | 70 | 86 | 86 |
R指针指向的30<51,30移动到L指针所在的位置,接下来右移L指针。⬇️
| 51 | 28 | 38 | 86 | 70 | 90 | 7 | 30 | 40 | 86 | 25 |
|---|---|---|---|---|---|---|---|---|---|---|
| ⬆️P | ||||||||||
| ⬆️L | ⬆️R | |||||||||
| 25 | 28 | 38 | 40 | 30 | 70 | 86 | 86 |
L指针指向的90>51,90移动到R指针所在位置,接下来左移R指针。⬇️
| 51 | 28 | 38 | 86 | 70 | 90 | 7 | 30 | 40 | 86 | 25 |
|---|---|---|---|---|---|---|---|---|---|---|
| ⬆️P | ||||||||||
| ⬆️L | ⬆️R | |||||||||
| 25 | 28 | 38 | 40 | 30 | 90 | 70 | 86 | 86 |
R指针指向的7<51,7移动到L指针位置,接下来右移L指针,L和R指针汇合,把P指针(51)移动到这里。⬇️
| 51 | 28 | 38 | 86 | 70 | 90 | 7 | 30 | 40 | 86 | 25 |
|---|---|---|---|---|---|---|---|---|---|---|
| ⬆️R⬆️L | ||||||||||
| 25 | 28 | 38 | 40 | 30 | 7 | 51 | 90 | 70 | 86 | 86 |
| ⬆️P |
接下来把P指针左侧和右侧作为两个单独的数组,再次排序,直到数组有序。
1 | #include <stdio.h> |
编写一个函数invert(x,p,n),该函数返回对x执行下列操作后的结果值:将x中从第p位开始的第n个(二进制)位求反(即1变成0,0变成1),x的其余各位保持不变。
1 | #include <stdio.h> |
编写一个函数setbits(x,p,n,y),该函数返回对x执行下列操作后的结果值:将x中从第p位开始的n个(二进制)位设置为y中最右边n位的值,x的其余各位保持不变。
例如:setbits(200,5,2,195)
200的二进制数是:1100 1000
这个二进制数的的第p位,开始的第n个二进制位。也就是第5位开始数2位(从右往左数)。
即:1100 1000,加粗的10就是要替换的。
替换为195的二进制数最右边2位的值:1100 0011。
也就是将00替换为11,即替换后的x为1111 1000,即十进制的248。
位操作符(按位与、按位或、按位异或)
移位操作符(左移、右移)
1 | #include <stdio.h> |
(~0 << n) 先把0按位取反,变成二进制位全都是1的二进制数,即1111 1111,再向右移n位(2位),变成1111 1100。
~(~0 << n) 把上面获得的1111 1100 再次按位取反,得到0000 0011。
~(~0 << n) << (p+1-n) 把0000 0011右移p+1-n位,即4位,得到0011 0000。
~(~(~0 << n) << (p+1-n)) 再次按位取反,得到 1100 1111。
x & ~(~(~0 << n) << (p+1-n))x与得到的二进制数进行按位与操作,有0则0,全1为1。得到
1100 1000。
(y & ~(~0 << n)) y与0000 0011进行按位与操作,得到0000 0011。
(y & ~(~0 << n)) << (p+1-n) 把0000 0011右移4位,得到0011 0000。
x & ~(~(~0 << n) << (p+1-n)) | (y & ~(~0 << n)) << (p+1-n)0011 0000和1100 1000进行按位与操作,得到1111 1000,十进制就是248。
編写函数any(s1,s2),将字符串s2中的任一字符在字符串s1中第一次出现的位置作为结果返回。如果s1中不包含s2中的字符,则返回-1。(标准库函数strpbrk具有同样的功能,但它返回的是指向该位置的指针。)
代码:
1 | #include <stdio.h> |
重新编写函数squeeze(s1,s2),将字符串s1中任何与字符串s2中的字符匹配的字符都删除。(把匹配的字符替换为’\0’)
代码:
1 | #include <stdio.h> |
编写函数htoi(s),把由十六进制数字组成的字符串(包含可选的前缀0x或0X)转换为与之等价的整型值。字符串中允许包含的数字包括:09、af、A~F。
代码:
1 | #include <stdio.h> |
上面的代码中,对输入的十六进制数字的每一位分三种情况处理:
《C程序设计语言》练习2-2
1 | for (i=0; i<lim-1 && (c=getchar()) != '\n' && c != EOF ; ++i) |
在不使用运算符&&或||的条件下编写一个与上面的for循环语句等价的循环语句。
先分析给出的原代码:
i = 0。i < lim - 1:确保数组的索引不超出界限。(c = getchar()) != '\n':从输入中读取字符,判断是否是换行符。c != EOF:判断是否到达文件尾。s[i] = c,将字符 c 存入数组 s[i] 中。++i,然后重新检查所有条件。特点:
&&)。getchar() 读到换行符或文件尾(EOF),整个循环停止,且不会再执行 s[i] = c。代码:
1 | for (i=0; i<lim-1; ++i) |
注意:在使用if判断c=getchar()) != '\n'和c != EOF时一定要加else来退出这个for循环,因为题目中循环使用&&的短路特性,当任一条件为假时就停止。
如果不加上break退出循环,虽然结果可能是一致的,但逻辑并不相等。
《C程序设计语言》练习2-1
编写一个程序以确定分别由signed及unsigned限定的char、short、int及long类型变量的取值范围。采用打印标准头文件中的相应值以及直接计算两种方式实现。后一种方法的实现较困难一些,因为要确定各种浮点类型的取值范围。
思路:
1 | #include <stdio.h> |
(char)((unsigned char) ~0 >> 1)~0把数字0进行按位取反,也就是把0000 0000变为1111 1111。~0转换为无符号型:(unsigned char) ~0,这一步是为了下面的右移操作。(unsigned char) ~0 >> 1(char)((unsigned char) ~0 >> 1)对于char型的最小值,要在前面最大值的基础上取反再减1。
在C标准中没有规定char是signed char还是unsigned char,主要取决于编译器。
但一般而言,未指定的数据类型均为signed,如int、char等价于signed int、signed char。
如果想要定义无符号类型,需要在前面加上unsigned。
以char型为例,就是一个占8位的二进制数,去掉符号位,数值位还有7位二进制数。
0-127的二进制表示是从00000000~01111111,全都是0开头的,0是代表正数的符号位,而-128~-1的补码表示为10000000-11111111,全是1开头的,所以1是代表负数的符号位。