在 Go 语言中,有多种方法可以交换两个整数的值而不使用额外变量:

方法1:使用算术运算(最常用)

package main

import "fmt"

func main() {
    a, b := 10, 20
    fmt.Printf("交换前: a=%d, b=%d\n", a, b)
    
    // 使用加减法
    a = a + b // a = 30
    b = a - b // b = 30 - 20 = 10
    a = a - b // a = 30 - 10 = 20
    
    fmt.Printf("交换后: a=%d, b=%d\n", a, b)
}

方法2:使用异或运算(避免溢出)

package main

import "fmt"

func main() {
    a, b := 10, 20
    fmt.Printf("交换前: a=%d, b=%d\n", a, b)
    
    // 使用异或运算
    a = a ^ b // a = 10 ^ 20
    b = a ^ b // b = (10 ^ 20) ^ 20 = 10
    a = a ^ b // a = (10 ^ 20) ^ 10 = 20
    
    fmt.Printf("交换后: a=%d, b=%d\n", a, b)
}

方法3:Go 特有的元组赋值(最简单)

package main

import "fmt"

func main() {
    a, b := 10, 20
    fmt.Printf("交换前: a=%d, b=%d\n", a, b)
    
    // Go 特有的元组赋值(实际上编译器可能使用临时变量,但代码中没有)
    a, b = b, a
    
    fmt.Printf("交换后: a=%d, b=%d\n", a, b)
}

方法4:使用函数封装

方式1:返回交换后的值

package main

import "fmt"

// 返回交换后的两个值
func swap(a, b int) (int, int) {
    a = a ^ b
    b = a ^ b
    a = a ^ b
    return a, b
}

// 或者使用加减法
func swapArithmetic(a, b int) (int, int) {
    a = a + b
    b = a - b
    a = a - b
    return a, b
}

func main() {
    x, y := 10, 20
    fmt.Printf("交换前: x=%d, y=%d\n", x, y)
    
    x, y = swap(x, y)
    fmt.Printf("交换后: x=%d, y=%d\n", x, y)
}

方式2:使用指针修改原值

package main

import "fmt"

// 使用指针参数
func swapPtr(a, b *int) {
    if a == b { // 避免同一个变量
        return
    }
    *a = *a ^ *b
    *b = *a ^ *b
    *a = *a ^ *b
}

// 更安全的指针交换,防止溢出
func swapPtrArithmetic(a, b *int) {
    if a == b {
        return
    }
    *a = *a + *b
    *b = *a - *b
    *a = *a - *b
}

func main() {
    x, y := 10, 20
    fmt.Printf("交换前: x=%d, y=%d\n", x, y)
    
    swapPtr(&x, &y)
    fmt.Printf("交换后: x=%d, y=%d\n", x, y)
}

方法5:结构体方法交换

package main

import "fmt"

type Pair struct {
    First, Second int
}

// 结构体方法:交换内部两个值
func (p *Pair) Swap() {
    p.First ^= p.Second
    p.Second ^= p.First
    p.First ^= p.Second
}

// 交换两个结构体的值
func SwapPairs(p1, p2 *Pair) {
    // 交换 First
    p1.First ^= p2.First
    p2.First ^= p1.First
    p1.First ^= p2.First
    
    // 交换 Second
    p1.Second ^= p2.Second
    p2.Second ^= p1.Second
    p1.Second ^= p2.Second
}

func main() {
    pair := &Pair{10, 20}
    fmt.Printf("交换前: First=%d, Second=%d\n", pair.First, pair.Second)
    
    pair.Swap()
    fmt.Printf("交换后: First=%d, Second=%d\n", pair.First, pair.Second)
}

方法6:泛型实现(Go 1.18+)

package main

import "fmt"

// 泛型交换函数,仅支持整数类型
func SwapGeneric[T ~int | ~int8 | ~int16 | ~int32 | ~int64 | 
                 ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64](a, b *T) {
    if a == b {
        return
    }
    *a ^= *b
    *b ^= *a
    *a ^= *b
}

// 使用泛型和约束
type Integer interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 |
    ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}

func SwapGeneric2[T Integer](a, b *T) {
    if a == b {
        return
    }
    *a = *a + *b
    *b = *a - *b
    *a = *a - *b
}

func main() {
    var x, y int = 10, 20
    fmt.Printf("交换前: x=%d, y=%d\n", x, y)
    
    SwapGeneric(&x, &y)
    fmt.Printf("泛型交换后: x=%d, y=%d\n", x, y)
    
    // 换回来
    SwapGeneric2(&x, &y)
    fmt.Printf("泛型交换2后: x=%d, y=%d\n", x, y)
}

方法7:匿名函数和闭包

package main

import "fmt"

func main() {
    a, b := 10, 20
    fmt.Printf("交换前: a=%d, b=%d\n", a, b)
    
    // 使用匿名函数
    swapFunc := func(x, y int) (int, int) {
        x = x ^ y
        y = x ^ y
        x = x ^ y
        return x, y
    }
    
    a, b = swapFunc(a, b)
    fmt.Printf("匿名函数交换后: a=%d, b=%d\n", a, b)
    
    // 或者立即执行的闭包
    a, b = func(x, y int) (int, int) {
        x = x + y
        y = x - y
        x = x - y
        return x, y
    }(a, b)
    
    fmt.Printf("闭包交换后: a=%d, b=%d\n", a, b)
}

方法8:位运算的变种

package main

import "fmt"

// 使用不同的位运算组合
func swapBitsVariation(a, b int) (int, int) {
    // 方法1: 标准异或
    a = a ^ b
    b = a ^ b
    a = a ^ b
    return a, b
}

// 另一种写法(单行表达式)
func swapOneLine(a, b int) (int, int) {
    b = a ^ b ^ (a = b)
    return a, b
}

func main() {
    x, y := 10, 20
    fmt.Printf("交换前: x=%d, y=%d\n", x, y)
    
    x, y = swapOneLine(x, y)
    fmt.Printf("单行交换后: x=%d, y=%d\n", x, y)
}

完整示例:性能比较

package main

import (
    "fmt"
    "time"
)

// 基准测试比较
func benchmarkSwap() {
    const iterations = 100000000 // 1亿次
    
    // 方法1: 元组赋值
    start := time.Now()
    a, b := 1, 2
    for i := 0; i < iterations; i++ {
        a, b = b, a
    }
    time1 := time.Since(start)
    
    // 方法2: 异或运算
    start = time.Now()
    a, b = 1, 2
    for i := 0; i < iterations; i++ {
        a = a ^ b
        b = a ^ b
        a = a ^ b
    }
    time2 := time.Since(start)
    
    // 方法3: 算术运算
    start = time.Now()
    a, b = 1, 2
    for i := 0; i < iterations; i++ {
        a = a + b
        b = a - b
        a = a - b
    }
    time3 := time.Since(start)
    
    fmt.Printf("元组赋值: %v\n", time1)
    fmt.Printf("异或运算: %v\n", time2)
    fmt.Printf("算术运算: %v\n", time3)
}

func main() {
    fmt.Println("性能比较 (1亿次交换):")
    benchmarkSwap()
}

实际建议

  1. 生产代码:直接使用 a, b = b, a,这是最简洁、可读性最好的方式
  2. 面试题:如果需要展示不使用临时变量的方法,使用异或运算
  3. 注意事项:
    · 算术方法可能溢出
    · 异或方法对于同一变量会得到 0
    · 乘除法方法要避免除零错误
// Go 中最推荐的交换方式
a, b := 10, 20
a, b = b, a  // 简单明了,编译器会优化

Go 语言的元组赋值 a, b = b, a 在底层通常会被编译器优化,是性能最好且最安全的方式。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐