题目
有 1、2、3、4 四个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?
分析
可填在百位、十位、个位的数字都是 1、2、3、4,组成所有的排列后再去掉不满足条件的排列。
实例代码
#include<stdio.h>
// 程序入口
int main()
{// [0] 定义了三个整型变量 `i`、`j` 和 `k`,它们将分别用于表示三位数的百位、十位和个位上的数字int i = 0;int j = i;int k = i;// [0]// [1] 输出`换行`printf("\n");// [1]// [2] 以下为三重循环// [3] 遍历百位上可能出现的数字,从 `1` 开始,到 `4` 结束(因为题目给定的数字是 `1`、`2`、`3`、`4`),每次循环 `i` 的值都会改变,代表不同的百位数字for(i = 1; i < 5; i++) { // [4] 遍历十位上可能出现的数字,同样是从 `1` 到 `4`for(j = 1; j < 5; j++) {// [5] 遍历个位上可能出现的数字,同样是从 `1` 到 `4`for (k = 1; k < 5; k++) {// [6] 确保i、j、k三位数互不相同if ( (i!=k) && (i!=j) && (j!=k) ) { // [7] `printf` 函数中的 `%d%d%d ` 是格式化输出的占位符,分别对应 `i`、`j`、`k` 这三个变量的值,输出时会按照顺序将它们组成一个三位数并打印出来,每个三位数后面跟着一个空格printf("%d,%d,%d ", i, j, k);// [7]}// [6]}// [5]}// [4]}// [3]// [2]// 程序正常结束return 0;
}
以上实例代码存在以下两点可优化的方面:
- 1.存在多余比较:在最内层的循环判断条件中,每次都要进行三次比较
(i!=k) && (i!=j) && (j!=k)
来确保三个数字互不相同。实际上,在外层循环已经对百位和十位数字做了一定的限制(比如百位从1
到4
依次取值,十位在每次百位确定后也从1
到4
取值且与百位不同),所以到内层判断个位数字时,有些比较是多余的。例如,当百位是1
,十位是2
时,在判断个位数字时其实只需要判断个位数字不等于1
且不等于2
即可,而不需要再判断十位不等于百位这种已经确定的情况,这会导致一些不必要的计算开销,尤其在处理更复杂的数字组合或数字范围更大时,这种多余的比较可能会累积影响效率。 - 2.未利用已有条件优化循环范围:在每个循环中,都是从
1
到4
进行完整的遍历。可以考虑根据前面已经确定的数字来缩小后续循环的范围,比如十位数字的循环可以从百位数字+1
开始(确保与百位不同且能减少循环次数),个位数字的循环也可以根据百位和十位数字进一步优化范围,这样能减少不必要的循环迭代次数,从而提高运行效率。
优化方法1之减少不必要的循环次数
在实例代码中,内层循环每次都要从 1 遍历到 4,即使前面已经确定了百位和十位数字,有些情况是可以提前判断并跳过不必要的循环的。
十位数字的循环从 i + 1 开始,这样就避免了重复判断十位数字等于百位数字的情况,减少了一些不必要的循环次数。
#include <stdio.h>int main() {int i, j, k;// 百位数字的循环for (i = 1; i < 5; i++) {// 十位数字的循环,注意要和百位数字不同for (j = 1; j < 5; j++) {if (j!= i) {// 个位数字的循环,要和百位、十位数字都不同for (k = 1; k < 5; k++) {if (k!= i && k!= j) {// 输出满足条件的三位数printf("%d%d%d ", i, j, k);}}}}}return 0;
}
优化方法2之使用位运算进行标记
利用位运算来标记已经使用过的数字,从而快速判断是否满足无重复数字的条件。
通过位运算来快速判断数字是否已经被使用过,相比于原始代码中的多次条件判断,在某些情况下可能会提高执行效率,尤其是当需要处理的数字范围较大且重复判断较多时,位运算的优势会更加明显。不过这种方法相对来说理解起来可能会稍微复杂一些。
#include <stdio.h>int main() {int i, j, k;// 百位数字的循环,从1到4for (i = 1; i < 5; i++) {// 使用位运算标记百位数字已经被使用int used = 1 << (i - 1);// 十位数字的循环,从1到4for (j = 1; j < 5; j++) {// 如果十位数字未被使用(通过位运算判断)if (!(used & (1 << (j - 1)))) {// 更新位标记,表示十位数字也被使用了used |= (1 << (j - 1));// 个位数字的循环,从1到4for (k = 1; k < 5; k++) {// 如果个位数字未被使用(通过位运算判断)if (!(used & (1 << (k - 1)))) {// 输出满足条件的三位数printf("%d%d%d ", i, j, k);}}// 清除十位数字的位标记,以便下一次十位数字循环时重新判断used &= ~(1 << (j - 1));}}}return 0;
}
输出结果
123 124 132 134 142 143 213 214 231 234 241 243 312 314 321 324 341 342 412 413 421 423 431 432
高价出售本人脑子没有用过有意者私。
。