刷题日记-数组

184 阅读4分钟

双重循环用Map替代

求集合的两个数之和的索引

  • 愚蠢的做法
  • 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值target的那两个整数,并返回它们的数组下标。
class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] res = new int[2];
       int diff = 0;
        for(int i = 0; i < nums.length; i ++) {
            diff = target - nums[i];
            for(int j = 0 ; j < nums.length; j ++) {
                if (i == j) {
                    continue;
                } 
                if (nums[j] == diff ) {
                    res[0] = i;
                    res[1] = j;
                    return res; 
                }
            }
        }
        return res;
    }
}
  • 优秀的做法
class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] res = new int[2];
        //使用map可以保证,key不重复,不用去判断
         HashMap<Integer, Integer> maps = new HashMap<>();
        for(int i = 0; i < nums.length; i++) {
            //获取差值,如果差值作为nums数组中的一个值,并且存在,则直接输出
            int diff = target - nums[i];
            if(maps.containsKey(diff)) {
                //先获取差值的坐标,因为差值排查的是前面填进去的;再获取当前值
                res[0] = maps.get(diff);
                res[1] = i;
                return res;
            }
            //key为数组值,value为索引
            maps.put(nums[i], i);
        }
        return res;
    }
}

寻找两个正序数组的中位数

  • 给定两个大小分别为m 和n的正序(从小到大)数组nums1和nums2。请你找出并返回这两个正序数组的中位数。

垃圾解法

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int[] num = new int[nums1.length + nums2.length];
        System.arraycopy(nums1,0,num,0,nums1.length);
        System.arraycopy(nums2,0,num,nums1.length,nums2.length);
        Arrays.sort(num);
        int n = (num.length - 1) / 2;    
        if (num.length == 1) {
            return num[0];
        } else if(num.length % 2 == 0) {
            return (num[n] + num[n + 1]) * 0.5;
        } else {
            return num[n];
        }
    }
}

答案解法

  • 二分查找,在两个有序数组中找中位数,假设此时数组长度为m+n,那么中位数的位置就是奇数情况(m+n)/2+1或者偶数情况(m+n)/2和(m+n)/2+1,排除两个数组中合起来为奇数情况(m+n)/2的个数的数或者偶数情况(m+n)/2-1的个数的数。例如m=2 n=3 m+n=5,中位数位置为3,那么就要排出两个数组前两个数,才能找到第3个;如果m=2 n=3 m+n=6,中位数位置为3 4,那么排除两个数组合起来的前两个数;本质上就是找到两个数组合起来的第k大的数,排除k-1个数
  • 有了上一段的铺垫,可以看出这题的关键就是怎么排除前几个数,题解的过程就是不断比较两个数组,排除两个数组的k-1个数。那就现在就迎来两个问题,每次比较几个数字每次排出几个个数。对于寻找两个数组合起来的第k个位置的数,理想情况是两个数组长度相等,在每个数字前各取k/2个,共排除k-1个;极端情况则是,一个数组中的最小值大于另一个数组的最大值,那么就要把另一个数组的k-1个个数全部排除。综合下来,为了避免排除错误,每次取两个数组中的k/2个元素进行比较,也仅排除一个数组的k/2个元素。
  • 理由是,当A数组第k/2个元素与B数组第k/2个元素,有三种情况A的大于B的A的小于B的A的等于B的,那么这三种情况无论哪种,都可以排除A或者B的前k/2个元素,那么此时要找的元素也就是k-k/2个元素了,以此类推,直到k=1,此时已经排除了k-1个数,进行最后一次比较取出最小的,即是目标数。
  • 根据数组下标的规则,那么第k/2的元素的下标就是k/2-1
  • 边界情况,将一个数组全部排除出去,此时第二个数组此时指向的是下一个数,那么就还需要排除第二个数组k-1个数,也就是从指向第二个数组的位置,往后数k-1,这时指向的就是要找的数
  • 数组指针移动,第一次为index=k/2-1,当比较一次后为index=k/2-1+1 k=k/2,而由于下一次比较需要向后移动k/2-1,那么第二次比较的指针newIndex=index+k/2-1;当index+k/2越界时,需要取newIndex=length-1,那么就要取index+k/2与length的最小值
  • k的变化,当正常情况下,k=k-k/2=k-(newIndex-index+1),当越界情况下,则要减去剩下的个数length-index,而此时newIndex=length-1,那么k=k-(lenght-index)=k-(newIndex-lenght+1)
  • 代码
class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int length1 = nums1.length, length2 = nums2.length;
        int totalLength = length1 + length2;
        if (totalLength % 2 == 1) {
            //合起来的数量为奇数,k=(m+n)/2+1
            int midIndex = totalLength / 2;
            double median = getKthElement(nums1, nums2, midIndex + 1);
            return median;
        } else {
            //为偶数,k=(m+n)/2和(m+n)/2+1
            int midIndex1 = totalLength / 2, midIndex2 = totalLength / 2+1;
            double median = (getKthElement(nums1, nums2, midIndex1) + getKthElement(nums1, nums2, midIndex2)) / 2.0;
            return median;
        }
    }

    public int getKthElement(int[] nums1, int[] nums2, int k) {
        int length1 = nums1.length, length2 = nums2.length;
        int index1 = 0, index2 = 0;
        while (true) {
            // 边界情况
            if (index1 == length1) {
                return nums2[index2 + k - 1];
            }
            if (index2 == length2) {
                return nums1[index1 + k - 1];
            }
            if (k == 1) {
                return Math.min(nums1[index1], nums2[index2]);
            }

            // 正常情况
            int half = k / 2;
            int newIndex1 = Math.min(index1 + half, length1) - 1;
            int newIndex2 = Math.min(index2 + half, length2) - 1;
            int pivot1 = nums1[newIndex1], pivot2 = nums2[newIndex2];
            if (pivot1 <= pivot2) {
                k -= (newIndex1 - index1 + 1);
                index1 = newIndex1 + 1;
            } else {
                k -= (newIndex2 - index2 + 1);
                index2 = newIndex2 + 1;
            }
        }
    }
}