题目:
给定一个未排序的整数数组 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
}