题目
给定一个数组arr,返回arr的最长无重复元素子数组的长度,无重复指的是所有数字都不相同。
子数组是连续的,比如[1,3,5,7,9]的子数组有[1,3],[3,5,7]等等,但是[1,3,7]不是子数组。
分析
需要计算子数组的最大长度,所以应该有一个数组变量来保存子数组;
为重复使用数组,可以引入变量index标记子数组下标;
为得到子数组的最大值,可以加入变量max保存其最大值;
双重嵌套循环,第一重循环决定从哪个数字开始,第二重循环向后遍历直到出现重复数字
代码
object Demo {
def main(args: Array[String]): Unit = {
println(maxLength(Array[Int](1,2,3,4,5,2,3,6,7,1,8,9)))
}
//计算数组的最长子数组长度
def maxLength(arr: Array[Int]): Int = {
//数组长度为1,则直接返回
if (arr.length == 1) return 1
//保存最长子数组长度
var max = 0
//保存子数组
val subarray = new ArrayBuffer[Int]()
for(i <- 0 until arr.length) {
breakable{
for(j <- i until arr.length) {
//确认是否出现重复数字
if (subarray.contains(arr(j))) {
//出现重复数字,判断子数组长度是否比max大
max = if (subarray.length > max) subarray.length else max
println(subarray.mkString("[",",","]"))
//将子数组清空
subarray.remove(0,subarray.length)
//跳出循环
break
} else {
//未出现重复数字,将数字加入到子数组中
subarray += arr(j)
}
}
}
}
return max
}
}
结果
优化
观察结果,第一次遍历到第三次的遍历明显重复多余,在得知数字重复时,应该更改循环变量,从重复数字的后一位开始遍历,这样就可以在一个循环中完成。同时当遍历到数组的最后一位时可以停止遍历了。
引入数组变量index保存子数组的每个数字的下标,
优化后代码
def maxLength(arr:Array[Int]) : Int = {
//数组长度为1,则直接返回
if (arr.length == 1) return 1
var max = 0
val subarray = new ArrayBuffer[Int]()
//保存子数组中的每个数字下标
val index = new ArrayBuffer[Int]()
var i = 0
while (i < arr.length) {
if (subarray.contains(arr(i)) ) {
//出现重复的数字
println(subarray.mkString("[",",","]"))
//将子数组长度与max比较,得到最大值
max = if (subarray.length > max) subarray.length else max
//重置遍历的变量i,从重复数字的后一位开始遍历
i = index(subarray.indexOf(arr(i))) + 1
//清空子数组和下标数组
subarray.remove(0,subarray.length)
index.remove(0,index.length)
println(s" i --> $i")
} else {
//数字与子数组中数字不重复,添加到子数组
subarray += arr(i)
//保存该数字在数组中的下标
index += i
i += 1
}
if (i == arr.length) {
max = if (subarray.length > max) subarray.length else max
println(subarray.mkString("[",",","]"))
}
}
return max
}
结果
思考
当数组很长时,子数组和下标数组可能会占用很大的内存,并且在arr.contains()的底层中应该也是遍历判断元素是否存在的,所以遍历时间应该差不多。为节省内存,可以将数组下标作为指针,遍历数组。
引入变量head标记子数组开始位置,tail标记子数组结尾位置,以循环遍历确定是否出现重复数字。
改进代码
def maxLength(arr:Array[Int]) : Int = {
//数组长度为1,则直接返回
if (arr.length == 1) return 1
var max = 0
//子数组的头
var head = 0
//子数组的尾
var tail = 1
//遍历变量
var i = 1
while(i < arr.length) {
var j = head
var isRepeat = false
//循环遍历,确定数字是否重复
breakable {
while (j < tail) {
if (arr(j) == arr(i)) {
println(s" head : $head tail : $tail")
//数字出现重复
isRepeat = true
//比较max和子数组长度,得到最大值
max = if (max < (tail - head )) (tail - head ) else max
//重置遍历变量i
i = j + 1
//重置子数组的开始位置和结束位置
head = i
tail = i
//出现重复直接跳出循环
break
}
//当前位置的数字不重复,后移一位,开始下一次判断
j += 1
}
}
//未出现重复数字
if (!isRepeat) {
//子数组向后移动一位
tail += 1
//遍历变量后移一位,开始下一次循环
i += 1
}
//子数组已经移动到最后一位,且子数组中所有的数字都不重复
if (i == arr.length) {
println(s" last head : $head tail : $tail")
//比较max和子数组长度,得到最大值
max = if (max < (tail - head )) (tail - head) else max
}
}
max
}
结果