对于一个无序数组,数组中元素为互不相同的整数,请返回其中最小的k个数,顺序与原数组中元素顺序一致。
给定一个整数数组A及它的大小n,同时给定k,请返回其中最小的k个数。
测试样例:
[1,2,4,3],4,2
返回:[1,2]
先用快速排序的思想找到第K小的数,然后遍历vector,小于等于第k小的数依次加入vector。 class KthNumbers { public: int Partition(vector<int> &A,int start, int end){ int i=start,j=end,key=A[start]; while(i<j){ while(i<j&&A[j]>=key) j--; A[i]=A[j]; while(i<j&&A[i]<=key) i++; A[j]=A[i]; } A[i]=key; return i; } int findKthMin(vector<int> A,int start, int end, int k){ int i=Partition(A,start,end); if(i+1==k){ return A[i]; }else if(i+1<k){ return findKthMin(A,i+1,end,k); }else{ return findKthMin(A,start,i-1,k); } } vector<int> findKthNumbers(vector<int> A, int n, int k) { int index=findKthMin(A,0,n-1,k); vector<int> vec; for(int i=0;i<n&&vec.size()<=k;i++){ if(A[i]<=index) vec.push_back(A[i]); } return vec; } };
//堆排序 class KthNumbers { public: vector<int> findKthNumbers(vector<int> A, int n, int k) { // write code here vector<int> AA=A; int cc=k; make_heap(AA.begin(),AA.end(),greater<int>()); set<int> nset; while(cc--) { pop_heap(AA.begin(),AA.end(),greater<int>()); nset.insert(AA.back()); AA.pop_back(); } vector<int>ans; for(auto i:A) { if(nset.count(i))ans.push_back(i); if(ans.size() == k)break; } return ans; } };
对于本题应首先明确一点:需要保留k个较小的元素,那也就意味着可以删除除n-k个较大的元素。 具体实现代码如下: public class KthNumbers { public static int[] findKthNumbers(int[] A, int n, int k) { // write code here int count=n-k;//要删除的元素数量 int LENGTH=A.length;//实际数组中的元素个数 while(count>0){ //删除当前数组中最大的元素 int max_index=0;//记录最大元素的下标 int i; for(i=1;i<LENGTH;i++){//寻找最大元素并记录其在数组中的下标 if(A[max_index]<A[i]){ max_index=i; } } //删除当前最大元素即max_index for(int j=max_index;j<LENGTH-1;j++){ A[j]=A[j+1]; } LENGTH--;//注意删除后数组的个数要减一 count--; }//end while**************** int res[] = new int[LENGTH];//结果集数组 for(int i=0;i<LENGTH;i++){//当删除完元素后只需将保留下的k个元素赋值到结果数组中 res[i]=A[i]; } return res; } }
O(Nlogk)复杂度解法,O(N)复杂度的BFPRT算法,太复杂,不造次了; public int[] findKthNumbers(int[] arr, int n, int k) { if (k < 1 || k > arr.length) { return arr; } int[] kHeap = new int[k]; for (int i = 0; i < k; i++) { heapInsert(kHeap, arr[i], i); } for (int i = k; i < arr.length; i++) { if (arr[i] < kHeap[0]) { kHeap[0] = arr[i]; heapify(kHeap, 0, k); } } return kHeap; } /* * 找到无序数组中最小的K个数 heapInsert建堆 */ public static void heapInsert(int[] arr, int value, int index) { arr[index] = value; while (index != 0) { int parent = (index - 1) / 2; if (arr[parent] < arr[index]) { swap(arr, parent, index); index = parent; } else { break; } } } /* * 找到无序数组中最小的K个数 heapify 调整堆 */ public static void heapify(int[] arr, int index, int heapSize) { int left = index * 2 + 1; int right = index * 2 + 2; int largest = index; while (left < heapSize) { if (arr[left] > arr[index]) { largest = left; } if (right < heapSize && arr[right] > arr[largest]) { largest = right; } if (largest != index) { swap(arr, largest, index); } else { break; } index = largest; left = index * 2 + 1; right = index * 2 + 2; } } /** * 交换两个数 ij * * @param numbers * @param i * @param j */ private static void swap(int[] numbers, int i, int j) { int temp = numbers[i]; numbers[i] = numbers[j]; numbers[j] = temp; }
/* 分析: 本题中的难点在于返回的最小的k个数,顺序需要与原数组中元素顺序一致,比较艰难。 方法1:复制一个数组B,对数组B进行归并排序,然后比较数组A和B,当匹配上时,复制到返回数组C中。 或者直接找到Kth数字,作为基准找到所有小于等于的数值,这种方法的有一个明显的弊端: 当数组A中最小的Kth数字有多个重复值,且在原数组中,在重复数字后有更小的数字, 例如输入:9 1 5 4 4 10 2 | 7 | 3,按照该方法的输出为1 4 4 得到错误的答案。 时间复杂度:O(nlgn)+nk+k 空间复杂度:n+k 代码如下: class KthNumbers { public: vector<int> findKthNumbers(vector<int> A, int n, int k) { // write code here vector<int> B = A; sort(B.begin(),B.end()); vector<int> result; for(int i = 0;i < n;i++) { for(int j = 0;j < k;j++) { if(A[i] == B[j]) result.push_back(A[i]); } } return result; } }; 运行时间:4ms 占用内存:496k 方法2:由于需要按照原顺序输出最小的k个数,所以可以考虑删除最大的n-k个值,那么剩下的k个最小的值自然是按照原顺序的。 时间复杂度:nk 空间复杂度:忽略不计 代码如下: class KthNumbers { public: vector<int> findKthNumbers(vector<int> A, int n, int k) { // write code here //int count = n - k; while((int)A.size()>k) { int temp = A[0]; int max_index = 0; for(int i = 1;i < (int)A.size();i++) { if(temp < A[i]) { temp = A[i]; max_index = i; } } A.erase(A.begin()+max_index); } return A; } }; 运行时间:4ms 占用内存:504k
import java.util.*; public class KthNumbers { public int[] findKthNumbers(int[] A, int n, int k) { // write code here if (k < 1 || k > n) { return A; } int kthMin = getKthMinByBFPRT(A, k); // 使用BFPRT算法求得第k小的数,O(N) int[] kMins = new int[k]; // 下面遍历一遍数组,利用第k小的数找到最小的k个数,O(N) int index = 0; for (int i = 0; i < n && index < k; i++) { if (A[i] <= kthMin) { // 小于第k小的数,必然属于最小的k个数 kMins[index++] = A[i]; } } return kMins; } /** * 使用BFPRT算法求第k小的数 * * @param arr * @param k * @return */ public static int getKthMinByBFPRT(int[] arr, int k) { int[] arrCopy = copyArray(arr); // 在得到第k小的数之后还要遍历一遍原数组,所以并不直接操作原数组 return select(arrCopy, 0, arrCopy.length - 1, k - 1); // 第k小的数,即排好序后下标为k-1的数 } /** * 拷贝数组 * * @param arr * @return */ public int[] copyArray(int[] arr) { int[] arrCopy = new int[arr.length]; for (int i = 0; i < arrCopy.length; i++) { arrCopy[i] = arr[i]; } return arrCopy; } /** * 在数组arr的下标范围[begin, end]内,找到排序后位于整个arr数组下标为index的数 * * @param arr * @param begin * @param end * @param index * @return */ public int select(int[] arr, int begin, int end, int index) { if (begin == end) { return arr[begin]; } int pivot = medianOfMedians(arr, begin, end); // 核心操作:中位数的中位数作为基准 int[] pivotRange = partition(arr, begin, end, pivot); // 拿到分区后中区的范围 if (index >= pivotRange[0] && index <= pivotRange[1]) { // 命中 return arr[index]; } else if (index < pivotRange[0]) { return select(arr, begin, pivotRange[0] - 1, index); } else { return select(arr, pivotRange[1] + 1, end, index); } } /** * 选基准 * * @param arr * @param begin * @param end * @return */ public int medianOfMedians(int[] arr, int begin, int end) { int num = end - begin + 1; int offset = num % 5 == 0 ? 0 : 1; // 5个成一组,不满5个的自己成一组 int[] mArr = new int[num / 5 + offset]; // 每组的中位数取出构成中位数数组mArr for (int i = 0; i < mArr.length; i++) { int beginI = begin + i * 5; int endI = beginI + 4; mArr[i] = getMedian(arr, beginI, Math.min(endI, end)); } // 求中位数数组mArr的中位数,作为基准返回 return select(mArr, 0, mArr.length - 1, mArr.length / 2); } /** * 在数组arr的下标范围[begin, end]内,找中位数,如果元素个数为偶数则找下中位数 * * @param arr * @param begin * @param end * @return */ public int getMedian(int[] arr, int begin, int end) { insertionSort(arr, begin, end); int sum = begin + end; int mid = (sum / 2) + (sum % 2); return arr[mid]; } /** * 这里仅用于对一组5个数进行插入排序,时间复杂度O(1) * * @param arr * @param begin * @param end */ public void insertionSort(int[] arr, int begin, int end) { for (int i = begin + 1; i <= end; i++) { int get = arr[i]; int j = i - 1; while (j >= begin && arr[j] > get) { arr[j + 1] = arr[j]; j--; } arr[j + 1] = get; } } /** * 优化后的快排partition操作 * * @param arr * @param begin * @param end * @param pivot * @return 返回划分后等于基准的元素下标范围 */ public int[] partition(int[] arr, int begin, int end, int pivot) { int small = begin - 1; // 小区最后一个元素下标 int big = end + 1; // 大区第一个元素下标 int cur = begin; while (cur < big) { if (arr[cur] < pivot) { swap(arr, ++small, cur++); } else if (arr[cur] > pivot) { swap(arr, --big, cur); } else { cur++; } } int[] range = new int[2]; range[0] = small + 1; // 中区第一个元素下标 range[1] = big - 1; // 中区最后一个元素下标 return range; } public void swap(int[] arr, int i, int j) { int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } }
class KthNumbers { public: vector<int> findKthNumbers(vector<int> A, int n, int k) { // write code here vector<int> temp(A); sort(temp.begin(), temp.end()); vector<int> result; for(int i = 0; i < n; i ++){ if(A[i] < temp[k]) result.push_back(A[i]); } return result; } };
class KthNumbers { int partion(vector<int> &A, int left, int right){ int pivot = A[right]; int index = left - 1; for(int i = left; i < right; ++ i){ if(A[i] < pivot){ ++index; swap(A[index], A[i]); } } ++ index; swap(A[index], A[right]); return index; } public: vector<int> findKthNumbers(vector<int> A, int n, int k) { // write code here vector<int> result; if(n == 0) return result; vector<int> num(A); int left = 0; int right = n - 1; int index = partion(num, left, right); while(index != k - 1){ if(index > k - 1) { right = index - 1; index = partion(num, left, right); } if(index < k - 1) { left = index + 1; index = partion(num, left, right); } } for(int i = 0; i < n; ++ i) if(A[i] < num[k]) result.push_back(A[i]); return result; } };
class KthNumbers { public: int quickFind(vector<int> &arr, int start, int end, int target) { if(start == end) { return arr[start]; } int current = start; int left = start + 1; int right = end; while(left <= right) { while(left <= right && arr[right] >= arr[current]) { right--; } if(left <= right) { int tmp = arr[right]; arr[right] = arr[current]; arr[current] = tmp; current = right; } while(left <= right && arr[left] <= arr[current]) { left++; } if(left <= right) { int tmp = arr[left]; arr[left] = arr[current]; arr[current] = tmp; current = left; } } if(current < target) { return quickFind(arr, current + 1, end, target); } if(current > target) { return quickFind(arr, start, current - 1, target); } return arr[current]; } vector<int> findKthNumbers(vector<int> A, int n, int k) { // write code here vector<int> res; if(k > n || n < 1) { return res; } res.assign(A.begin(), A.end()); int val = quickFind(res, 0, n - 1, k - 1); res.clear(); for(int i = 0; i < A.size(); i++) { if(A[i] <= val) { res.push_back(A[i]); } } return res; } };
基于快排思想划分区域,选取第k小的数字,然后遍历原数组取比它小的所有数字
import java.util.*; public class KthNumbers { public int[] findKthNumbers(int[] A, int n, int k) { // write code here int[] res = new int[k]; int[] tmp = A.clone(); int left =0,right=A.length-1,target=0; while(true){ int idx = partion(A,left,right); if(idx+1 == k){ target = A[idx]; break; }else if(idx+1<k){ left = idx+1; }else{ right = idx-1; } } for(int i=0,j=0;i<tmp.length && j<k;i++){ if(tmp[i]<=target){ res[j++]=tmp[i]; } } return res; } public int partion(int[] nums,int i,int j){ int pviot = nums[i]; int left =i,right=j; while(left < right){ while(left=pviot)right--; //if(left<right)nums[left]=nums[right]; while(left<right && nums[left]<=pviot)left++; if(left<right){ swap(nums,right,left); } //if(left<right)nums[right]=nums[left]; } swap(nums,left,i); //nums[left]=pviot; return left; } private void swap(int[] nums, int left, int right) { int tmp = nums[left]; nums[left]=nums[right]; nums[right]=tmp; } }
import java.util.*; public class KthNumbers { public int[] findKthNumbers(int[] A, int n, int k) { // write code here int[] arr = new int[k]; int m = GetK(A,k); for(int i=0,j=0;i<n&&j<k;i++){ if(A[i]<=m){ arr[j] = A[i]; j++; } } return arr; } public static int GetK(int[] A,int k){ int[] tmp = new int[A.length]; for(int i=0;i<A.length;i++){ tmp[i] = A[i]; } Arrays.sort(tmp); return tmp[k-1]; } }
public class KthNumbers { public static int[] findKthNumbers(int[] A, int n, int k) { int count = n - k; int length = A.length; while (count > 0) { int maxNumberIndex = 0; int i; for (i = 1; i < length; i++) { if (A[maxNumberIndex] < A[i]) { maxNumberIndex = i; } } for (int j = maxNumberIndex; j < length - 1; j++) { A[j] = A[j + 1]; } length--; count--; } int[] result = new int[length]; for (int i = 0; i < length; i++) { result[i] = A[i]; } return result; } }
考虑到n可能比较大,利用priority_queue维护k个最小的数值,只需要占用O(k)的空间。(网易游戏电面就说了这道题目,限制是内存太小,不能同时放置n个数)。
class KthNumbers {
public:
vector<int> findKthNumbers(vector<int> A, int n, int k) {
// write code here
priority_queue<pair<int, int>> pq;
for (int i = 0; i < n; i++){
pq.push({ A[i], i });
if (i >= k) pq.pop();
}
vector < pair<int, int>> tmp;
while (!pq.empty()){
tmp.push_back(pq.top());
pq.pop();
}
sort(tmp.begin(), tmp.end(), [](const pair<int, int>& a, const pair<int, int>& b){
return a.second < b.second;
});
vector<int> res;
for (auto& t : tmp){
res.push_back(t.first);
}
return res;
}
};