快排三种基本解法以及两种快排优化

/*
 快速排序
 基本思想
   选定每次排序的基准数据 在剩下的位置将小于基准值的数据放在基准值得左边,大于基准值的数据放到基准值的右边
   一次划分之后 如果此基准值的左右两边仍存在大于两个数据  则继续划分排序 直至每个数字都有序
  递归实现Quick_Sort1(int *arr,int len):快排一次划分的时间复杂度为O(logn) 最坏就是在有序条件下 时间复杂度为O(n^2)
  整体时间复杂度为O(n*logn)
  非递归实现Quick_Sort2(int *arr,int len)

 快排三种实现方式
   第一种就是选取第一个元素或者最后一个元素作为基准 代码是Quick_Sort1 和Quick_Sort2
   这样的坏处是如果所给序列是有许多的  那么时间复杂度太大 选取的基准点就失去了意义 并不能称作是基准值了
   第二种随机取值法
    绝大多数情况下保证了时间复杂度最好情况是O(logn)  但是最坏情况下 假如整个数组的数字都相等 那么时间复杂度还是能达到O(n^2)
   第三种 三数取中
   找到low high  mid = low + ((high - low) >> 1);//计算数组中间的元素的下标    

取这三个下标对应的最小值 保证arr[low]是这三个数之间的第二大值 然后又可以和普通划分函数一样

*/

void swap(int *a,int *b)
{
	int tmp  = *a;
	*a = *b;
	*b = tmp;
}
//第一种划分函数:以第一个数字为基准值进行一次划分 最后返回基准的下标
int partion(int *arr,int low,int high)
{
	if(arr == NULL )
		return -1;
	int tmp = arr[low];

	while(low < high)
	{
		while((low < high) && arr[high] >= tmp)
		{
			high--;
		}
		if(low == high)//如果遇到比基准值小的 放到low的位置
		{
			break;
		}
		else
		{
			arr[low] = arr[high];
		}

		while((low < high) && arr[low] <=  tmp)
		{
			low++;
		}
		if(low == high)
		{
			break;
		}
		else
		{
			arr[high] = arr[low];
		}
	}
	arr[low] = tmp;
	return low;
}
//第二种划分函数:随机选取基准划分
int part(int *arr,int low,int high)
{
	srand((unsigned)time(NULL));
	//产生随机位置
    int pivotPos = rand()%(high - low) + low;
	//将此随机位置的值与low位置的值交换 又可以和普通划分函数一样
	swap(arr[pivotPos],arr[low]);

	int tmp = arr[low];

	while(low < high)
	{
		while((low < high) && arr[high] >= tmp )
		{
			high--;
		}
		if(low == high)//如果遇到比基准值小的 放到low的位置
		{
			break;
		}
		else
		{
			arr[low] = arr[high];
		}

		while((low < high) && arr[low] <=  tmp)
		{
			low++;
		}
		if(low == high)
		{
			break;
		}
		else
		{
			arr[high] = arr[low];
		}
	}
	arr[low] = tmp;
	return low;
}
//第三种划分函数
int Select_Mid(int *arr,int low,int high)
{
	//int mid = low + ((high - low) >> 1);//计算数组中间的元素的下标  
	int mid = low + ((high - low) >> 1);
    //使用三数取中法选择枢轴  
    if (arr[mid] > arr[high])//目标: arr[mid] <= arr[high]  
    {  
        swap(arr[mid],arr[high]);  
    }  
    if (arr[low] > arr[high])//目标: arr[low] <= arr[high]  
    {  
        swap(arr[low],arr[high]);  
    }  
    if (arr[mid] > arr[low]) //目标: arr[low] >= arr[mid]  
    {  
        swap(arr[mid],arr[low]);  
    }  
	return arr[low];
}
//三数取中的一次划分
int part1(int *arr,int low,int high)
{
	
	int tmp = Select_Mid(arr,low,high);

	while(low < high)
	{
		while((low < high) && arr[high] >= tmp )
		{
			high--;
		}
		if(low == high)//如果遇到比基准值小的 放到low的位置
		{
			break;
		}
		else
		{
			arr[low] = arr[high];
		}

		while((low < high) && arr[low] <=  tmp)
		{
			low++;
		}
		if(low == high)
		{
			break;
		}
		else
		{
			arr[high] = arr[low];
		}
	}
	arr[low] = tmp;
	return low;
}
static void Quick(int *arr,int start,int end)
{
	int par = part1(arr,start,end);
	if(par > start + 1)
	{
		Quick(arr,start,par-1);
	}
	if(par < end -1)
	{
		Quick(arr,par+1,end);
	}
}
void Quick_Sort1(int *arr,int len)
{
	if(arr == NULL ||len < 0)
		return;
	for(int i = 0 ;i < len - 1 ;++i)
	{
		Quick(arr,0,len-1);
	}
}

快排非递归实现

//用栈保存每一次的一对low和high区间 空间复杂度为O(n*logn)
void Quick_Sort2(int *arr,int len)
{
	if( arr == NULL ||len < 0)
		return ;
	int low = 0;
	int high = len - 1;
	int par =  partion(arr,low,high);
	stack<int> st;
	if(par > low + 1)
	{
		st.push(low);
		st.push(par-1);
	}
	if(par < high - 1)
	{
		st.push(par+1);
		st.push(high);
	}
	while(!st.empty())
	{
		int high = st.top();st.pop();
		int low = st.top();st.pop();
		int par = partion(arr,low,high);
		if(par > low + 1)
		{
			st.push(low);
			st.push(par-1);
	}
	if(par < high - 1)
	{
		st.push(par+1);
		st.push(high);
	}
	}
}

/*

快排优化一

在low和high之间的举例为10以内时,可以选择直接插入排序 节省时间

*/

//快排优化一 在划分到适合的长度进行直接插入排序
void insert_sort(int *arr,int low,int high)
{
	if(arr == NULL || low < 0 || high < 0)
		return ;
	int tmp = 0;
	int j;
	for(int i = low+1;i < high; i++)
	{
		tmp = arr[i];
		for(j = i-1;j >= 0;j--)
		{
			if(arr[j] > tmp)
			{
				arr[j+1] = arr[j];
			}
			else
			{
				break;
			}
		}
		arr[j+1] = tmp;
	}
}
static void Quick1(int *arr,int start,int end)
{
	int par = part1(arr,start,end);
	if(end - start + 1 < 10)
	{
			insert_sort(arr,start,end);
	}
	else
	{
		if(par > start + 1)
		{
			Quick(arr,start,par-1);
		}
		if(par < end -1)
		{
			Quick(arr,par+1,end);
		}
	}
}
void Quick_Sort3(int *arr,int len)
{
	if(arr == NULL ||len < 0)
		return;
	for(int i = 0 ;i < len - 1 ;++i)
	{
		Quick(arr,0,len-1);
	}
}

/*

 快排优化二
   在一次划分之后 将与基准值相等的元素聚集在一起不再进行下一次的划分操作 可以减少迭代查找次数
   具体操作将与基准值相等的数放到数组的两端 一次划分完成后 将这些数字挪回基准值得范围 具体过程如下图所示

*/

//快排优化二 一次划分后将基准值调至靠近基准值周围 与基准值相等的数字不再参加下一次划分
void QuickSort(int *arr,int low,int high)
{
	int first = low;
    int last = high;

    int left = low;
    int right = high;

    int leftLen = 0;
    int rightLen = 0;


    //一次分割
    int key = Select_Mid(arr,low,high);//使用三数取中法选择枢轴

    while(low < high)
    {
        while(high > low && arr[high] >= key)
        {
            if (arr[high] == key)//处理相等元素
            {
                swap(&arr[right],&arr[high]);
                right--;
                rightLen++;
            }
            high--;
        }
        arr[low] = arr[high];

        while(high > low && arr[low] <= key)
        {
            if (arr[low] == key)
            {
                swap(&arr[left],&arr[low]);
                left++;
                leftLen++;
            }
            low++;
        }
        arr[high] = arr[low];
    }
    arr[low] = key;

    //一次快排结束
    //把与枢轴key相同的元素移到枢轴最终位置周围
    int i = low - 1;
    int j = first;
    while(j < left && arr[i] != key)
    {
        swap(&arr[i],&arr[j]);
        i--;
        j++;
    }
    i = low + 1;
    j = last;
    while(j > right && arr[i] != key)
    {
        swap(&arr[i],&arr[j]);
        i++;
        j--;
    }
	//如果左右两端都还有超过两个数据 那么就需要进行划分排序
	if(low-1 > first+1)
	{
		QuickSort(arr,first,low - 1 - leftLen);
	}
	if(low+1 < last-1)
	{
		QuickSort(arr,low + 1 + rightLen,last);
	}

}

 

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值