leetcode_二进制合集

371. 两整数之和

题目

不使用运算符 + 和 - ,计算两整数 a 、b 之和。

示例 1:

输入: a = 1, b = 2
输出: 3

示例 2:

输入: a = -2, b = 3
输出: 1

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/sum-of-two-integers 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解答

两个整数a, b; a ^ b是无进位的相加; a&b得到每一位的进位;让无进位相加的结果与进位不断的异或, 直到进位为0;

思路写在代码中

func getSum(a int, b int) int {
    var sum, carry int 		// 结果 和 进位
    
    sum = a ^ b 			// 不进位加法 0101 ^ 0011 = 0110
    carry = (a&b) << 1 		// 获取进位(全一则一,左移表示进位) 0101&0011 = 0001
    // 还有进位没加进去,递归做加法
    if carry != 0 {
        return getSum(sum, carry)
    }
    return sum
}


// 简写
func getSum(a int, b int) int {
    if b == 0 {
        return a
    }
    
    return getSum(a^b, (a&b)<<1)
}

136. 只出现一次的数字 I

题目

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:
输入: [2,2,1]
输出: 1

示例 2:
输入: [4,1,2,1,2]
输出: 4

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/single-number

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解答一:map统计再遍历

func singleNumber(nums []int) int {
    if len (nums) == 1 {
        return nums[0]
    }

    m := make(map[int]int, len(nums))
    for _, v := range nums {
        m[v]+=1
    }

    for k, v := range m {
        if v == 1 {
            return k
        }
    }
    return 0
}

// 执行用时 :12 ms, 在所有 Go 提交中击败了80.86%的用户
// 内存消耗 :5.9 MB, 在所有 Go 提交中击败了7.72%的用户

解答二:二进制

思路:

  • 数字和自己异或^,结果为0
  • 数字和0异或^,还是为这个数字
func singleNumber(nums []int) int {
    if len(nums) == 1 {
        return nums[0]
    }

    var ret int
    for _, v := range nums {
        ret^=v
    }
    return ret
}
// 执行用时 :8 ms, 在所有 Go 提交中击败了98.44%的用户
// 内存消耗 :4.7 MB, 在所有 Go 提交中击败了100.00%的用户

二进制特性

  • 数字和自己本身异或为 0
    • ^ 相同为 0,不同为 1,不进位)
    • & 相同为 1 不同为 0,不进位)
  • 0 和任何数字异或为这个数字

137. 只出现一次的数字 II

题目

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。

说明:你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,3,2]
输出: 3

示例 2:

输入: [0,1,0,1,0,1,99]
输出: 99

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/single-number-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解答

解法一

使用map,时间和空间复杂度都为O(n)

func singleNumber(nums []int) int {
    sub := make(map[int]int, len(nums))
    
    for _, v := range nums {
        sub[v]++
    }
    
    for k, v := range sub {
        if v == 1 {
            return k
        }
    }
    return -1
}
// 执行用时 :8 ms, 在所有 Go 提交中击败了51.79%的用户
// 内存消耗 :4.3 MB, 在所有 Go 提交中击败了42.86%的用户

解法二

数学运算 3×(a+b+c)−(a+a+a+b+b+b+c)=2c

					 sum
1 1 1 2 3 3 3		 14
1 1 1 2 2 2 3 3 3	 18	
2 2   4/2=2			 18-14=4

解答

func singleNumber(nums []int) int {
    // golang 没有无重复集合set,这里用map代替
    sub := make(map[int]int, len(nums))
    var sum, dep int
    for _, v := range nums {
        // 只加一次
        if sub[v] == 2 {
            dep += v
        } else {
            sub[v]++
        }
        sum += v
    }
   	return sum - dep*3
}
// 执行用时 :8 ms, 在所有 Go 提交中击败了51.79%的用户
// 内存消耗 :4.3 MB, 在所有 Go 提交中击败了42.86%的用户

解法三

评论大神的解法暂时没怎么理解,后面再补充

解答

func SingleNumber2(nums []int) int {
    a,b := 0,0
    for _,v := range nums {
        a = (a^v) & ^b
        b = (b^v) & ^a
    }
    return a
}

解法四

依然是二进制

参考上一题:a ^ a ^ b = b 。

我们假想一种运算 ∆ 可以使得 a ^ a ^ a = 0,且 b ^ 0 = b

1 0 0	a
1 0 0	a
1 0 1	b
1 0 0	a
------
1 0 1	res

从每一位开始分析,需要进行运算之后,把多余的 1 返还给结果

这里就是用到每一位逐次 & (相同为1),然后对 3 取模

  • 比如 1 + 1 + 1 = 3
  • 再对多出来的 3+1 = 4
  • 4 % 3 = 1 ,归还给结果

大部分编程语言默认int的长度为 4字节,即 4 x 8bit = 32位

逐次右移动,计算每一位,然后归还给res,直到32位结束

func SingleNumber2(nums []int) int {
    if len(nums) == 1 {
        return nums[0]
    }
   	var res int
    // 逐次右移
    for i := 0; i < 32; i++ {
        var sum int
        for _, v := range nums {
            // 101 101 110 101 -> 001 001 001 001 -> 
            sum += (v >> i) & 1
            sum %= 3
        }
        // 返还
        res = res | sum(res << 1)
    }
}

260. 只出现一次的数字 III

题目

给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。

示例 :

输入: [1,2,1,3,2,5]
输出: [3,5]

注意:

结果输出的顺序并不重要,对于上面的例子, [5, 3] 也是正确答案。 你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/single-number-iii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

用map统计的方法就不再重复了,比较无脑,空间不可能是常数

还是利用二进制特性:

  • 数字和自己异或^,结果为0
  • 数字和0异或^,还是为这个数字

所以

  • 先将所有数字异或可以得到:这两个数字异或的结果

  • 再从低往高找差异位,再进行分组

  • 组内进行异或,可以找到两个数

    1,  1,  2,  2,  3,  4,  5,  5
    001,001,010,010,011,100,101,101
    逐次异或:
    001,001,010,000,011,111,010,111
      
    末位为1的分组:
    001,001,101,101,011 -> 3(011)
    末位为0的分组:
    010,010,100         -> 4(100)
    
  • 全部异或得到:111,最低位为第 1(即 两个数从此出现差异)。

  • 3 和 4 分别最低位为 1 和 0,正好分到了 xx1 和 xx0 两个组

  • 分组,组内分别再异或即可得到 3 和 4。

解答

func singleNumber(nums []int) []int {
    // 得到两个数异或后的结果
    var bt int
    for _, v := range nums {
        bt ^= v
    }
    var (
        ret1, ret2 int // 两个结果
        index = findFirst1(bt)  // 两个结果的最低差异位
    )
    for _, v := range nums {
        // 按照差异位分组,组内异或便能得到对应结果
        if (v>>index)&1 == 1 {
            ret1 ^= v
        } else {
            ret2 ^= v
        }
    }
    return []int{ret1, ret2}
}

// 为1的最低位(两个数字异或,不同的位为1)
func findFirst1(n int) int {
    var index int
    // 在int32范围内 && 最低位不为1
    for index < 32 && (n&1) == 0 {
        n>>=1
        index++
    }
    return index
}


// 执行用时 :8 ms, 在所有 Go 提交中击败了76.67%的用户
// 内存消耗 :4.2 MB, 在所有 Go 提交中击败了65.22%的用户

Golang实现SET

package main

import (
	"fmt"
	"sort"
	"sync"
)

type Set struct {
	m map[int]bool
	sync.RWMutex
}

func New() *Set {
	return &Set{
		m: map[int]bool{},
	}
}

func (s *Set) Add(item int) {
	s.Lock()
	defer s.Unlock()
	s.m[item] = true
}

func (s *Set) Remove(item int) {
	s.Lock()
	defer s.Unlock()
	delete(s.m, item)
}

func (s *Set) Has(item int) bool {
	s.RLock()
	defer s.RUnlock()
	_, ok := s.m[item]
	return ok
}

func (s *Set) Len() int {
	return len(s.List())
}

func (s *Set) Clear() {
	s.Lock()
	defer s.Unlock()
	s.m = map[int]bool{}
}

func (s *Set) IsEmpty() bool {
	if s.Len() == 0 {
		return true
	}
	return false
}

func (s *Set) List() []int {
	s.RLock()
	defer s.RUnlock()
	list := []int{}
	for item := range s.m {
		list = append(list, item)
	}
	return list
}

func (s *Set) SortList() []int {
	s.RLock()
	defer s.RUnlock()
	list := []int{}
	for item := range s.m {
		list = append(list, item)
	}
	sort.Ints(list)
	return list
}

func main() {
	//初始化
	s := New()

	s.Add(1)
	s.Add(1)
	s.Add(0)
	s.Add(2)
	s.Add(4)
	s.Add(3)

	s.Clear()
	if s.IsEmpty() {
		fmt.Println("0 item")
	}

	s.Add(1)
	s.Add(2)
	s.Add(3)

	if s.Has(2) {
		fmt.Println("2 does exist")
	}

	s.Remove(2)
	s.Remove(3)
	fmt.Println("无序的切片", s.List())
	fmt.Println("有序的切片", s.SortList())

}

分享: