Go语言:LeetCode--数组中重复的数据

题目:给定一个题目:给定一个整数数组 a ,其中 1 <= a[i] <= n (n为数组长度),其中有些元素出现 两次 而其他元素出现 一次
找到所有出现 两次 的元素。
你可以不用任何额外空间并在 O(n) 时间复杂度内解决这个问题吗?
示例:
输入: [ 4, 3, 2, 7, 8, 2, 3, 1 ]
输出: [ 2, 3 ]

解法一:使用 map 。用 map 中的 key 值存储数组中的每个元素,value 存储其对应出现次数。最后输出 value 值为 2 的 key 即可。

func findDuplicates(nums []int) []int {
	var mapper map[int]int
	mapper = make(map[int]int)
	var i int
	for i = 0; i < len(nums); i++ {
		mapper[nums[i]]++
	}
	var res []int
	for key, value := range mapper {
		if value == 2 {
			res = append(res, key)
		}
	}
	return res
}

时间复杂度:线性遍历数组和 map,时间复杂度为 O(n) .
空间复杂度:申请了额外的空间 map,空间复杂度为 O(n) .

解法二:排序思想。先对数组进行排序,然后遍历数组,前后两个元素相等,说明该元素出现次数为 2 。

func findDuplicates(nums []int) []int {
	sort.Ints(nums)
	var i int
	var res []int
	for i = 1; i < len(nums); i++ {
		if nums[i] == nums[i-1] {
			res = append(res, nums[i])
		}
	}
	return res
}

时间复杂度:对数组进行排序的时间复杂度为 O(nlgn) ,遍历数组的时间复杂度                      为 O(n) .所以总的时间复杂度为 O(nlgn) .
空间复杂度:没有申请额外的空间,空间复杂度为 O(1) .

上述两种方法比较容易想到,但是均没有满足时间复杂度为O(n),空间复杂度为O(1)的要求。下面介绍另一种思想:

解法三:利用索引号,巧妙进行哈希。数组下标值的范围为 0 ~ n-1,数组中的元素值 a[i] 的范围为 1 ~ n 。因此,我们可以利用元素值 (a[i] - 1) 作为下标进行哈希。

(1)对于只出现一次的元素 a[i] 而言,元素 a [(a[i]-1)] 只会访问一次。
(2)对于出现两次的元素 a[i] 而言,元素 a [(a[i]-1)] 会访问两次。
(3)为了甄别出哪个元素被访问两次,每次访问 a [(a[i]-1)] 时,将 a [(a[i]-1)]     变成 - a[(a[i]-1)] ,变成了负数后,方便识别该数是否被访问过。

具体如以下示例:
Go语言:LeetCode--数组中重复的数据_第1张图片

  1. a[0] = 4,找到 a[| a[0] |-1],即 a[4-1] 处的元素 a[3] = 7,将 a[3] 置为 -7 表示数值 为 4 的元素出现了一次。此时的数组内容为:
               [ 4 , 3 , 2 , -7 , 8 , 2 , 3 , 1 ]
  2. a[1] = 3,找到 a[| a[1] |-1],即 a[3-1] 处的元素 a[2] = 2,将 a[2] 置为 -2 表示数值为 3 的元素出现了一次。此时的数组内容为:
               [ 4 , 3 , -2 , -7 , 8 , 2 , 3 , 1 ]
  3. a[2] = 2,找到 a[| a[2] |-1],即 a[2-1] 处的元素 a[1] = 3,将 a[1] 置为 -3 表示数值为 2 的元素出现了一次。此时的数组内容为:
               [ 4 , -3 , -2 , -7 , 8 , 2 , 3 , 1 ]
  4. a[3] = -7,找到 a[ | a[3] |-1],即 a[7-1] 处的元素 a[6] = 3,将 a[6] 置为 -3 表示数值为 7 的元素出现了一次。此时的数组内容为:
               [ 4 , -3 , -2 , -7 , 8 , 2 , -3 , 1 ]
  5. a[4] = 8,找到 a[ | a[4] |-1],即 a[8-1] 处的元素 a[7] = 1,将 a[7] 置为 -1 表示数值为 8 的元素出现了一次。此时的数组内容为:
               [ 4 , -3 , -2 , -7 , 8 , 2 , -3 , -1 ]
  6. a[5] = 2,找到 a[ | a[5] |-1],即 a[2-1] 处的元素 a[1] = -3,此时a[1]为负数,表示之前已经有一个 2 访问了该下标,所以数值 2 是出现了两次,将 a[1] 置为 3 。此时的数组内容为:
               [ 4 , 3 , -2 , -7 , 8 , 2 , -3 , -1 ]
  7. a[6] = -3,找到 a[ | a[3] |-1],即 a[3-1] 处的元素 a[2] = -2,此时a[2]为负数,表示之前已经有一个 3 访问了该下标,所以数值 3 是出现了两次将 a[2] 置为 2 。此时的数组内容为:
               [ 4 , 3 , 2 , -7 , 8 , 2 , -3 , -1 ]
  8. a[7] = 1,找到 a[ | a[3] |-1],即 a[1-1] 处的元素 a[0] = 4,将 a[0] 置为 -4 表示数值为 1 的元素出现了一次。此时的数组内容为:
               [ -4 , 3 , 2 , -7 , 8 , 2 , -3 , -1 ]

实现代码如下:

func getAbs(num int) int {
	if num >= 0 {
		return num
	}
	return -num
}
func findDuplicates(nums []int) []int {
	var i int
	var res []int
	for i = 0; i < len(nums); i++ {
		index := getAbs(nums[i]) - 1 //获取数值nums[i]对应的下标index
		if nums[index] <= 0 {        //如果nums[index]为负数,表示nums[i]曾经出现过
			res = append(res, getAbs(nums[i]))
		}
		nums[index] = -nums[index] //访问一次,变为相反数,作为标记
	}
	return res
}

时间复杂度:仅仅遍历了一遍数组,时间复杂度为 O(n).
空间复杂度:没有申请额外的空间,空间复杂度为 O(1).

你可能感兴趣的:(Leetcode)