數字在排序數組中出現的起始索引號

題目如下:

給定一個升序的整數數組,查找某一個值在數組中出現的索引號,例如,輸入數組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)。

最後,這個題目還有另外一個變種就是數字在排序數組中出現的次數

發佈了107 篇原創文章 · 獲贊 98 · 訪問量 117萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章