排序应用——快速排序,堆排序

题外话
	很多人都认为(包括过去的笔者),排序算法各种编程语言都有实现,我们就没必要再去造轮子了,所以再更多的时候就会忽略它们的应用场景和思想。这里,笔者的观点是,改造的轮子还是要造的。
	有一个这样的问题(后续就称呼为TopK问题):给定一个数组,返回前K个最小值,返回的数组顺序没有要求,当然,K小于数组长度。这其实是LeetCode上一道标着“简单”的题,然后很多网友直接调一个排序接口就搞定了(包括笔者),效率还算高。但看到官方题解用了三种方法,分别是直接排序的,快排思想和堆的思想,突然发现我可能是误解官方题目的意思了,哈哈。
	其实在这里,用快排和堆排序的思想就很好,一般来说我们直接排序的算法复杂度为O(nlogn),而另两种的复杂度为O(nlogk),先来说说快排吧。
快速排序
我们都知道快排的思想其实是分治,把一个待解决问题,拆成若干个子问题。快排通过不断选取参考值来划分左右,直到不能划分为止。附上C++的实现:
	//快速排序
	void quickSort(int arr[],int startIndex,int endIndex)
	{
		if(startIndex == endIndex)
		{
			return;
		}
		int pivotIndex = startIndex;

		std::cout<<"startIndex->"<<startIndex<<" endIndex->"<<endIndex<<std::endl;

		for(int i = startIndex + 1;i < endIndex; ++i)
		{
			if(arr[i] < arr[startIndex])
			{
				swap(arr[++pivotIndex],arr[i]);
			}
		}

		std::cout<<"pivotIndex->"<<pivotIndex<<std::endl;

		if(pivotIndex == startIndex && pivotIndex < endIndex)
		{
			quickSort(arr,pivotIndex + 1,endIndex);
		}
		else
		{
			swap(arr[pivotIndex],arr[startIndex]);
			quickSort(arr,startIndex,pivotIndex);
			quickSort(arr,pivotIndex,endIndex);
		}
		
		

	}
在这里,针对TopK问题,因为并不需要分割点左右两边都是有序的,所以我们其实只要找到分割点为K就行了。代码如下:

	//快排思想解决topk问题
	void topKByQuickSort(int arr[],int startIndex,int endIndex,int k)
	{

		if(startIndex == endIndex)
		{
			return;
		}
		int pivotIndex = startIndex;

		std::cout<<"startIndex->"<<startIndex<<" endIndex->"<<endIndex<<std::endl;

		for(int i = startIndex + 1;i < endIndex; ++i)
		{
			if(arr[i] < arr[startIndex])
			{
				swap(arr[++pivotIndex],arr[i]);
			}
		}
		swap(arr[pivotIndex],arr[startIndex]);

		
		if(pivotIndex == startIndex && pivotIndex < endIndex)
		{
			++pivotIndex;
		}

		std::cout<<"pivotIndex->"<<pivotIndex<<std::endl;

		if(pivotIndex < k)
		{
			topKByQuickSort(arr,pivotIndex,endIndex);
		}
		else if(pivotIndex > k)
		{
			topKByQuickSort(arr,startIndex,pivotIndex);
		}
	}

其实还可以优化的,当K超过数组长度的二分之一时,我们完全可以取反,可以转化为找前Len - k 的最大值问题,这样就能保证TopK问题的复杂始终不超过O(log n/2)。

堆排序

堆排序主要是利用堆这个数据结构的特性——堆其实就是一个数组实现的完全二叉树,堆中所有父节点的值均大于子节点。由于这个特性,我们可以利用堆来做很多事,堆排序就是一个经典例子。因为堆的特性,堆顶节点的值要么是最小的,要么是最大的(这跟构建的堆类型有关,有最小堆和最大堆),所以,我们就可以不断弹出堆顶元素并更新堆,直至堆的长度变为0,这样就达到了排序的目的。然后这里的TopK问题跟堆排序的思想基本一致。附上C++实现代码:
	#include <iostream>
	#include<vector>
	int heapSize = 0;


	int getLeftIndex(int index)
	{
		return (index << 1) + 1;

	}


	int getRightIndex(int index)
	{
		return (index << 1) + 2;
		
	}

	void swapValue(int& a,int& b)
	{

		int temp = a;
		a = b;
		b =  temp;

	}

	void adjugeHeap(std::vector<int> &vc,int index)
	{
		int leftIndex = getLeftIndex(index);
		int lessIndex = index;
		if(leftIndex < heapSize && vc[leftIndex] < vc[lessIndex])
		{
			lessIndex = leftIndex;
		}

		int rightIndex = getRightIndex(index);
		if(rightIndex < heapSize && vc[rightIndex] < vc[lessIndex])
		{
			lessIndex = rightIndex;
		}

		if(lessIndex == index)
		{
			return;
		}
		else
		{
			swapValue(vc[lessIndex],vc[index]);
			adjugeHeap(vc,lessIndex);

		}

	}


	void buildHeap(std::vector<int>& vc)
	{
		heapSize = vc.size();
		for(int i = (heapSize - 1) >> 1;i >= 0; --i)
		{
			adjugeHeap(vc,i);
		}

	}


	int popHeap(std::vector<int>& vc)
	{
		if(heapSize == 0)
		{
			return -1;
		}

		int temp = vc[0];
		vc[0] = vc[heapSize-- - 1];
		adjugeHeap(vc,0);

		return temp;

	}


	//删除某个节点
	unsigned char deleteHeap(std::vector<int>& vc,int index)
	{
		unsigned char resCode = 0;
		if(heapSize == 0 || index >= heapSize)
		{
			resCode = 1;

		}
		
		vc[index] = vc[heapSize-- - 1];
		adjugeHeap(vc,index);
		return resCode;
	}


	void insertHeap(std::vector<int>& vc,int value)
	{

		//vc.reserve(vc.size() + 1);
		vc.push_back(value);
		swapValue(vc[heapSize++],vc[vc.size() - 1]);
		for(int i = (heapSize -1) >> 1; i >= 0; --i)
		{
			adjugeHeap(vc,i);

		}

	}

	void heapSort(std::vector<int>& vc)//堆排序
	{
		buildHeap(vc);
		int temHeapSize = heapSize;
		while(heapSize > 0)
		{
			swapValue(vc[heapSize-- -1],vc[0]);
			adjugeHeap(vc,0);
		}
		heapSize = temHeapSize;
	}

	void topKbyHeap(std::vector<int>& vc,int k)
	{
		buildHeap(vc);
		if(k > heapSize)
		{
			return;
		}
		for(int i = k;i < heapSize; ++ i)
		{
			if(vc[k] < vc[0])
			{
				swapValue(vc[k],vc[0]);
			}
		}

	}

	void logVc(std::vector<int> &vc)
	{
		for(int i = 0; i < vc.size(); ++i)
		{
			std::cout<<vc[i]<<"-";
		}
		std::cout<<std::endl;
	}

这里,跟快排一样,堆排序思想解决TopK问题算法复杂度也是O(nlogk),k也可以始终小于n/2。

总结

算法是核心,编程是基础。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

王富贵9527

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

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

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

打赏作者

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

抵扣说明:

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

余额充值