题目描述:
- Given an array of integers, return indices of the two numbers such that they add up to a specific target.
- You may assume that each input would have exactly one solution, and you may not use the same element twice.
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
问题分析:
- 此题可以看做是 另一篇博客Two Sum II - Input array is sorted的升级版问题。原问题数组是已经排好序的,所以可以按照排序的相关性质进行解题。而该题中的数组是一个未排序数组。故有两大思路:
- 方法一:用Two Sum II - Input array is sorted中的hashmap的方法来做。
- 方法二:因为最终返回的是索引,所以先用一个数组indices[i]用来存储nums[i]在原数组的索引,然后对数组进行排序,排序过程中两元素会发生交换,相应索引数组中对应的值也要发生交换;如原nums[3] = {2,3,1},indices[3] = {0,1,2},排序后 nums[3] = {1,2,3},indices[3] = {2,0,1},然后对排序后的数组用Two Sum II - Input array is sorted 提到的双指针做法,注意,若最终找到了等于target的情况,返回的是索引数组indices[]相应的值。
经验教训:
- 只分析时间复杂度,方法一是O(N),方法二仅排序便需要O(nlogN),但最终在leetcode运行时,确是方法二效率高,击败了98.68%的答案,这说明什么?
- 说明由于hash函数计算比较耗时,增大了常数项,导致hashmap方法不如方法二
代码实现:
- 方法二:用改进版的堆排序进行排序
public int[] twoSum(int[] nums, int target) {
if (nums == null || nums.length == 0 || nums.length == 1) {
return new int[] {0,0};
}
//indices[i]用来存储nums[i]的原索引
int[] indices = new int[nums.length];
for (int i = 0; i < indices.length; i++) {
indices[i] = i;
}
//排序,同时也调整索引数组
sort(nums,indices);
int l = 0;
int r = nums.length - 1;
int sum = 0;
while (l < r) {
sum = nums[l] + nums[r];
if (sum == target) {
return new int[] {indices[l], indices[r]};
}else if (sum < target) {
l++;
}else {
r--;
}
}
return new int[] {-1, -1};
}
public void sort(int[] nums, int[] indices ) {
//插入的方法建大根堆(从小到大排序):O(nlogn)效率不高
/*for (int i = 0 ; i < nums.length; i++) {
heapInsert(nums, indices, i);
}*/
//借助于heapify建大根堆(从小到大排序):O(N)
int end = nums.length - 1;
for (int i = ((end - 1) >> 1); i >= 0; i--) {
heapify(nums, indices, i, end);
}
//建堆完毕,每次交换首尾,然后调整堆
swap(nums, indices, 0, end);
for (int i = end - 1; i > 0 ; i--) {
heapify(nums, indices, 0, i);
swap(nums, indices, 0, i);
}
}
//heapInsert:
public void heapInsert(int[] nums , int[] indices, int i) {
while (i > 0) {
int p = (i - 1) / 2;
if (nums[i] >= nums[p]) {
return;
}
swap (nums, indices, i ,p);
i = p;
}
}
//heapify:表示i+1~end都已经满足堆的特性,然后来了个i,将i~end调整成堆
public void heapify(int[] nums, int[] indices, int i, int end) {
for (int j = 2 * i + 1; j <= end; j = 2 * i + 1) {
if (j < end && nums[j] < nums[j + 1]) {
j++;
}
if (nums[i] >= nums[j]) {
return;
}
swap (nums, indices, i, j);
i = j;
}
}
//交换:注意索引数组同样交换
public void swap (int[] nums, int[] indices, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
temp = indices[i];
indices[i] = indices[j];
indices[j] = temp;
}