快速排序方法及其应用(最小的k个数)

快速排序方法及其应用(最小的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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笑着的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值