1.题目:给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次.
说明:你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
难度:简单
思路:
1·可以考虑使用暴力解法,写两个for循环,i从0开始遍历到数组length,第二个循环j从0开始遍历。
public int singleNumber(int[] nums) {
for (int i = 0; i < nums.length; i++) {
int flag = i;
for (int j = 0; j < nums.length; j++) {
if (nums[i] == nums[j] && i != j) {
flag = -1;
break;
}
}
if (flag != -1) return nums[flag];
}
return 0;
}
时间复杂度O(n^2),空间复杂度O(1)
2·考虑到每个元素都只出现一次或者两次,所以最终结果的和应该为sum=2a+2b+…+n,所以我们只要求得sum1=a+b+c+…+n 用2*sum1-sum就等于没有‘对象’的那个数哈哈
public int singleNumber(int[] nums) {
Set<Integer> set = new HashSet<>();
int sum = 0, sum1 = 0, i = 0;
while (i < nums.length) {
set.add(nums[i]);
sum += nums[i++];
}
for (Integer integer : set) {
sum1 += integer;
}
return sum1 * 2 - sum;
}
时间复杂度O(n),空间复杂度O(n)
发现两次都没有达到题目的要求,第一个时间复杂度高了,第二使用了n的空间。
然后去评论里面看看大神的答案哈哈,用到了异或.
先来看看异或是什么东东。
若x是二进制数0101,y是二进制数1011;
0101
1011
1110 则x⊕y=1110
不同位取1,同一位取0。可以发现相同的数取异或等于0,任何数与0异或有等于他本身;
所以 a ^ a ^ b = b; a ^ b ^ a = b; b ^ a ^ b = b;
3·这道题可以巧妙的使用异或来完成题目
a异或a异或b异或b异或c异或c异或d=d
public int singleNumber(int[] nums) {
int result = nums[0];
for (int i = 1; i < nums.length; i++) {
result ^= nums[i];
}
return result;
}
拓展:给你一个长度为 n 的数组,其中只有一个数字出现了奇数次,其他均出现偶数次
对于这种要找出特殊值得题目,一般不会使用指针,下次考虑下异或喔!
从这里就可以看出前两种的方法的局限性了,但是第三种还是仍然适用的。
可以看出即使同一个简单的题目,也有很优化的方法,加油!
2.题目:给你一个长度为 n 的数组,其中只有一个数字出现了大于等于 n/2 次,问如何使用优秀的时空复杂度快速找到这个数字。
思路:
1.使用暴力解法,新建一个map,key为数,value为值。最后遍历map找出value最大的数为众数。
初步可以估计出时间复杂度O(n),空间复杂度0(n)
public int majorityElement(int[] nums) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(nums[i])) map.put(nums[i], map.get(nums[i]) + 1);
else map.put(nums[i], 1);
}
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if (entry.getValue() > nums.length / 2) return entry.getKey();
}
return 0;
}
因为有个特殊条件(有一个数字出现了大于等于 n/2 次)还没有使用----当然可以在取出value++的是否判断是否大于等于 n/2 提前结束循环,当然并不影响时间复杂度和空间复杂度
看了下评论。
2.其实也又想到过这个问题。就是排好序后,最中间的nums[n/2]必定是我们想要的答案,但是忘记java排序的api了,附上代码,很简洁。
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length/2];
}
当然效果也还是一般咯。
3.第三个方法就比较巧妙了:先假设result是第一个数,然后从第二个数开始遍历,遇见相同的就+1,不同的就-1,如果count==0,就result赋值为下一个数,接着遍历,最后result的值 就是结果。该方法的思想是众数一定比其他所有的数加起来的数量要多,就算是众数与其他每一个数相抵消,最后剩下来的也是众数。况且还有其他数之间的抵消,所以剩下来的一定是众数。
int count = 1;
int result = nums[0];
for (int i = 1; i < nums.length; i++) {
count = result == nums[i] ? ++count : --count;
if (count == 0) {
result = nums[i+1];
}
}
return result;
3.编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。
该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
思路:
1.要从最小的数开始遍历,左上角的数。开始遍历,当该行的当前数>target开始下一行,代码很简单:
public boolean searchMatrix(int[][] matrix, int target) {
if (matrix.length == 0 || matrix[0].length == 0) return false;
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[0].length; j++) {
if (matrix[i][j] == target) return true;
if (matrix[i][j] > target) break;
}
}
return false;
}
时间复杂度O(n^2),空间复杂度O(0)
2.在刚才的代码中继续优化,我们上一段代码只用到从左到右的递增,还没用到从上到校的递增。比如我们到了第i行的第j列已经大于我target了,毫无疑问第i+1行第j列也必定大于target。
public boolean searchMatrix(int[][] matrix, int target) {
if (matrix.length == 0 || matrix[0].length == 0) return false;
int right = matrix[0].length;
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < right; j++) {
if (matrix[i][j] == target) return true;
if (matrix[i][j] > target) {
right = j;
break;
}
}
}
return false;
}
时间复杂度O(n^2),空间复杂度O(0)
哈哈哈还不如刚才了,我发现内存比上一次还大,那就继续修改代码试试。
3.使用递归是万万不可能的,因为递归对内存消耗更大。栈帧会不断进入虚拟机栈,直到第一个栈帧出栈。我们试试能不能制定一个规则:遇到a[i][j]>target时要么i++,j–,a[i][j]<target时j++,a[i][j]=target就跳出循环break;
看了看评论:发现还是思路不对
分治法。
左下角的元素是这一行中最小的元素,同时又是这一列中最大的元素。比较左下角元素和目标:
若左下角元素等于目标,则找到
若左下角元素大于目标,则目标不可能存在于当前矩阵的最后一行,问题规模可以减小为在去掉最后一行的子矩阵中寻找目标
若左下角元素小于目标,则目标不可能存在于当前矩阵的第一列,问题规模可以减小为在去掉第一列的子矩阵中寻找目标
若最后矩阵减小为空,则说明不存在
我的理解是找到一个临界分界点,向左或向右接近答案,下午搜搜分治法是什么东东。感觉也很巧妙这个解法,总是能找到一个该向上或先右的规则。真是笨奥哈哈。
target>a[i][j] j++
target<a[i][j] i–
说实话我还是对这个能遍历到所有元素怀疑
public boolean searchMatrix(int[][] matrix, int target) {
if (matrix.length == 0 || matrix[0].length == 0) return false;
int i = matrix.length - 1;
int j = 0;
while (i >= 0 && j < matrix[0].length) {
if (matrix[i][j] < target) j++;
else if (matrix[i][j] > target) i--;
else return true;
}
return false;
}
哈哈哈打扰了,还有更好的办法!!!
看了下评论代码都差不多,我试试他的是多少…
public boolean searchMatrix(int[][] matrix, int target){
if (matrix.length==0)
return false;
int i = matrix.length-1,j=0;
while(i>=0 && j<matrix[0].length){
if (matrix[i][j] == target)
return true;
else if(matrix[i][j]>target)
i--;
else if(matrix[i][j]<target)
j++;
}
return false;
}
说起来就比我快了1ms差距就这么大吗,我看看我的代码可以改改哪?
public boolean searchMatrix(int[][] matrix, int target) {
if (matrix.length==0) return false;
int i = matrix.length - 1;
int j = 0;
while (i >= 0 && j < matrix[0].length) {
if (matrix[i][j] < target) j++;
else if (matrix[i][j] > target) i--;
else if (matrix[i][j] == target) return true;
}
return false;
}