[劍指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 上面!

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