第K小算法
方法一:基于快速排序的选择方法
方法一:基于快速排序的选择方法
思路和算法
「划分」 过程是:从子数组 a[l⋯r] 中选择任意一个元素 x 作为主元,调整子数组的元素使得左边的元素都小于等于它,右边的元素都大于等于它, x 的最终位置就是 q,且每次经过「划分」操作后,我们一定可以确定一个元素的最终位置,即 x 的最终位置为 q,并且保证 a[l⋯q−1] 中的每个元素小于等于 a[q],且 a[q] 小于等于 a[q+1⋯r] 中的每个元素。所以只要某次划分的 q为倒数第 k 个下标的时候,我们就得到了结果,我们并不关注a[l⋯q−1] 和 a[q+1⋯r] 是否是有序。
inline int partition(vector<int>& arr, int l, int r)
{//partition,分组,返回arr[i]的准确排序位置
int x = arr[r], i = l - 1;
for (int j = l; j < r; ++j) {
if (arr[j] <= x) {
swap(arr[++i], arr[j]);
}
}
swap(arr[i + 1], arr[r]);
return i + 1;
}
inline int randomPartition(vector<int>& arr, int l, int r)
{//随机取一个数,返回其准确位置
int i = rand() % (r - l + 1) + l;//随机取一个arr[i]数
swap(arr[i], arr[r]);//作为游标
return partition(arr, l, r);//返回arr[i]的准确位置,不关注arr[l...i-1]和arr[i+1...r]是否有序
}
int quickSelect(vector<int>& arr, int l, int r, int index)
{//返回第k小的值
int q = randomPartition(arr, l, r);
if (q == index)
{
return arr[q];
} else
{
return q < index ? quickSelect(arr, q + 1, r, index) : quickSelect(arr, l, q - 1, index);
}
}
int quick_findKthSmallest(vector<int>& nums, int k)
{//调用选择函数,容器元素从0位置开始存储,最小是0
srand(time(0));
return quickSelect(nums, 0, nums.size() - 1, k);
}
方法二:基于堆排序的选择方法
思路和算法
使用堆排序来解决这个问题——建立一个大根堆,做 k−1 次删除操作后堆顶元素就是我们要找的答案。
void maxHeapify(vector<int>& a, int i, int heapSize)
{//调整堆
int l = i * 2 + 1, r = i * 2 + 2, largest = i;
if (l < heapSize && a[l] > a[largest])
{
largest = l;
}
if (r < heapSize && a[r] > a[largest])
{
largest = r;
}
if (largest != i)
{
swap(a[i], a[largest]);
maxHeapify(a, largest, heapSize);
}
}
void buildMaxHeap(vector<int>& a, int heapSize)
{//建堆
for (int i = heapSize / 2; i >= 0; --i)
{
maxHeapify(a, i, heapSize);
}
}
int heap_findKthSmallest(vector<int>& nums, int k)
{//获得堆顶最小值,最小是1
int heapSize = nums.size();
buildMaxHeap(nums, heapSize);
for (int i = nums.size() - 1; i >=k ; --i)
{
swap(nums[0], nums[i]);
--heapSize;
maxHeapify(nums, 0, heapSize);
}
return nums[0];
}
完整代码
/*
* @Description:
* @Author: dive668
* @Date: 2021-06-03 10:38:58
* @LastEditTime: 2021-06-03 21:30:06
*/
#include <iostream>
#include <vector>
#include <sstream>
#include <string>
#include <fstream>
#include <ctime>
#include <algorithm>
using namespace std;
inline int partition(vector<int>& arr, int l, int r)
{//partition,分组,返回arr[i]的准确排序位置
int x = arr[r], i = l - 1;
for (int j = l; j < r; ++j) {
if (arr[j] <= x) {
swap(arr[++i], arr[j]);
}
}
swap(arr[i + 1], arr[r]);
return i + 1;
}
inline int randomPartition(vector<int>& arr, int l, int r)
{//随机取一个数,返回其准确位置
int i = rand() % (r - l + 1) + l;//随机取一个arr[i]数
swap(arr[i], arr[r]);//作为游标
return partition(arr, l, r);//返回arr[i]的准确位置,不关注arr[l...i-1]和arr[i+1...r]是否有序
}
int quickSelect(vector<int>& arr, int l, int r, int index)
{//返回第k小的值
int q = randomPartition(arr, l, r);
if (q == index)
{
return arr[q];
} else
{
return q < index ? quickSelect(arr, q + 1, r, index) : quickSelect(arr, l, q - 1, index);
}
}
int quick_findKthSmallest(vector<int>& nums, int k)
{//调用选择函数,容器元素从0位置开始存储,最小是0
srand(time(0));
return quickSelect(nums, 0, nums.size() - 1, k);
}
int str2int(string s)
{//将字符串转换为int,用于从文件中读取内容存储到容器中
int temp;
stringstream ss;
ss<<s;
ss>>temp;
return temp;
}
vector<int> file_getnum(string filename)
{//读取数据集文件,返回数组容器
vector<int> vec;
string s1;
int temp;
ifstream fp(filename.c_str(),ios::in);
if(fp)
{
while(!fp.eof())
{
getline(fp,s1);
vec.push_back(str2int(s1));
}
}
else cout<<"file open error!"<<endl;
fp.close();
return vec;
}
void maxHeapify(vector<int>& a, int i, int heapSize)
{//调整堆
int l = i * 2 + 1, r = i * 2 + 2, largest = i;
if (l < heapSize && a[l] > a[largest])
{
largest = l;
}
if (r < heapSize && a[r] > a[largest])
{
largest = r;
}
if (largest != i)
{
swap(a[i], a[largest]);
maxHeapify(a, largest, heapSize);
}
}
void buildMaxHeap(vector<int>& a, int heapSize)
{//建堆
for (int i = heapSize / 2; i >= 0; --i)
{
maxHeapify(a, i, heapSize);
}
}
int heap_findKthSmallest(vector<int>& nums, int k)
{//获得堆顶最小值,最小是1
int heapSize = nums.size();
buildMaxHeap(nums, heapSize);
for (int i = nums.size() - 1; i >=k ; --i)
{
swap(nums[0], nums[i]);
--heapSize;
maxHeapify(nums, 0, heapSize);
}
return nums[0];
}
int main()
{
string filename;
string filename_new;
cout << "输入要读取的文件名字(example:test0):";
cin >> filename;
int k=0;
cout<<"给出要找的最小的k:";
cin>>k;
filename_new=filename+".txt";
clock_t t;
vector<int> nums;
vector<int>::iterator p;
nums=file_getnum(filename_new);
cout<<"开始输出"<<endl;
cout<<heap_findKthSmallest(nums,k)<<endl;
cout<<"输出结束"<<endl;
return 0;
}
测试数据集,对比两种 k 位数算法
(1)随机生成大小不同数据集合,如大小分别为(1000,2000,5000,10000,20000, 50000, 100000)的数据集合;
(2)对每个算法,针对不同大小的数据集合,运行算法 1000 次并记录算法运行的平均时间;
(3)将得到的运行时间绘制成曲线,分析并比较两种不同的算法。
随机数数据集生成
/*
* @Description:
* @Author: dive668
* @Date: 2021-06-03 22:09:28
* @LastEditTime: 2021-06-03 22:19:31
*/
#include <iostream>
#include <sstream>
#include <cstring>
#include <ctime>
#include <algorithm>
using namespace std;
const int MAX = 1e3;
int* generate_num(int percent)//参数是重复率 0~10
{
FILE *fp;
fp=freopen("test_1000.txt","w",stdout);//设置 cout printf 这些输出流都输出到 test.txt里面去
int *x = new int[MAX+1]();
int *num = new int[MAX + 1]();
int i=1;
int temp = 0;
if(percent==0)
{
for (i = 1; i <= MAX;++i)
{
x[i] = i;
}
std::random_shuffle(x+1,x+MAX+1);//打乱
cout << x[1];
for (i = 2; i <= MAX;++i)
{
cout << endl<< x[i];
}
fclose(fp);
return x;
}
else
{
srand(time(0));
for (i = 1; i <= MAX - MAX * 10 * percent * 0.01;++i)
{
x[i]=i;
num[i]++;
}
while(true)
{
for (i = 1; i <= MAX;++i)
{
temp=i;
if(num[temp]==0) break;
}
break;
}
for (i = MAX - MAX * 10 * percent * 0.01 + 1; i <= MAX;++i)
{
x[i] = temp;
}
std::random_shuffle(x+1,x+MAX+1);//打乱
for(i=1;i<=MAX;++i)
{
if(i==1)
cout<<x[i];
else
cout<<endl<<x[i];
}
fclose(fp);
return x;
}
}
int main()
{
int *x = new int[MAX + 1]();
int percent;
cout << "输入重复比率(0~10):";
cin >> percent;
x = generate_num(percent);
//恢复标准输入输出
freopen("CON","r",stdin);
freopen("CON","w",stdout);
cout << "生成成功" << endl;
return 0;
}
正确解决 VS Code / GCC / Clang 编译时中文乱码问题
随机数数据集选择排序1000次运行时间比较
/*
* @Description:
* @Author: dive668
* @Date: 2021-06-03 10:38:58
* @LastEditTime: 2021-06-03 22:47:37
*/
#include <iostream>
#include <vector>
#include <sstream>
#include <string>
#include <fstream>
#include <ctime>
#include <algorithm>
using namespace std;
inline int partition(vector<int>& arr, int l, int r)
{//partition,分组,返回arr[i]的准确排序位置
int x = arr[r], i = l - 1;
for (int j = l; j < r; ++j) {
if (arr[j] <= x) {
swap(arr[++i], arr[j]);
}
}
swap(arr[i + 1], arr[r]);
return i + 1;
}
inline int randomPartition(vector<int>& arr, int l, int r)
{//随机取一个数,返回其准确位置
int i = rand() % (r - l + 1) + l;//随机取一个arr[i]数
swap(arr[i], arr[r]);//作为游标
return partition(arr, l, r);//返回arr[i]的准确位置,不关注arr[l...i-1]和arr[i+1...r]是否有序
}
int quickSelect(vector<int>& arr, int l, int r, int index)
{//返回第k小的值
int q = randomPartition(arr, l, r);
if (q == index)
{
return arr[q];
} else
{
return q < index ? quickSelect(arr, q + 1, r, index) : quickSelect(arr, l, q - 1, index);
}
}
void quick_findKthSmallest(vector<int>& nums, int k)
{//调用选择函数,容器元素从0位置开始存储,最小是0
srand(time(0));
quickSelect(nums, 0, nums.size() - 1, k);
}
int str2int(string s)
{//将字符串转换为int,用于从文件中读取内容存储到容器中
int temp;
stringstream ss;
ss<<s;
ss>>temp;
return temp;
}
vector<int> file_getnum(string filename)
{//读取数据集文件,返回数组容器
vector<int> vec;
string s1;
int temp;
ifstream fp(filename.c_str(),ios::in);
if(fp)
{
while(!fp.eof())
{
getline(fp,s1);
vec.push_back(str2int(s1));
}
}
else cout<<"file open error!"<<endl;
fp.close();
return vec;
}
void maxHeapify(vector<int>& a, int i, int heapSize)
{//调整堆
int l = i * 2 + 1, r = i * 2 + 2, largest = i;
if (l < heapSize && a[l] > a[largest])
{
largest = l;
}
if (r < heapSize && a[r] > a[largest])
{
largest = r;
}
if (largest != i)
{
swap(a[i], a[largest]);
maxHeapify(a, largest, heapSize);
}
}
void buildMaxHeap(vector<int>& a, int heapSize)
{//建堆
for (int i = heapSize / 2; i >= 0; --i)
{
maxHeapify(a, i, heapSize);
}
}
void heap_findKthSmallest(vector<int>& nums, int k)
{//获得堆顶最小值,最小是1
int heapSize = nums.size();
buildMaxHeap(nums, heapSize);
for (int i = nums.size() - 1; i >=k ; --i)
{
swap(nums[0], nums[i]);
--heapSize;
maxHeapify(nums, 0, heapSize);
}
}
int main()
{
string filename;
string filename_new;
cout << "输入要读取的文件名字(example:test0):";
cin >> filename;
int k=0;
cout<<"给出要找的最小的k:";
cin>>k;
clock_t time;
int temp;
filename_new=filename+".txt";
clock_t t;
vector<int> nums;
vector<int>::iterator p;
nums=file_getnum(filename_new);
cout<<"开始运行快速选择"<<endl;
t=clock();
for(int i=0;i<1000;++i)
quick_findKthSmallest(nums,k);
cout<<"运行时间"<<clock()-t<<endl;
cout<<"------------------"<<endl;
cout<<"开始运行堆选择"<<endl;
t=clock();
for(int i=0;i<1000;++i)
heap_findKthSmallest(nums,k);
cout<<"运行时间"<<clock()-t<<endl;
return 0;
}