劍指Offer對答如流系列 - 在排序數組中查找數字

面試題53:在排序數組中查找數字

一、題目描述

問題(1) 數字在排序數組中出現的次數
統計一個數字在排序數組中出現的次數。
例如輸入排序數組{1, 2, 3, 3,3, 3, 4, 5}和數字3,由於3在這個數組中出現了4次,因此輸出4。

問題(2)0到n-1中缺失的數字
一個長度爲n-1的遞增排序數組中的所有數字都是唯一的,並且每個數字都在範圍0到n-1之內。在範圍0到n-1的n個數字中有且只有一個數字不在該數組中,請找出這個數字。

問題(3)數組中數值和下標相等的元素
假設一個單調遞增的數組裏的每個元素都是整數並且是唯一的。
請編程實現一個函數找出數組中任意一個數值等於其下標的元素。例如,在數組{-3, -1,1, 3, 5}中,數字3和它的下標相等。

二、問題分析

問題(1)分析
思路一:
對於一個排序數組,最容易想到的方法就是二分查找,不過重要的是找到第一個和最後一個3,所以要改進二分法:對於中間數字等於3的情況,這時可以分類討論如下:

  1. 如果中間數字的前一個數字也等於3,說明第一個3在前面,繼續在前半段查找第一個3;
  2. 如果中間數字的前一個數字不等於3,說明該位置是第一個3;
  3. 如果中間數字的後一個數字也等於3,說明最後一個3在後面,繼續在後半段查找最後一個3;
  4. 如果中間數字的後一個數字不等於3,說明該位置是最後一個3;

思路二:
由於數組是排序的整數,可以直接找k-0.5和k+0.5應該在數組中哪個位置。這種方法一目瞭然。(不要從頭到尾遍歷,走二分查找吧)

問題(2)分析
數組形式大概是這樣的:
在這裏插入圖片描述
由於是排序數組,大都走二分查找算法,結合上圖可知:
當中間數字等於其下標時,我們在後半部分查找;
當中間數字不等於其下標時:

  1. 如果中間數字的前一個數字也不等於其下標,則在前半部分查找;
  2. 如果中間數字的前一個數字等於其下標,則說明中間數字的下標即爲我們所要找的數字。

問題(3)分析
排序數組繼續走二分查找:

  1. 當中間數字等於其下標時,中間數字即爲所求數字;
  2. 當中間數字大於其下標時,在左半部分區域尋找;
  3. 當中間數字小於其下標時,在右半部分區域尋找;

三、問題解答

問題(1)

思路一:

    public int GetNumberOfK(int [] array , int k) {
        if(array==null || array.length<=0) {
            return 0;
        }
        int firstK = getFirstK(array,0,array.length-1,k);
        if(firstK == -1) {
            return 0;
        }
        int lastK = getLastK(array,firstK,array.length-1,k);
        return lastK-firstK+1;
    }

    private int getFirstK(int[] arr, int start, int end,int k){
        if(start>end) {
            return -1;
        }
        int mid = (start+end)>>1;
        if(arr[mid]==k){
            if( mid == 0 ||arr[mid-1]!=k ) {
                return mid;
            } else {
                end = mid-1;
            }
        } else if (arr[mid]<k){
            start = mid+1;
        } else {
            end = mid-1;
        }
        return getFirstK(arr,start,end,k);
    }

    private int getLastK(int[] arr, int start, int end,int k){
        if(start>end) {
            return -1;
        }
        int mid = (start+end)>>1;
        if(arr[mid]==k){
            if(mid==arr.length-1 || arr[mid+1]!=k ) {
                return mid;
            } else {
                start = mid+1;
            }
        } else if(arr[mid]<k){
            start = mid+1;
        } else {
            end = mid-1;
        }
        return getLastK(arr,start,end,k);
    }

思路二:

  public int GetNumberOfK(int [] arr , int k) {
        if(arr==null || arr.length<=0) {
            return 0;
        }
        int first = getLoc(arr, k , k-0.5);
        int last = getLoc(arr,k,k+0.5);
        return last-first;
    }
    
    private int getLoc(int[]arr, int k, double m){ 
        int start=0,end=arr.length-1;
        while(start <= end){
            int mid=(start+end)>>1;
            if(arr[mid]>m){
                end=mid-1;
            } else {
                start=mid+1;
            }
        }
        return start;
    }

問題(2)

    public int getNumberSameAsIndex(int[] arr) {
        if(arr==null || arr.length<=0) {
            // 表示錯誤
            return -1;  
        }
        int low=0;
        int high=arr.length-1;
        while(low<=high) {
            int mid= (high+low)>>1;
            if(arr[mid]>mid) {
                high=mid-1;
            } else if(arr[mid]<mid) {
                low=mid+1;
            } else {
                return mid;
            }
        }
        return -1;
    }

問題(3)

    public int getNumberSameAsIndex(int[] arr) {
        if(arr==null || arr.length<=0) {
            // 表示錯誤
            return -1;  
        }
        int low=0;
        int high=arr.length-1;
        while(low<=high) {
            int mid= (high+low)>>1;
            if(arr[mid]>mid) {
                high=mid-1;
            } else if(arr[mid]<mid) {
                low=mid+1;
            } else {
                return mid; 
            }
        }
        return -1;
    }
發佈了194 篇原創文章 · 獲贊 3472 · 訪問量 53萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章