算法(二):快排、一次划分问题、最近点对、TOP K

一、快排的一次划分

#include <iostream>
#include <stack>
#include <queue>
using namespace std;

template<class Type>
int Partition(Type *arr,int left,int right)  
//快排的一次划分,返回下标i,以i将数据分为两部分
//一部分大于i,一部分小于i
{
	int i=left;
	int j=right;
	Type tmp=arr[left];
	while(i<j)
	{
		while(i<j && arr[j] > tmp)
		{
			--j;
			if(i<j) arr[i]=arr[j];
		}
		while(i<j && arr[i] < tmp)
		{
			++i;
			if(i<j) arr[j]=arr[i];
		}
	}
	arr[i]=tmp;
	return i;
}

二、快排的递归调用

template <typename Type>      // 快排的递归调用
void PassQuick(Type *arr,int left,int right)
{
	if(left <right)
	{
		Type mid=Partition(arr,left,right);
		PassQuick(arr,left,mid-1);
	    PassQuick(Type *arr,mid+1,right);
	}
}

三、快排的非递归调用

快排的非递归调用,可以利用栈和队列两种形式

template<class Type>
void QuickSort(Type *ar,int n)
{
	SNicePassQuick(ar,0,n-1); //用栈,进行快排的非递归调用
	QNicePassQuick(ar,0,n-1); //用队列,进行快排的非递归调用
}

  1. 利用栈
template <class Type>            //用栈,进行快排的非递归调用
void SPassQuick(Type *arr,int left,int right)
{
	if(left >= right) return ;
	stack<int> s;
	s.push(left);
	s.push(right);
	while(! s.empty())
	{
		right=s.top();s.pop();
		left =s.top();s.pop();
		Type mid=Partition(arr,left,right);
		if(left < mid-1)
		{
			s.push(left);
			s.push(mid -1);
		}
		if(mid+1 < right)
		{
			s.push(mid +1);
			s.push(right);
		}
	}
}
  1. 利用队列
template<class Type>     //用队列,进行快排的非递归调用
void QPassQuick(Type *arr,int left,int right)
{
	if(left >=right) return ;
	queue<int> q;
	q.push(left);
	q.push(right);
	while(! s.empty() )
	{
		left =q.front();   q.pop();
		right=q.front();   q.pop();
		int mid = Partition(ar,left,right);
		if(left < mid-1)
		{
			q.push(left);
			q.push(mid-1);
		}
		if(mid+1 < right)
		{
			q.push(mid+1);
			q.push(right);
		}
	}
}

四、快排的随机数法和三位取中法的优化

  1. 随机数法
template<class Type>          //随机数法
int RandPartition(Type *ar,int left,int right)
{
	srand(time(NULL));
	int pos = rand() %(right - left + 1) + left;
	swap(ar[left],ar[pos]);
	return Partition(ar,left,right);
}

  1. 三位取中法
template<class Type>         //三位取中法
int MidPartition(Type *ar,int left,int right)
{   // 23 89 45 
	int mid = (right - left + 1)/2 + left;
	//ar[left] , ar[mid] , ar[right];
	if(ar[mid] > ar[left] && ar[mid] < ar[right])
	{
		swap(ar[mid],ar[left]);
	}else if(ar[right] > ar[mid] && ar[right] < ar[left])
	{
		swap(ar[right],ar[left]);
	}

	return Partition(ar,left,right);
}

这个三位取中法存在严重错误:两个if语句无法确定三个数排序的各种序列。可以考虑通过以下方法优化:

  • 定义一个结构体:
struct MidNode
{
      Type data;
      int indax;
}
  • STL库中的三位取中函数,Mid[3]={X,X,X} ------> mid[1]

五、快排一次划分:求第几小数

int Select_K_Min (int *ar,int n,int k)
{
	if(ar == NULL || n < 1 || k<1 || k > n)
		return -1;
	else
		return Select_K(ar,0,n-1,k);
}

int Select_K(int *ar,int left,int right,int k)
{
	if(left == right && k == 1) return ar[left];

	int pos = Partition(ar,left,right);
	int j = pos - left + 1;
	if(k <= j) return Select_K(ar,left,pos,k);
	else return Select_K(ar,pos+1,right,k - j);
}

六、快排一次划分:最接近点对问题

int CPari(int *ar,int n)
{
	if(ar == NULL || n < 1) return INT_MAX;
	else 
		return cPari(ar,0,n-1);
}


int cPari(int *ar,int left,int right)
{
	if(right - left <= 0) return INT_MAX;
	int k = (right - left + 1)/2;
	int pos = left + k - 1;
	Select_K(ar,left,right,k);// 0 1 2 3 4  // 5 6 7 8 9

	int d1 = CPari(ar,left,pos); //s1;
	int d2 = CPari(ar,pos+1,right); //s2;
	int maxs1 = MaxS1(ar,left,pos); //
	int mins2 = MinS2(ar,pos+1,right);
	return min(min(d1,d2),mins2 - maxs1);
}

int  MaxS1(int *ar,int left,int right)
{
	return ar[right];
}
int  MinS2(int *ar,int left,int right)
{
	int tmp = ar[left];
	for(int i = left+1;i<=right;++i)
	{
		if(tmp > ar[i])
		{
			tmp = ar[i];
		}
	}
	return tmp;
}

int main()
{
	int ar[]={56,67,12,23,90,85,45,78,34,100,20,14,55,23};
	int n = sizeof(ar)/sizeof(ar[0]);

	int minlen = CPari(ar,n);
	cout<<minlen<<endl;
	cout<<endl;

	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值