題目如下:
給定一個升序的整數數組,查找某一個值在數組中出現的索引號,例如,輸入數組2,3,3,4,4,5;查找的數是3,則返回1,2。時間複雜度要求爲O(logN)。
初次拿到這個題目可以立即想到用二分查找來做,先比較中間的數和要查找的數,如果關鍵字(要查找的數)小於中間的數,那麼在數組的左半部分繼續查找,如果關鍵字大於中間的數,那麼在數組的右半部分繼續查找,如果關鍵字和中間的數相等,那麼先比較中間數字的前一個數字是否和關鍵字相等,如果相等,繼續用關鍵字和前一個數字的前一個數字比較,如果不等,那麼當前數字就是要查找的數字,其所在的索引就是第一次出現的地方。對於結束的索引,可以用類似的方法來做,先比較中間數字的後一個數字是否和關鍵字相等,如果相等,繼續用關鍵字和後一個數字的後一個數字比較,如果不等,那麼當前數字就是要查找的數字,其所在的索引就是最後一次出現的地方。但是這樣做,最壞的情況下,時間複雜度會退化爲O(N),即當數組是同一個數的時候。所以這種方法不是時間上最優的。
其實,本題目是二分查找的變種,我們可以分爲兩步來做,第一步,求得該數字第一次出現的索引,第二步,求得該數字最後一次出現的索引。
首先來看第一次出現的索引怎麼來求,首先比較中間的數和要查找的數,如果關鍵字(要查找的數)小於中間的數,那麼在數組的左半部分繼續查找,如果關鍵字大於中間的數,那麼在數組的右半部分繼續查找,如果關鍵字和中間的數相等,那麼比較中間數字的前一個數字是否和關鍵字相等,如果不相等,那麼當前的中間索引就是第一次出現的索引,如果相等,那麼繼續在前半部分查找。具體的實現代碼如下:
//尋找開始索引
int GetFirstTarget(int A[], int n, int target,int nStart,int nEnd)
{
if (nStart > nEnd)
{
return -1;
}
//中間索引
int nMid = nStart + ( (nEnd-nStart) >> 1);
int nMidData = A[nMid];
while (nStart <= nEnd)
{
if (target > nMidData)
{
nStart = nMid+1;
}
else if (target < nMidData)
{
nEnd = nMid-1;
}
else if (target == nMidData)
{
if ((target != A[nMid-1] && nMid > 0) || nMid == 0)
{
return nMid;
}
else
nEnd = nMid-1;
}
//更新中間值得索引和值
nMid = nStart + ( (nEnd-nStart) >> 1);
nMidData = A[nMid];
}
return -1;
}
//尋找結束索引
int GetSecondTarget(int A[], int n, int target,int nStart,int nEnd)
{
if (nStart > nEnd)
{
return -1;
}
//中間索引
int nMid = nStart + ( (nEnd-nStart) >> 1);
int nMidData = A[nMid];
while (nStart <= nEnd)
{
if (target > nMidData)
{
nStart = nMid+1;
}
else if (target < nMidData)
{
nEnd = nMid-1;
}
else if (target == nMidData)
{
if ((target != A[nMid+1] && nMid < n) || nMid == n-1)
{
return nMid;
}
else
nStart = nMid+1;
}
//更新中間值得索引和值
nMid = nStart + ( (nEnd-nStart) >> 1);
nMidData = A[nMid];
}
return -1;
}
最後就是主功能函數進行調用了,其代碼如下:
vector<int> searchRange(int A[], int n, int target)
{
std::vector<int> vecIndex;
vecIndex.resize(2);
vecIndex[0] = -1;
vecIndex[1] = -1;
if (A == NULL || n <= 0)
{
return vecIndex;
}
vecIndex[0] = GetFirstTarget(A,n,target,0,n-1);
vecIndex[1] = GetSecondTarget(A,n,target,0,n-1);
return vecIndex;
}
兩次查找的時間複雜度都是O(logN),所以總的時間複雜度就是O(logN)。
最後,這個題目還有另外一個變種就是數字在排序數組中出現的次數