128.最长连续序列

140 阅读2分钟

题目:
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

请你设计并实现时间复杂度为 O(n) **的算法解决此问题。
解法: 方法一:hash表 只有数字没被使用,并且存在时,计数器++

// map遍历可以删除,slice遍历时删除小心越界
func longestConsecutive(nums []int) int {
	max := 0
	count := 0
	usedNums := make(map[int]struct{})
	for i := range nums {
		usedNums[nums[i]] = struct{}{}
	}
	for num := range usedNums {
		delete(usedNums, num)
		count ++
		preTmp, nextTmp := num - 1, num + 1
		_, preOk := usedNums[preTmp]
		_, nextOk := usedNums[nextTmp]
		for preOk{
			count ++
			delete(usedNums, preTmp)
			preTmp --
			_, preOk = usedNums[preTmp]
		}
		for nextOk  {
			count ++
			delete(usedNums, nextTmp)
			nextTmp ++
			_, nextOk = usedNums[nextTmp]
			
		}
		if count > max {
			max = count
		}
		count = 0
	}
	return max
}

方法二:hash表优化
只有当前一个数字不存在时,才进行往后遍历,妙啊

func longestConsecutive(nums []int) int {
	maxCount := 0
	numsMap := make(map[int]struct{})
	for i := range nums {
		numsMap[nums[i]] = struct{}{}
	}

	for num := range numsMap {
		curCount := 1
		if _, ok := numsMap[num - 1]; !ok {
			for {
				num ++
				_, ok := numsMap[num]
				if !ok {
					break
				}
				curCount ++
			}
		}
		if curCount > maxCount {
			maxCount = curCount
		}
	}
	
	return maxCount
}

方法三:动态规划
numsLengthMap,key,val意味着num和num的最大长度/或者是遍历标志,遍历过,或者是某个key的left,right。 遍历3 {{2:3},{3:-1},{4:3}} 遍历5的时候,5的left=3:ma[5]=left+1=4 {{2:3},{3:-1},{4:3},{5:4}} 同时更新map[5-3]=4: {{2:4},{3:-1},{4:3},{5:4}}

func longestConsecutive(nums []int) int {
	maxCount := 0
	// 标识key是否遍历过了
	numsMap := make(map[int]int)
	// for i := range nums {
	// 	numsMap[nums[i]] = 1
	// }
	for _, num := range nums {
		_, ok := numsMap[num]
		// num是第一次遍历到
		if !ok {
			left := numsMap[num - 1]
			right := numsMap[num + 1]
			curLength := left + right + 1
			// 标记num和num左右两个数字都已经遍历
			numsMap[num] = -1
			// 更新连续序列最大和最小num的连续序列长度
			numsMap[num - left] = curLength
			numsMap[num + right] = curLength
			if curLength > maxCount {
				maxCount = curLength
			}
		}
	}
	
	return maxCount
}

方法四:并查集
unionfind保存num的最远右边界,因为num包括负数,所以unionfind使用map代替数组。
初始unionfind设置每个元素num的father为自己,然后遍历nums,检查num+1是否存在unionfind,如果存在则更新father的为num+1(使用merge)。最后遍历unionfind,找到最大的rank查看最大值

var unionfind map[int]int
func longestConsecutive(nums []int) int {
	maxCount := 0
	unionfind = make(map[int]int)
	// 初始化unionfind
	for _, num := range nums {
		unionfind[num] = num
	}
	for i := range nums {
		if _, ok := unionfind[nums[i] + 1]; ok {
			merge(nums[i], nums[i] + 1)
		}
	}
	
	for son := range unionfind {
		father := find(son)
		if father - son + 1 > maxCount {
			maxCount = father - son + 1
		}
	}
	// fmt.Println(unionfind)
	return maxCount
}

// 找到x的father
func find(x int) int {
	if x == unionfind[x] {
		return x
	}
	// 不路径压缩就这么返回
	// return find(unionfind[x])
	// 路径压缩:
	originFather := find(unionfind[x])
	// 修改x的father为创世主
	unionfind[x] = originFather
	return unionfind[x]
}

// 简单处理,将x father指向y father
func merge(x, y int) {
	xf := find(x)
	yf := find(y)
	if xf == yf {
		return 
	}
	unionfind[xf] = yf
}