Discuss:www.cnblogs.com/grandyang/p…
Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.
Notice that the solution set must not contain duplicate triplets.
Example 1:
Input: nums = [-1,0,1,2,-1,-4]
Output: [[-1,-1,2],[-1,0,1]]
Example 2:
Input: nums = []
Output: []
Example 3:
Input: nums = [0]
Output: []
Constraints:
-
0 <= nums.length <= 3000
-
-105 <= nums[i] <= 105
解法一:
暴力求解,会超时。
class Solution {
fun threeSum(nums: IntArray): List<List<Int>> {
if (nums.size < 3) {
return mutableListOf()
}
val result = mutableListOf<List<Int>>()
var isOnceAllZero = false
for (i in 0..nums.size - 3) {
val x = nums[i]
for (j in i + 1..nums.size - 2) {
val y = nums[j]
for (k in j + 1 until nums.size) {
val z = nums[k]
if (x + y + z == 0) {
val item = mutableListOf<Int>()
item.add(x)
item.add(y)
item.add(z)
var isAdd = true
if (x == y && y == z && !isOnceAllZero) {
isOnceAllZero = true
result.add(item)
continue
}
if (result.isNotEmpty()) {
kotlin.run breaking@{
result.forEach {
if (it.containsAll(item)) {
isAdd = false
return@breaking
}
}
}
}
if (isAdd) {
result.add(item)
}
}
}
}
}
return result
}
}
解法二:
利用 Map 存储下标与数组对应下标的值,消除一层循环,但这样还是会超时。
class Solution {
fun threeSum(nums: IntArray): List<List<Int>> {
if (nums.size < 3) {
return mutableListOf()
}
val result = mutableListOf<List<Int>>()
val map = mutableMapOf<Int,Int>()
var isOnceAllZero = false
for (index in nums.indices){
map[index] = nums[index]
}
for (i in 0..nums.size - 3) {
val x = nums[i]
for (j in i + 1..nums.size - 2) {
val y = nums[j]
val z = Math.negateExact((x+y))
if (map.containsValue(z)&& map.values.lastIndexOf(z)>j){
val item = mutableListOf<Int>()
item.add(x)
item.add(y)
item.add(z)
var isAdd = true
if (x == y && y == z && !isOnceAllZero) {
isOnceAllZero = true
result.add(item)
continue
}
if (result.isNotEmpty()) {
kotlin.run breaking@{
result.forEach {
if (it.containsAll(item)) {
isAdd = false
return@breaking
}
}
}
}
if (isAdd) {
result.add(item)
}
}
}
}
return result
}
}
解法三:
对原数组进行排序,然后开始遍历排序后的数组,这里注意不是遍历到最后一个停止,而是到倒数第三个就可以了。这里可以先做个剪枝优化,就是当遍历到正数的时候就 break,为什么呢,因为数组现在是有序的了,如果第一个要 fix 的数就是正数了,则后面的数字就都是正数,就永远不会出现和为 0 的情况了。然后还要加上重复就跳过的处理,处理方法是从第二个数开始,如果和前面的数字相等,就跳过,因为不想把相同的数字 fix 两次。对于遍历到的数,用 0 减去这个 fix 的数得到一个 target,然后只需要再之后找到两个数之和等于 target 即可。用两个指针分别指向 fix 数字之后开始的数组首尾两个数,如果两个数和正好为 target,则将这两个数和 fix 的数一起存入结果中。然后就是跳过重复数字的步骤了,两个指针都需要检测重复数字。如果两数之和小于 target,则将左边那个指针i右移一位,使得指向的数字增大一些。同理,如果两数之和大于 target,则将右边那个指针j左移一位,使得指向的数字减小一些。
class Solution {
fun threeSum(nums: IntArray): List<List<Int>> {
if (nums.size < 3) {
return mutableListOf()
}
val result = mutableListOf<List<Int>>()
nums.sort()
if (nums.first() > 0 || nums.last() < 0) {
return mutableListOf()
}
for (k in 0..nums.size - 3) {
if (nums[k] > 0) {
break
}
if (k > 0 && nums[k] == nums[k - 1]) {
continue
}
val target = 0 - nums[k]
var i = k + 1
var j = nums.size - 1
while (i < j) {
when {
nums[i] + nums[j] == target -> {
val item = mutableListOf<Int>()
item.add(nums[k])
item.add(nums[i])
item.add(nums[j])
result.add(item)
while (i < j && nums[i] == nums[i + 1]) {
++i
}
while (i < j && nums[j] == nums[j - 1]) {
--j
}
++i
--j
}
nums[i] + nums[j] < target -> {
++i
}
else -> {
--j
}
}
}
}
return result
}
}
解法四:
也可以利用 Set 的不能包含重复项的特点来防止重复项的产生,那么就不需要检测数字是否被 fix 过两次。
class Solution {
fun threeSum(nums: IntArray): List<List<Int>> {
if (nums.size < 3) {
return mutableListOf()
}
val result = mutableSetOf<List<Int>>()
nums.sort()
if (nums.first() > 0 || nums.last() < 0) {
return mutableListOf()
}
for (k in 0..nums.size - 3) {
if (nums[k] > 0) {
break
}
val target = 0 - nums[k]
var i = k + 1
var j = nums.size - 1
while (i < j) {
when {
nums[i] + nums[j] == target -> {
val item = mutableListOf<Int>()
item.add(nums[k])
item.add(nums[i])
item.add(nums[j])
result.add(item)
while (i < j && nums[i] == nums[i + 1]) {
++i
}
while (i < j && nums[j] == nums[j - 1]) {
--j
}
++i
--j
}
nums[i] + nums[j] < target -> {
++i
}
else -> {
--j
}
}
}
}
return result.toList()
}
}