1. 程式人生 > >求取一組無序陣列中第k大的數

求取一組無序陣列中第k大的數

方法1.:維持一個大小為k最小堆,

  1. 後面來的數小或者等於堆頂元素,則跳過,;
  2. 後面來的數大於堆頂元素,堆頂元素彈出,新元素加入最小堆

 

最後留下的k個數就是,所有數中前k大的數,堆頂元素就是第k大的數

時間複雜度:由於維持大小為k的堆花費時間為log(k),所以時間複雜度為nlog(k).

程式碼如下:

//構建最小堆,當前數比父結點小就往上冒
void bulidMinHeap(int arr[], int index) {
	while (arr[index] < arr[(index - 1) / 2]) {
		swap(arr[index], arr[(index - 1) / 2]);
		index = (index - 1) / 2;
	}
}


//維持堆的結構,選取出當前數與其左右孩子中最小的數
//如果最小數為自身,退出
//如果為左右孩子,與其交換
void heapify(int arr[], int index, int heapSize) {
	int leftChild = index * 2 + 1;
	int smallestIndex;
	while (leftChild < heapSize) {
		smallestIndex = leftChild + 1 < heapSize && arr[leftChild] > arr[leftChild + 1]
			? leftChild + 1 : leftChild;
		smallestIndex = arr[index] > arr[smallestIndex] ? smallestIndex : index;
		if (smallestIndex == index){
			break;
		}
		swap(arr[index], arr[smallestIndex]);
		index = smallestIndex;
		leftChild = index * 2 + 1;
	}
}


int calKthMin(int arr[], int k, int arrSize) {
	//建立大小為k的最小堆
	for (int i = 0; i < k; i++){
		bulidMinHeap(arr, i);
	}
	//對後面的數進行處理
	for (int i = k; i < arrSize; i++){
		if (arr[i] <= arr[0]) continue;
		else {
			swap(arr[0], arr[i]);
			heapify(arr, 0, k);
		}
	}
	return arr[0];
}

使用c++STL程式碼如下;

注意:優先佇列預設為less<int>引數,less越來越小之意,即最大堆

//priority_queue<int, vector<int>, greater<int>> minHeap;
//priority_queue<int, vector<int>, less<int>> maxHeap;
int calKthMaxByQueue(int arr[], int k, int arrSize) {
	priority_queue<int, vector<int>, greater<int>> minHeap;
	for (int i = 0; i < k; i++) {
		minHeap.push(arr[i]);
	}
	for (int i = k; i < arrSize; i++){
		if (arr[i] <= minHeap.top()) continue;
		else {
			minHeap.pop();
			minHeap.push(arr[i]);
		}
	}
	return minHeap.top();
}

方法2:利用快排中paitition函式的思想,一直paitition到等於軸心數的區域包含arr[arrSize - k]

假設求第100大的數,那個排序之後該數就該在陣列中的下標就該為1000 - 100 = 900 

時間複雜度:每次選擇的pivotNum數的好壞將影響時間,長期期望為O(n);

//partition過程,返回新子區間的上下界
vector<int> partition(int arr[], int leftIndex, int rightIndex) {
	int lessBoundary = leftIndex - 1;
	int biggerBoundary = rightIndex + 1;
	int curIndex = leftIndex;

	int randomIndex = rand() % (rightIndex - leftIndex + 1) + leftIndex;
	swap(arr[randomIndex], arr[rightIndex]);
	int pivotNum = arr[rightIndex];

	while (curIndex < biggerBoundary) {
		if (arr[curIndex] < pivotNum) {
			swap(arr[curIndex++], arr[++lessBoundary]);
		}
		else if (arr[curIndex] > pivotNum) {
			swap(arr[curIndex], arr[--biggerBoundary]);
		}
		else {
			curIndex++;
		}
	}
	vector<int> leftRightIndex(2);
	leftRightIndex[0] = lessBoundary;
	leftRightIndex[1] = biggerBoundary;
	return leftRightIndex;
}


//kthIndex:第k大的數在陣列中應該在的位置
//equalRange為本次partition得到的等於x的區間
//kthIndex在equalRange中停止,否則去子區間尋找
int process(int arr[], int kthIndex, int leftIndex, int rightIndex) {
	vector<int> equalRange = partition(arr, leftIndex, rightIndex);
	if (kthIndex < equalRange[1] && kthIndex > equalRange[0]) {
		return arr[kthIndex];
	}
	else if (kthIndex <= equalRange[0]) {
		return process(arr, kthIndex, leftIndex, equalRange[0]);
	}
	else{
		return process(arr, kthIndex, equalRange[1], rightIndex);
	}
}

int calKthMaxByPartition(int arr[], int k, int arrSize) {
	return process(arr, arrSize - k, 0, arrSize - 1);
}