【问题标题】:How to swap two numbers without using temp variables or arithmetic operations?如何在不使用临时变量或算术运算的情况下交换两个数字?
【发布时间】:2011-04-08 12:29:56
【问题描述】:

这个等式交换两个没有临时变量的数字,但使用算术运算:

a = (a+b) - (b=a);

如果没有算术运算,我怎么能做到这一点?我在考虑异或。

【问题讨论】:

  • 你为什么要这样做?
  • 顺便说一句,该代码的行为是未定义的。 ab 都可以在没有中间序列点的情况下读取和写入。首先,编译器有权在评估a+b 之前评估b=a
  • 为什么对问题的人投反对票?他出于好奇问道。这是一个很好的问题,它让我们中的许多人了解了这种方法以及它的缺点。来吧,伙计们,你们怎么了?一票赞成。
  • 问题中的“算术”方法不正确;它在标记此问题的语言(C、C++、ObjC)中不起作用
  • -1 用于无用的问题。你得到了 XOR 答案,现在你评论说这还不够好。当然不是,这是愚蠢的异或技巧。使用临时变量并完成它。如果您有实际问题或真正的问题,请提出。投票结束。

标签: c++ objective-c c swap


【解决方案1】:
a=a+b;
b=a-b;
a=a-b;

这简单而有效....

【讨论】:

  • 似乎包含一些算术运算符。
  • 这也容易溢出,不像 xor 示例。
  • @Mikhail:溢出将发生在a=a+b。然后b=a-ba=a-b 将发生两个下溢,导致两个变量正确交换值。
  • @displayName 正确吗?在连续 3 次未定义的行为之后,我不会使用该词。
【解决方案2】:

为什么不使用标准库?

std::swap(a,b);

【讨论】:

  • 我认为提问者想要一个涉及较低级别细节的解决方案,而不仅仅是调用现成的函数/库。
  • @sandeepan:这不是我或你来解释问题所在。我们应该用问题的最佳解决方案来回答所陈述的问题(如果这不是 OP 所要求的,那么我们将不会得到复选标记)。但我坚持认为这是解决所提问题的最佳解决方案(甚至 OP 实际上也在玩愚蠢的面试类型问题)。
  • @Martin 我不同意,我们应该在回答之前解释和理解好,并继续思考我们是否解释正确。这个问题不是关于优化/最佳解决方案,它只是一个不同的解决方案。
  • @sandeeoan:问题是我确实理解这个问题以及他想要什么。但是我“给了他他需要的东西,而不是他想要的东西”,因此他应该成为一个更好的开发者。
  • @Martin 在您的回复中没有任何迹象表明这是特定于 C++ 的。例如,异或交换在任何地方都有效。但是 C 标准库中没有与 c++ swap 等效的东西。
【解决方案3】:

在 C 中这应该可以工作:

a = a^b;
b = a^b;
a = a^b;

或者看起来更酷/更怪异:

a^=b;
b^=a;
a^=b;

更多详情请查看this。 XOR 是一个非常强大的运算,它有很多有趣的用法。

【讨论】:

  • 感谢 XOR .. bt XOR 操作非常慢,还有其他方法可以解决这个问题吗?
  • @mr_eclair:在任何典型的平台上,你都不会比 KennyTM 的建议更快。
  • 为了使其更不可读,您可以使用逗号运算符:a^=b, b^=a, a^=b;
  • @mr_eclair:你说它慢是什么意思? 方法 是使用该死的临时变量。 (使用std::swap。)就是这样。这个问题有什么意义?您有实际问题吗?
  • 更糟糕的是:a ^= b ^= a ^= b(或者,更糟糕的是,a^=b^(b=a)
【解决方案4】:

在不使用任何临时存储或算术运算的情况下交换两个数字的最佳方法是将两个变量加载到寄存器中,然后以相反的方式使用寄存器!

您不能直接从 C 中执行此操作,但编译器可能非常有能力为您解决(至少,如果启用了优化) - 如果您编写简单、明显的代码,例如 KennyTM 建议的代码在他的评论中。

例如

void swap_tmp(unsigned int *p)
{
  unsigned int tmp;

  tmp = p[0];
  p[0] = p[1];
  p[1] = tmp;
}

使用 gcc 4.3.2 编译并带有 -O2 优化标志给出:

swap_tmp:
        pushl   %ebp               ;  (prologue)
        movl    %esp, %ebp         ;  (prologue)
        movl    8(%ebp), %eax      ; EAX = p
        movl    (%eax), %ecx       ; ECX = p[0]
        movl    4(%eax), %edx      ; EDX = p[1]
        movl    %ecx, 4(%eax)      ; p[1] = ECX
        movl    %edx, (%eax)       ; p[0] = EDX
        popl    %ebp               ;  (epilogue)
        ret                        ;  (epilogue)

【讨论】:

  • 我不认为变量必须驻留在堆栈上才能被视为临时变量。
【解决方案5】:

我以前没有见过这个 C 解决方案,但我相信有人已经想到了。而且可能比我有更多的发帖自控力。

fprintf(fopen("temp.txt", "w"), "%d", a);
a = b;
fscanf(fopen("temp.txt", "r"), "%d", &b);

没有额外的变量!

它对我有用,但根据 stdio 实现,您可能需要对输出缓冲做一些事情。

【讨论】:

  • 写入文件后不关闭文件并不适用于所有系统。而且太过分了!
【解决方案6】:

使用异或,

void swap(int &a, int &b)
{
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
}

一个带异或的内衬,

void swap(int &a, int &b)
{
    a ^= b ^= a ^= b;
}

这些方法看起来很干净,因为它们不会在任何测试用例中失败,但同样因为(如方法 2)变量的值在同一个序列点内被修改了两次,所以据说它具有未定义ANSI C 声明的行为。

【讨论】:

    【解决方案7】:

    C++11 允许:

    • Swap values:

      std::swap(a, b);
      
    • Swap ranges:

      std::swap_ranges(a.begin(), a.end(), b.begin());
      
    • tie创建左值tuple

      std::tie(b, a) = std::make_tuple(a, b);
      
      std::tie(c, b, a) = std::make_tuple(a, b, c);
      

    【讨论】:

      【解决方案8】:
      a =((a = a + b) - (b = a - b));
      

      【讨论】:

        【解决方案9】:

        除了上述解决方案之外,如果其中一个值超出有符号整数的范围,则可以通过这种方式交换两个变量值

        a = a+b;
        b=b-(-a);
        a=b-a;
        b=-(b);
        

        【讨论】:

          【解决方案10】:

          也可以使用乘法和除法。

           int x = 10, y = 5;
          
           // Code to swap 'x' and 'y'
           x = x * y;  // x now becomes 50
           y = x / y;  // y becomes 10
           x = x / y;  // x becomes 5
          

          【讨论】:

            猜你喜欢
            • 2010-11-12
            • 2023-02-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-12-30
            • 1970-01-01
            相关资源
            最近更新 更多