【Swift】LeetCode 56. 合并区间
56. 合并区间

题目描述

思路 and Swift 题解
这道题目的思路一样很简单。首先,我们基于intervals数组的左端点对整个区间进行一道升序排序。之后,我们从排序后的intervals的第一个元素(下标为0)出发,令l = intervals[0][0],r = intervals[0][1],从下一个元素开始(下标为1)开始对整个数组进行遍历。
如果当前区间i的左端点比r更大,就说明当前的[l, r]和这个区间没有交集,此时将[l, r]保存到答案当中,并更新l = intervals[i][0],r = intervals[i][1]。否则说明当前区间与[l, r]有交集。需要注意的一个点是,当前区间的右端点intervals[i][1]可能比r大,由于这两个区间已经合并,因此应该令r = max(r, intervals[i][1])。
循环结束之后,我们还需要将最后一对[l, r]插入到答案当中,即可返回。
思路非常清晰,回到 Swift 语法本身,我们应该如何对数组基于自定义的规则(本题的规则就是:对数组当中存储的数组进行排序,基于数组中区间的第一个元素,也就是左端点,进行降序排序)。
在 Swift 当中,标准库提供了一个名为sorted(by:)的方法,Array对象可以使用该方法对其中的元素进行排序。它的参数接受一个函数,这个函数的形参有两个,类型都是Array当中的元素类型,返回值是Bool,基于Bool值告知该方法如何对Array当中的两个元素进行排序。一种朴素的写法是:
func sortFunc(_ i1: [Int], _ i2: [Int]) -> Bool {return i1[0] < i2[0]
}var intervals = intervals.sorted(by: sortFunc)
// ... ... ...
这样的写法一定是没问题的,我们在 C++ 和 Golang 当中也通常采用这种方式来自定义数组的排序规则。但 Swift 有更简便的用法,这里就要引入 Swift 闭包的概念。闭包表达式的语法形如:
{ (parameter) -> return type instatements
}
sorted(by:)方法可以接受一个闭包表达式作为数组排序的规则:
var intervals = intervals.sorted(by: { (i1: [Int], i2: [Int]) -> Bool in return i1[0] < i2[0] })
不得不说,Swift 的闭包写法比较奇怪,不经过大量的练习,感觉很难写好,不如 Python 当中的 Lambda 表达式或 TypeScript 当中的 () => {}方便。
Swift 的闭包表达式可以基于上下文来推断参数的类型,sorted(by:)方法将会被一个元素本身是数组的数组调用,因此该方法将要接受的函数的签名显然是([Int], [Int]) -> Bool。基于这个函数签名,闭包可以推断出参数的类型就是[Int],因此在写闭包表达式的时候可以忽略类型:
var intervals = intervals.sorted(by: { i1, i2 in return i1[0] < i2[0] })
我们甚至可以省略return:
var intervals = intervals.sorted(by: { i1, i2 in i1[0] < i2[0] })
此外,Swift 自动为内联闭包提供了参数名称缩写的功能,可以直接通过$0,$1,$2来顺序调用闭包参数。在闭包表达式当中使用参数名称缩写(以$0为例)时,可以在闭包定义中省略参数列表,参数名称缩写的类型也会基于函数签名的类型自动推断。in关键字可以被省略:
var intervals = intervals.sorted(by: { $0[0] < $1[1] })
Swift 还有一种尾随闭包的用法,指的是书写在函数圆括号之后的闭包表达式,函数支持将其作为最后一个参数进行调用。使用尾随闭包时,不需要写出它的函数标签。针对sorted(by:)方法,它只有一个参数,此时使用尾随闭包可以忽略(),至此我们便可以引出 Swift 当中最常用的排序方法,那就是基于在尾随闭包当中定义规则的排序方法:
var intervals = intervals.sorted { $0[0] > $1[0] }
完整的 Swift 题解是:
class Solution {func merge(_ intervals: [[Int]]) -> [[Int]] {var intervals = intervals.sorted(by: { i1, i2 in i1[0] < i2[0] })var ans = [[Int]]()var l = intervals[0][0], r = intervals[0][1]for i in 1..<intervals.count {if r < intervals[i][0] {ans.append([l, r])l = intervals[i][0]r = intervals[i][1]} else {r = max(r, intervals[i][1])}}ans.append([l, r])return ans}
}
