题目
有 1、2、3、4 四个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?
分析
可填在百位、十位、个位的数字都是 1、2、3、4,组成所有的排列后再去掉不满足条件的排列。
实例代码
cpp
#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 开始,这样就避免了重复判断十位数字等于百位数字的情况,减少了一些不必要的循环次数。
cpp
#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之使用位运算进行标记
利用位运算来标记已经使用过的数字,从而快速判断是否满足无重复数字的条件。
通过位运算来快速判断数字是否已经被使用过,相比于原始代码中的多次条件判断,在某些情况下可能会提高执行效率,尤其是当需要处理的数字范围较大且重复判断较多时,位运算的优势会更加明显。不过这种方法相对来说理解起来可能会稍微复杂一些。
cpp
#include <stdio.h>
int main() {
int i, j, k;
// 百位数字的循环,从1到4
for (i = 1; i < 5; i++) {
// 使用位运算标记百位数字已经被使用
int used = 1 << (i - 1);
// 十位数字的循环,从1到4
for (j = 1; j < 5; j++) {
// 如果十位数字未被使用(通过位运算判断)
if (!(used & (1 << (j - 1)))) {
// 更新位标记,表示十位数字也被使用了
used |= (1 << (j - 1));
// 个位数字的循环,从1到4
for (k = 1; k < 5; k++) {
// 如果个位数字未被使用(通过位运算判断)
if (!(used & (1 << (k - 1)))) {
// 输出满足条件的三位数
printf("%d%d%d ", i, j, k);
}
}
// 清除十位数字的位标记,以便下一次十位数字循环时重新判断
used &= ~(1 << (j - 1));
}
}
}
return 0;
}
输出结果
cpp
123 124 132 134 142 143 213 214 231 234 241 243 312 314 321 324 341 342 412 413 421 423 431 432
高价出售本人脑子没有用过有意者私。
。