第k大(小)數,尋找最小的k個數(進一步要求順序與原陣列中元素順序一致)
阿新 • • 發佈:2019-01-24
215. Kth Largest Element in an Array(leetcode)
快排思路求解,ac程式碼
class Solution {
public:
int partion(vector<int> &nums, int left, int right)
{
int pivot = nums[left];
while (left < right){
while (left < right && nums[right] >= pivot)
--right;
nums[left] = nums[right];
while (left < right && nums[left] < pivot)
++left;
nums[right] = nums[left];
}
nums[left] = pivot;
return left;
}
int qsort(vector<int> &nums, int left, int right, int k){
if (left > right)
return -1;
int pos = partion(nums, left, right);
int leftSize = pos - left + 1;
if (leftSize == k){
return nums[pos];
}
else if (leftSize < k){
return qsort(nums, pos + 1, right, k - leftSize);
}
else{
return qsort(nums, left, pos - 1 , k);
}
}
int findKthLargest(vector<int>& nums, int k) {
int len = nums.size();
return qsort(nums, 0, len - 1, len - k + 1); // 第len - k + 1 從小到大
}
};
我的ac程式碼:
class Solution {
public:
void down2up(vector<int> &bigheap, int no,int val)
{
bigheap[no] = val;
// 從根節點調整到 子節點
int tmpno = no;
while (tmpno > 1){
int fa = tmpno / 2;
if (fa >= 1)
{
if (bigheap[tmpno] > bigheap[fa])
{
int tmpVal = bigheap[tmpno];
bigheap[tmpno] = bigheap[fa];
bigheap[fa] = tmpVal;
tmpno = fa;
}
else{
break;
}
}
else{
break;
}
}
}
void up2downAdjust(vector<int> &nums,int n) // 下標範圍是 1 - n
{
int fa = 1;
while (fa <= n){
int lson = 2 * fa;
int rson = 2 * fa + 1;
if (lson > n && rson > n){
return;
}
else if (lson <= n && rson > n)
{
if (nums[lson] > nums[fa]){
int tmp = nums[fa];
nums[fa] = nums[lson];
nums[lson] = tmp;
fa = lson;
}
else{
return;
}
}
else if (lson > n && rson <= n)
{
if (nums[rson] > nums[fa]){
int tmp = nums[fa];
nums[fa] = nums[rson];
nums[rson] = tmp;
fa = rson;
}
else{
return;
}
}
else{
if (nums[fa] >= nums[lson] && nums[fa] >= nums[rson])
return;
else{
if (nums[lson] >= nums[rson]){
int tmp = nums[fa];
nums[fa] = nums[lson];
nums[lson] = tmp;
fa = lson;
}
else{
int tmp = nums[fa];
nums[fa] = nums[rson];
nums[rson] = tmp;
fa = rson;
}
}
}
}
}
// O(nlogk)的時間複雜度
int findKthLargest(vector<int>& nums, int k) {
int len = nums.size();
int n = len - k + 1; // 第len - k + 1 從小到大 構建大根堆
// 建立大根堆
vector<int> bigheap(n + 1);
for (int i = 0; i < n; i++)
{
down2up(bigheap, i + 1, nums[i]);
}
for (int i = n; i < len; i++) //後續元素組個比較
{
if (nums[i] < bigheap[1]){ // 比堆頂小 與棧頂交換 然後調整堆
bigheap[1] = nums[i];
up2downAdjust(bigheap, n);
}
}
return bigheap[1]; //返回堆頂
}
};
分治思想,借用快速排序思路
一趟排序,num個最小的數 在做部分
* 如果 num = k , 則結束
* 如果 num < k , 則在右部分找到 k - num個最小的數
* 如果 num > k , 則繼續在左部分找這k個數
/**************************************
尋找最小的k個數
**************************************/
#include <cstdio>
#include <iostream>
using namespace std;
int partion(int a[], int low, int high)
{
int pivot = a[low];
while (low < high)
{
while (low < high && a[high] >= pivot)
--high;
a[low] = a[high];
while (low < high && a[low] < pivot)
++low;
a[high] = a[low];
}
a[low] = pivot; // 此時low 就是pivot 最終的位置
return low; // 返回最終確定的位置
}
/* 將k個最小的數放到 a[0]-a[k-1]中 */
void select_k_min(int a[], int k , int left , int right)
{
if(left >= right)
return;
if(k <= 0 || k > right - left)
return;
int pos = partion(a, left, right);
int num = pos - left + 1; // 左部分的個數,此時得到了最小的num個數
if(num == k) // 找到
return;
else if(num < k) { //需要在右部分,尋找最小的k-num個數
select_k_min(a, k - num , pos + 1, right);
}else{ // 需要繼續左部分操作,找到最小的k個數
select_k_min(a, k, left, pos -1 );
}
}
int main()
{
int a[10] = {3,10,9,8,5,2,6,7,4,1 };
int n = 10;
int k = 6;
int left = 1;
int right = n -1;
select_k_min(a, k , left , right);
for (int i = left; i < left + k - 1; i++)
printf("%d ", a[i]);
printf("%d\n", a[left + k - 1]);
return 0;
}
注: 以上方法是不穩定的,即選擇的最下的k個數不一定保持原來陣列中的相對順序。
堆排序
堆排序是不穩定的排序
每次調整的時間複雜度是O(h)
堆排序的事件複雜度在最好,平均,最壞的情況下都是O(nlogn)
利用堆排序可以求出最小的k個數
class KthNumbers {
public:
void BuildHeap(vector<int> &v, int n)
{
if (n <= 0)
return;
int index = n / 2;
for (int i = index; i >= 1; i--) // 從i到1依次調整
AdjustHeap(v, i, n);
}
//調整最大堆
void AdjustHeap(vector<int> &v, int i, int n)
{
while (i < n)
{
int iVal = v[i - 1];
int left = 2 * i - 1;
int right = 2 * i + 1 - 1;
// 取得左右孩子的值,因為v中的數不等,所以初始就為iVal
int leftVal = iVal;
if (left < n)
leftVal = v[left];
int rightVal = iVal;
if (right < n)
rightVal = v[right];
if (leftVal >= iVal && rightVal >= iVal)
return;
if (leftVal == iVal && rightVal == iVal)
{
return;
}
else if (leftVal == iVal)
{
// 右邊有效
if (rightVal < iVal){
swap(v[i-1], v[right]);
i = right + 1;
}
}
else if (rightVal == iVal)
{
// 左邊有效
if (leftVal < iVal){
swap(v[i-1], v[left]);
i = left + 1;
}
}
else{
if (leftVal < rightVal)
{
if (leftVal < iVal){ //調左邊
swap(v[i-1], v[left]);
i = left + 1;
}
}
else{
if (rightVal < iVal){ // 調右邊
swap(v[i-1], v[right]);
i = right + 1;
}
}
}
}
}
vector<int> findKthNumbers(vector<int> A, int n, int k) {
// write code here
if (k <= 0)
return vector<int>();
if (k >= n || n < 1) {
return A;
}
BuildHeap(A, n);
vector<int> rs;
for (int i = 0; i < k; i++)
{
int val = A[0];
A[0] = A[n - 1 - i];
A[n - 1 - i] = val;
AdjustHeap(A, 1, n - 1 - i);
printf("%d,", val);
rs.push_back(val);
}
return rs;
}
};
尋找最小的k個數(進一步要求順序與原陣列中元素順序一致)
題目描述
對於一個無序陣列,陣列中元素為互不相同的整數,請返回其中最小的k個數,順序與原陣列中元素順序一致。
給定一個整數陣列A及它的大小n,同時給定k,請返回其中最小的k個數。
測試樣例:
[1,2,4,3],4,2
返回:[1,2]
在上面的基礎上,利用pair< int,int> 儲存結構後,再按照次序排序,得到最終的結果
利用堆排序,ac程式碼,同理可用快排思路
class KthNumbers {
public:
void BuildHeap(vector<pair<int, int>> &vp, int n)
{
if (n <= 0)
return;
int index = n / 2;
for (int i = index; i >= 1; i--) // 從i到1依次調整
AdjustHeap(vp , i, n);
}
//調整最大堆
void AdjustHeap(vector<pair<int, int>> &vp, int i, int n)
{
while (i < n)
{
int iVal = vp[i - 1].first;
int left = 2 * i - 1;
int right = 2 * i + 1 - 1;
// 取得左右孩子的值,因為v中的數不等,所以初始就為iVal
int leftVal = iVal;
if (left < n)
leftVal = vp[left].first;
int rightVal = iVal;
if (right < n)
rightVal = vp[right].first;
if (leftVal >= iVal && rightVal >= iVal)
return;
if (leftVal == iVal && rightVal == iVal)
{
return;
}
else if (leftVal == iVal)
{
// 右邊有效
if (rightVal < iVal){
swap(vp[i - 1], vp[right]);
i = right + 1;
}
}
else if (rightVal == iVal)
{
// 左邊有效
if (leftVal < iVal){
swap(vp[i - 1], vp[left]);
i = left + 1;
}
}
else{
if (leftVal < rightVal)
{
if (leftVal < iVal){ //調左邊
swap(vp[i - 1], vp[left]);
i = left + 1;
}
}
else{
if (rightVal < iVal){ // 調右邊
swap(vp[i - 1], vp[right]);
i = right + 1;
}
}
}
}
}
vector<int> findKthNumbers(vector<int> A, int n, int k) {
// write code here
if (k <= 0)
return vector<int>();
if (k >= n || n < 1) {
return A;
}
vector<pair<int, int>> vp(n);// 構造<數字,下標>的pair陣列,方便結果輸出
for (int i = 0; i < n; i++)
{
pair<int, int> pairtmp(A[i], i);
vp[i] = pairtmp;
}
BuildHeap(vp, n); //建立堆
vector<pair<int, int>> rs; //依次取出最小的k個數
for (int i = 0; i < k; i++)
{
pair<int,int> val = vp[0];
vp[0] = vp[n - 1 - i];
vp[n - 1 - i] = val;
AdjustHeap(vp, 1, n - 1 - i);
//printf("%d,", val.first);
rs.push_back(val);
}
// 按照原始序號對結果排序
sort(rs.begin(), rs.end(),
[](pair<int, int> p1, pair<int, int> p2){ // c++11 , lambda
return p1.second < p2.second;
});
vector<int> ans;
for (pair<int, int> pt : rs) //c++11 for range
ans.push_back(pt.first);
return ans;
}
};