You are given an integer array nums
and an integer k
.
In one operation, you can pick two numbers from the array whose sum equals k
and remove them from the array.
Return the maximum number of operations you can perform on the array.
Example 1:
Input: nums = [1,2,3,4], k = 5 Output: 2 Explanation: Starting with nums = [1,2,3,4]: - Remove numbers 1 and 4, then nums = [2,3] - Remove numbers 2 and 3, then nums = [] There are no more pairs that sum up to 5, hence a total of 2 operations.
Example 2:
Input: nums = [3,1,3,4,3], k = 6 Output: 1 Explanation: Starting with nums = [3,1,3,4,3]: - Remove the first two 3's, then nums = [1,4,3] There are no more pairs that sum up to 6, hence a total of 1 operation.
Constraints:
1 <= nums.length <= 105
1 <= nums[i] <= 109
1 <= k <= 109
The problem asks to find the maximum number of pairs in an integer array nums
that sum up to a given integer k
. We can solve this efficiently using two approaches: sorting and using a hash table.
This approach leverages the efficiency of binary search implicitly through two pointers.
Sort the array: First, we sort the input array nums
in ascending order. This step allows us to efficiently find pairs that sum to k
. Sorting takes O(n log n) time.
Two Pointers: We use two pointers, l
and r
, initialized to the beginning and end of the sorted array, respectively.
Iteration: The core logic lies in the while loop:
s = nums[l] + nums[r]
.k
, we've found a pair. We increment the ans
(our counter for successful pairs), move l
one step to the right, and r
one step to the left.k
, we need a smaller sum, so we decrement r
(move to a smaller number).k
, we need a larger sum, so we increment l
(move to a larger number).l
and r
cross each other (l >= r
), indicating we've checked all possible pairs.Return ans
: Finally, we return the total count of pairs found, ans
.
Time Complexity: O(n log n) due to sorting. The two-pointer iteration is O(n). Space Complexity: O(log n) or O(1) depending on the sorting algorithm used (in-place sorting algorithms like merge sort have O(log n) space complexity due to recursion; some implementations might have O(1) in-place).
This approach uses a hash table (or dictionary/map) to store the frequency of each number in the array.
Frequency Count: We iterate through nums
and store the frequency of each number in a hash table cnt
. This takes O(n) time.
Pair Check: For each number x
in nums
, we check if k - x
exists in cnt
.
k - x
exists, we have found a pair. We increment ans
and decrement the frequency of k - x
in cnt
. This ensures we don't reuse the same number in multiple pairs.k - x
doesn't exist, we simply increment the frequency of x
in cnt
.Return ans
: After iterating through all numbers, we return ans
.
Time Complexity: O(n) because both iterating through nums
and checking the hash table take linear time.
Space Complexity: O(n) in the worst case to store the frequency of all numbers in the hash table.
Approach 1 (Sorting):
from typing import List
class Solution:
def maxOperations(self, nums: List[int], k: int) -> int:
nums.sort()
l, r = 0, len(nums) - 1
ans = 0
while l < r:
s = nums[l] + nums[r]
if s == k:
ans += 1
l += 1
r -= 1
elif s > k:
r -= 1
else:
l += 1
return ans
Approach 2 (Hash Table):
from collections import Counter
class Solution:
def maxOperations(self, nums: List[int], k: int) -> int:
cnt = Counter(nums)
ans = 0
for x in nums:
if cnt[k - x] > 0:
ans += 1
cnt[k - x] -= 1
if cnt[x] > 0 :
cnt[x] -= 1
return ans
Both approaches correctly solve the problem. The choice depends on the specific constraints and performance requirements. If memory is a major concern and n is very large, the sorting approach might be preferred because while it's O(n log n), the constant factor might be smaller than the hash table approach's O(n) with its higher constant factors depending on the hash table implementation. Otherwise, the hash table approach is generally faster due to its linear time complexity.