[剑指Offer]-在排序数组中查找数字

题目描述(一)

统计一个数字在排序数组中出现的次数。{1,2,3,3,3,3,4,5} 3出现了4次。

解题思路

  • 当然可以无脑用哈希表或者直接遍历,空间复杂度O(n),时间复杂度O(1)代码略
  • 优化:在找第一个k时,首先也是二分找到中间的k,然后和它上一个数字相比,如果还是k,则在前半段数组继续二分,直到找到中间的数为k且前一个不等于k。
  • 用同样的方法,找最后一个k
    时间复杂度为O(logn)
参考代码:
package offer;

/**
 * 在排序数组中查找数字  统计一个数字在排序数组中出现的次数
 */
public class Offer53 {
    public static void main(String[] args) {
        int nums[] = {2, 2, 2, 2, 2, 2, 3, 5};
        int target = 2;
        System.out.println(GetNumberOfK(nums, target));
    }

    private static int GetFirstK(int[] data, int k, int start, int end) {
        if (start > end) {
            return -1;
        }
        int middIndex = (start + end) / 2;
        int middData = data[middIndex];

        if (middData == k) {
            if ((middIndex > 0 && data[middIndex - 1] != k) || middIndex == 0) {
                return middIndex;
            } else {
                end = middIndex - 1;
            }
        } else if (middData > k) {
            end = middIndex - 1;
        } else {
            start = middIndex + 1;
        }

        return GetFirstK(data, k, start, end);
    }

    private static int GetLastK(int[] data, int k, int start, int end) {
        if (start > end) {
            return -1;
        }

        int middIndex = (start + end) / 2;
        int middData = data[middIndex];

        if (middData == k) {
            if ((middIndex < data.length - 1 && data[middIndex + 1] != k) || middIndex == end) {
                return middIndex;
            } else {
                start = middIndex + 1;
            }
        } else if (middData > k) {
            end = middIndex - 1;
        } else {
            start = middIndex + 1;
        }

        return GetLastK(data, k, start, end);
    }

    public static int GetNumberOfK(int[] data, int k) {
        int number = 0;
        if (data != null && data.length > 0) {
            int first = GetFirstK(data, k, 0, data.length - 1);
            int last = GetLastK(data, k, 0, data.length - 1);

            if (first > -1 && last > -1) {
                number = last - first + 1;
            }
        }
        return number;
    }
}


题目描述(二)

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0 ~ n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

解题思路

-用公式n*(n-1)/2求出0~n-1所有数字之和s1,在求出数组所有数字之和s2,那个不在数组中的数字就是s1-s2的差,这种解法需要O(n)的时间

  • 如果中间元素的值和它的下标相等,那么下一轮只需要找它的右半边
  • 如果中间元素的值和它的下标不等,并且它前一个元素和它的下标相等,意味着这个中间的数字就是第一个值与下标不等的数字。
  • 如果中间元素的值和它的下标不等,并且他前面的元素和它的下标相等不等,那么只需查找左半边。
参考代码:
package offer;

/**
 * 0-n-1中缺失的数字
 */
public class Offer53_2 {
    public static void main(String[] args) {
        int nums[] = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10};
        //System.out.println(getMissingNum(nums));
        System.out.println(getMissingNumH(nums));
    }


    /**
     * 一般  O(n)
     *
     * @param nums
     * @return
     */
    private static int getMissingNum(int[] nums) {
        if (nums == null || nums.length <= 0) {
            return -1;
        }
        int sum1 = 0;
        int sum2 = (nums[0] + nums[nums.length - 1]) * (nums.length + 1) / 2;
        for (int i = 0; i < nums.length; i++) {
            sum1 += nums[i];
        }
        return sum2 - sum1;
    }

    /**
     * 改良
     * {0, 1, 2, 3, 4, 5, 7, 8, 9, 10}
     *
     * @param arr
     * @return
     */
    private static int getMissingNumH(int[] arr) {
        if (arr == null || arr.length <= 0)
            return -1;
        int low = 0;
        int high = arr.length - 1;
        while (low <= high) {
            int mid = (low + high) >> 1;
            if (arr[mid] != mid) {
                if (mid == 0 || arr[mid - 1] == mid - 1)
                    return mid;
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        return -1;
    }
}



题目描述(三)

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0到n-1之内。在范围0到n-1的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

解题思路

  • 先获取数组的中间数,若中间数的值和下标相等,则找到一个满足条件的数;
  • 若中间数的值大于它的下标,则根据数组递增的特性可知:中间元素至少比他的下标大1,而且中间数以后的元素的值至少每个比前面大1,同时它们的下标每次也是增加1,所以右边的这些元素的值都大于它们的下标(至少大1),因此需要继续在左边寻找。
  • 同理,若中间数的值小于它的下标,则中间数左边的那些元素的值也都小于它们的下标,因此需要继续在右边寻找。
参考代码:
package offer;

/**
 * 数组中数值和下标相等的元素
 */
public class Offer53_3 {
    public static void main(String[] args) {
        int nums[] = {-3, -1, 1, 3, 5};
        //System.out.println(getMissingNum(nums));
        System.out.println(getMissingNumH(nums));
    }

    private static int getMissingNumH(int[] arr) {
        if (arr == null || arr.length <= 0)
            return -1;
        int left = 0;
        int right = arr.length - 1;
        while (left <= right) {
            int mid = (left + right) >> 1;
            if (arr[mid] == mid) {
                return mid;
            }
            if (arr[mid] > mid) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return -1;
    }
}




附录

该题源码在我的 ?Github 上面!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章