1. Two Sum LeetCode(0ms 极致探索)

题目描述

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.

题目大意

给定一个整数数组,两个数之和等于目标值(target)则返回这两个数的下标。

可以确定的是,每个样例有且仅有一个解,同一元素不能使用两次。

Example:

Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

Subscribe to see which companies asked this question.


题解:

1. 暴力求解,这是最为简单易行的解法,使用双重循环分别枚举两个数A和B,直到找到A + B = target为止,时间复杂度O(n^2);

    (实际时间使用情况,由于测试样例的缘故,正向枚举需要几百ms时间,而逆向则只需要29ms[Java])

2. 借助Hash求解,这是较为标准的解法,首先将数组中每个数存入Hash表,然后枚举数组内数字(假设当前枚举数字为A,则在Hash表中查询target- A是否存在,找到则返回解),枚举一趟中一定因为有解的存在而返回,时间复杂度O(n);

    (实际时间使用情况,10ms[Java]以内)

3. 排序后搜索,将数组排序使之成为有序数组,一个指针[start]指向数组第一个元素,另一个指针[end]指向数组最后一个元素。如果两数之和恰好等于target,则返回;如果两数之和比target小,说明当前和较小,将start指向下一个元素(end已经不能后移);如果两数之和比target大,则将end指向前一个元素。由此,每次问题都缩小规模,直至最终的答案。时间复杂度O(nlogn)

    (实际时间使用情况,5ms[Java]左右,情况甚至好于Hash,原因在于数据规模不大的情况下,内存分配等占用的时间超过实际算法计算时间)


解法2代码:

Java:

public class Solution {
    public int[] twoSum(int[] nums, int target) {
        // 创建一个map以储存数字
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        for (int i = 0; i < nums.length; ++i) {
        	if (map.containsKey(target - nums[i])) {
        		return new int[]{map.get(target - nums[i]), i};
        	}
        	map.put(nums[i], i);
        }
        return null;
    }
}

C++:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        // map存储数组信息 vector存储结果
		map<int, int> m;
		vector<int> ans;
		// 遍历数组查询结果
		for (int i = 0; i < nums.size(); ++i) {
			// 查找到配对元素
			if (m.find(target - nums[i]) != m.end()) {
				ans.push_back(m[target - nums[i]]);
				ans.push_back(i);
				return ans;
			}
			// 否则将现在元素插入map
			m.insert(pair<int, int>(nums[i], i));
		}
		return ans;
    }
};


极致速度探索1(双指针 5ms):

public class Solution {
	private int findIndexHead(int[] nums, int n) {
		for (int i = 0; i < nums.length; ++i) {
			if (nums[i] == n) {
				return i;
			}
		}
		return -1;
	}
	private int findIndexTail(int[] nums, int n) {
		for (int i = nums.length - 1; i > -1; --i) {
			if (nums[i] == n) {
				return i;
			}
		}
		return -1;
	}
    public int[] twoSum(int[] nums, int target) {
    	// 拷贝数组
    	int[] cNums = Arrays.copyOf(nums, nums.length);
    	Arrays.sort(cNums);
    	// 从前面和后面分别开始遍历
    	for (int start = 0, end = cNums.length - 1; start < end;) {
    		if (cNums[start] + cNums[end] == target) {
    			return new int[]{findIndexHead(nums, cNums[start]),
    							 findIndexTail(nums, cNums[end])};
    		}
    		else if (cNums[start] + cNums[end] < target) {
    		    start++;
    		}
    		else {
    		    end--;
    		}
    	}
        return null;
    }
}

极致探索速度2:

探索1的速度已经比较快了,但是与0ms差距较大,考虑到系统HashMap涉及ReHash过程,手写一个OpenHash,固定Hash表长度,实际慢了1ms,为6ms。。。

public class Solution {
		// 手写Hash结构
	class Hash{
		// 保存元素 以OpenHash的方式处理Hash冲突
		private class Elem {
			public int key;
			public int value;
			public Elem next;
			public Elem(int key, int value) {
				this.key = key;
				this.value = value;
				this.next = null;
			}
		}
		// Hash数组
		private Elem[] arr;
		// 构造函数
		public Hash(int len) {
			arr = new Elem[len];
		}
		// 插入元素方法
		public void put(int key, int value) {
			int index = key % arr.length;
			if (arr[index] == null) {
				arr[index] = new Elem(key, value);
				return;
			}
			Elem last = arr[index];
			while (last.next != null) {
				last = last.next;
			}
			last.next = new Elem(key, value);
		}
		// 获取元素方法
		public int get(int key) {
			int index = key % arr.length;
			if (arr[index] == null) {
				return Integer.MIN_VALUE;
			}
			for (Elem last = arr[index]; last != null; last = last.next) {
				if (last.key == key) {
					return last.value;
				}
			}
			return Integer.MIN_VALUE;
		}
	}
    public int[] twoSum(int[] nums, int target) {
    	// 查找最小值
    	int minVal = nums[0];
    	for (int i = 1; i < nums.length; ++i) {
    		minVal = Math.min(minVal, nums[i]);
    	}
    	minVal = -minVal;
    	// 处理target以及nums数组
    	target += 2 * minVal;
    	for (int i = 0; i < nums.length; ++i) {
    		nums[i] += minVal;
    	}
    	// 使用定义的Hash结构
    	Hash hash = new Hash(1991);
    	for (int i = 0; i < nums.length; ++i) {
    	    if (target - nums[i] < 0) {
    	        continue;
    	    }
    		if (hash.get(target - nums[i]) != Integer.MIN_VALUE) {
    			return new int[]{hash.get(target - nums[i]), i};
    		}
    		hash.put(nums[i], i);
    	}
        return null;
    }
}

极致速度探索版3:

考虑使用BitSet一次创建足够多的空间,将Hash过程变成数组找寻下标的方式,

结果是,仍然为6ms。。。

public class Solution {

	private int find(int[] nums, int target) {
		for (int i = 0; i < nums.length; ++i) {
			if (nums[i] == target) {
				return i;
			}
		}
		return -1;
	}
    public int[] twoSum(int[] nums, int target) {
    	// 寻找最小和最大值
    	int minVal = nums[0];
    	int maxVal = nums[0];
    	for (int i = 1; i < nums.length; ++i) {
    		minVal = Math.min(minVal, nums[i]);
    		maxVal = Math.max(maxVal, nums[i]);
    	}
    	// 开一个BitSet
    	BitSet set = new BitSet(maxVal - minVal + 1);
    	for (int i = 0; i < nums.length; ++i) {
    		int nToFind = target - minVal - nums[i];
    		if (nToFind < 0) {
    			continue;
    		}
    		if (set.get(nToFind)) {
    			return new int[]{find(nums, target - nums[i]), i};
    		}
    		set.set(nums[i] - minVal);
    	}
        return null;
    }

}


极致探索版4:先探到这把,洗洗睡了。。。


猜你喜欢

转载自blog.csdn.net/baidu_23318869/article/details/71155613