给出一个区间的集合,请合并所有重叠的区间。
示例 1:
输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入: [[1,4],[4,5]]
输出: [[1,5]]
解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。
我的思路:
首先对输入的初始二维数组进行排序,我的排序方法是插入排序,按子数组的第二个元素从大到小进行排序,避免了随后进行二次排序。
然后利用数学上的两区间有交集进行判断两个相邻的子数组是否为包含或者有交集,此时利用递归的方法比较容易理解。
代码的时间复杂度和空间复杂度都很高,需要进行优化,发上来做个笔记,后面再优化。
优化前,排序使用得是插入排序,仅仅排序得时间复杂度为O(n2),效率极低:
func merge(intervals [][]int) [][]int {
if len(intervals) == 0 {
return intervals
}
res := make([][]int,0)
//对 intervals 进行排序,按每个子数组第一个元素排序
SortInsert(intervals)
//对 intervals 进行二次排序,按每个子数组第二个元素排序
res = SortMerge(intervals,len(intervals),0,res)
return res
}
func Exc(i int ,j int , num [][]int) {
num[i],num[j] = num[j],num[i]
}
//插入排序--二维数组
func SortInsert(num [][]int) {
for i := 0;i<len(num)-1;i++ {
t := i+1
if num[t][1]>num[i][1] {
for l := 0 ; l<=i ; l++{
if num[l][1] < num[t][1] {
Exc(l,t,num)
}
}
}
}
}
// 得到重叠区间
func SortMerge(num [][]int , l int ,i int , res [][]int) [][]int {
if len(num) > 1 && len(res) == 0{
if num[0][0] >= num[1][0] && num[0][0] <= num[1][1] { //此时区间重叠
num[1][1] = num[0][1]
num[0][1] = 'a'
} else if num[0][0]<=num[1][0] && num[0][1]>=num[1][1] {
num[1][1] = num[0][1]
num[1][0] = num[0][0]
num[0][1] = 'a'
}
res = SortMerge(num[1:],l,i+1,res)
}
if num[0][1] != 'a'{
res = append(res,num[0])
}
return res
}
优化后:
将插入排序算法换为快速排序算法,快排得时间复杂度为O(nlogn),而且如果采用三点比较法选取分区点,效率还会进一步提高:
leetcode 计算耗时与网络环境也有关,这是测出来几次得最快速度。
代码如下:
func merge(intervals [][]int) [][]int {
if len(intervals) == 0 {
return intervals
}
res := make([][]int,0)
//对 intervals 进行排序,按每个子数组第一个元素排序
SortQuick(intervals)
//对 intervals 进行二次排序,按每个子数组第二个元素排序
res = SortMerge(intervals,len(intervals),0,res)
return res
}
func Exc(i int ,j int , num [][]int) {
num[i],num[j] = num[j],num[i]
}
//插入排序--二维数组
func SortInsert(num [][]int) {
for i := 0;i<len(num)-1;i++ {
t := i+1
if num[t][1]>num[i][1] {
for l := 0 ; l<=i ; l++{
if num[l][1] < num[t][1] {
Exc(l,t,num)
}
}
}
}
}
//快速排序
func SortQuick(num [][]int) {
//fmt.Println(num)
length := len(num)-1
if length+1 <= 11 {
SortInsert(num)
return
}
var flag int
if length+1 == 0 {
return
}
a := num[0][1]
x := (length+1)/2
b := num[x][1]
c := num[length][1]
flag,mid := FindMid(a,b,c,x,length)
num[flag],num[length] = num[length],num[flag]
flag = length
// fmt.Println(num)
i := 0
for j := 0;j <= length ; j++ {
//排序规则,从大到小排序,且i与j需要不相等,避免与下面得 num[i] 和 num[j]交换形成死循环
if num[j][1] > mid{
//遇到 i == flag 得情况说明分区点提前交换,此时更新key值
num[i] , num[j] = num[j] ,num[i]
i++
// fmt.Println(i,j,num)
}
}
//防止不必要得交换
num[i] , num[flag] = num[flag] , num[i]
//递归调用
// fmt.Println(mid,num)
SortQuick(num[:i])
SortQuick(num[i+1:])
}
//三点取中法选取分区点,此方法比固定取某个key点更加优化一些
//传出两个值,分别为key 和 对应得 value
//c语言可用数组传递
func FindMid(a , b , c , x , l int) (int,int) {
// fmt.Println(a,b,c)
max := a
min := b
if b > max {
max = b
}
if c > max {
max = c
}
if a < min {
min = a
}
if c < min {
min =c
}
// fmt.Println(max,min)
if a < max && a > min {
return 0,a
}
if b < max && b > min {
return x,b
}
if c < max && c > min {
return l,c
}
return 0,a
}
// 得到重叠区间
func SortMerge(num [][]int , l int ,i int , res [][]int) [][]int {
if len(num) > 1 && len(res) == 0{
if num[0][0] >= num[1][0] && num[0][0] <= num[1][1] { //此时区间重叠
num[1][1] = num[0][1]
num[0][1] = 'a'
} else if num[0][0]<=num[1][0] && num[0][1]>=num[1][1] {
num[1][1] = num[0][1]
num[1][0] = num[0][0]
num[0][1] = 'a'
}
res = SortMerge(num[1:],l,i+1,res)
}
if num[0][1] != 'a'{
res = append(res,num[0])
}
return res
}
看来优化很重要,快排果然优秀!