快速排序方法及其应用(最小的k个数)
快速排序是一种比较好的排序方法,时间复杂度为O(nlgn)。快速排序方法中最主要的函数是Partition函数,Partition函数的作用是函数运行完之后,返回值位置及前面的数值都比后面的小,即如下代码中small位置及前面的 值都比small后面的值小。
int Partition(int* number, int length, int start, int end)
{
if (number == nullptr || length <= 0 || start < 0 || end >= length)
throw new std::exception;
int index = getRandomNum(start, end);
swap(&number[index],&number[end]);
int small = start - 1;
for (index = start;index < end; ++index)
{
if (number[index] < number[end])
{
++small;
if (small != index)
swap(&number[small], &number[index]);
}
}
++small;
swap(&number[small],&number[end]);
return small;
}
完整的快速排序代码:
#include <iostream>
#include <algorithm>
#include <exception>
#include <cstdlib>
#include <ctime>//time
template<class T>
void swap(T* a, T* b)//注意用指针构造交换函数时,交换的是指针指向的值
{
T temp = *a;
*a = *b;
*b = temp;
}
int RandomInRange(int st, int en)
{
srand(unsigned(time(0)));//把当前时间作为随机数种子
return st + rand() % (en - st + 1);
}
int Partition(int data[], int length, int start, int end)
{
if (data == nullptr || length <= 0 || start < 0 || end >= length)
{
throw std::exception;
}
int index = RandomInRange(start, end);
swap(&data[index], &data[end]);
int small = start - 1;
for (index = start; index < end; ++index)
{
if (data[index] < data[end])//当条件不成立时index增加,small不变
{
++small;
if (small != index)
swap(&data[index], &data[small]);
}
}
++small;
swap(&data[small], &data[end]);
return small;//返回的结果是左边比data[end]小,右边比data[end]大的数
}
void QuickSort(int data[], int length, int start, int end)
{
if (start == end)
return;
//**********************************这部分是判断数组是否已经按升序排好序,排好的话就直接终止,这样就防止出现最糟糕的情况
bool sorted = true; //《剑指offer》p81
for (int i = start; i < end; ++i)
{
if (data[i] > data[i + 1])//如果数组已经是排序的话,则排序就终止了
{
sorted = false;
}
}
if (sorted)
return;
//****************************************************************************************
int index = Partition(data, length, start, end);
if (index > start)
QuickSort(data, length, start, index - 1);
if (index < end)
QuickSort(data, length, index + 1, end);
}
void Show(const int& v)
{
std::cout << v << " ";
}
int main()
{
int arr[]{ 6, 5, 8, 4, 3, 1 };
for (auto x : arr)
std::cout << x << " ";
//std::for_each(arr, arr + sizeof(arr) / sizeof(int), Show);
std::cout << "\n";
QuickSort(arr, sizeof(arr) / sizeof(int), 0, sizeof(arr) / sizeof(int) - 1);
std::for_each(arr, arr + sizeof(arr) / sizeof(int), Show);
return 0;
}
下面补充一个相关的题目《剑指offer:面试题40》最小的k个数:
题目:输入n个整数,找出其中最小的k个数。例如,输入4 、5 、1 、6 、2 、7 、3 、8这8个数,则最小的4个数字是1 、2 、3 、4.
本题主要用到了快速排序中的Partition方法,另外如果涉及到输入数组的话,可以使用指针创建动态数组的方法,另外使用完之后,记得delete
完整代码如下:
#include <iostream>
#include <ctime>
#include <exception>
void swap(int* a , int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int getRandomNum(int st, int en)
{
srand(unsigned(time(0)));
return st + rand() % (en - st + 1);
}
int Partition(int* number, int length, int start, int end)
{
if (number == nullptr || length <= 0 || start < 0 || end >= length)
throw new std::exception;
int index = getRandomNum(start, end);
swap(&number[index],&number[end]);
int small = start - 1;
for (index = start;index < end; ++index)
{
if (number[index] < number[end])
{
++small;
if (small != index)
swap(&number[small], &number[index]);
}
}
++small;
swap(&number[small],&number[end]);
return small;
}
void GetLeastNumbers(int* input, int n, int k)
{
if (input == nullptr || k > n || k <= 0 || n<= 0)
return;
int start = 0;
int end = n - 1;
int index = Partition(input, n, start, end);//Partition函数的作用是返回值之后,返回值位置及前面的数值都比后面的小
while(index != k - 1)
{
if (index > k - 1)
end = index - 1;
else
start = index + 1;
index = Partition(input, n, start, end);
}
for(int i = 0; i < n; ++i)
std::cout << input[i] << '\t';
std::cout << std::endl;
}
int main()
{
int a[]{4,5,1,6,2,7,3,8};
// int n;
// std::cin >> n;
// int* a = new int[n];//数组的动态联编
// for (int i = 0; i < n;++i)
// {
// std::cin >> a[i];
// }
GetLeastNumbers(a,sizeof(a)/sizeof(int), 4);
// delete [] a;
return 0;
}
另附一个适合海量数据的方法(使用标准库中的mutilset)时间复杂度为O(nlgk)
#include <iostream>
#include <iterator>
#include <vector>
#include <set>
using namespace std;
typedef multiset<int, greater<int>> intSet;
typedef multiset<int, greater<int>>::iterator setIterator;
void GetLeastNumbers(const vector<int>& data, intSet& leastNumbers, int k)
{
leastNumbers.clear();
if (k < 1 || data.size() < k)
return;
vector<int>::const_iterator iter = data.begin();
for(;iter < data.end();++iter)
{
if (leastNumbers.size() < k)
leastNumbers.insert(*iter);
else
{
setIterator MaxNum = leastNumbers.begin();
//multiset中使用了geater<int>使得最大值在开始位置,
//插入数据后就自动按从大到小的顺序排序
if (*MaxNum > *iter)
{
leastNumbers.erase(MaxNum);//注意这里清除的是定位器指向的值
leastNumbers.insert(*iter);
}
}
}
}
int main()
{
// int a[]{4,5,1,6,2,7,3,8};
int n;
std::cin >> n;
int temp;//数组的动态联编
std::vector<int> vec;
for (int i = 0; i < n;++i)
{
std::cin >> temp;
vec.push_back(temp);
}
intSet Numbers;
GetLeastNumbers(vec,Numbers, 4);
// delete [] a;
for (auto x :Numbers)
std::cout << x << '\t';
std::cout << std::endl;
return 0;
}