【数据结构】八大排序之快速排序(递归和非递归方法)

上一博文我们讲了冒泡排序,但是由于他的时间复杂度过高为O(n*n),于是在冒泡排序的基础上今天要说的是快速排序。

 

本文讲述两个内容:

1.快速排序的三种方法。

2.快速排序的优化

 

一.什么是快速排序???

     通过一趟排序将要排序的数据分割成独立的两部分其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

 中心思想就是:找一个基准值,把数据分为两部分,然后去排序,类似于二分查找。

 

1.快速排序的的时间复杂度:

          平均为:O(n×log(n))

          最坏情况下为:O(n * n)

 

2.快速排序不是稳定的排序

 

二.快速排序递归的三种方法:

1.挖坑法

2.交换指针法(左右指针)

3.交换指针(前后指针)

 

(1.)递归:

1.)挖坑法(递归)

      1.在数组中选取一个基准值,我在这里选取的是数组第一个数据

      2.定义两个指针(left,right)分别指向数组的首位和末尾(left = 0,right = sz - 1)

    3.将left看为第一个坑,然后用基准值和right指针所指的数比较如果比基准值大,则right指针向左移(减减)。反之则将right指针所指的值付给left所指的的值,letf指针向右移(加加)。

          再将right指针所指位置看为一个坑,用left指针所指的值和基准值比较,如果比基准值小,则left指针向右移(加加)。反之则将left所指的值付给right指针所指的值,right指针向左移。

           最后将point值赋给最后的坑的位置

我们以数组int array[ ] = { 4, 7, 6, 5, 3, 2, 8, 1 }; 为例

 

代码展示:

void quickSort(int* array, int left, int right)
{
	if (left >= right)
		return;

	int Index = partsort(array, left, right);

	//分开而治
	//左半部分
	quickSort(array, left, Index - 1);

	//右半部分
	quickSort(array, Index + 1, right);
}


int partsort(int* array, int left, int right)
{
	//基准值
	int ponit = array[left];
	
	//坑的位置
	int temp = left;

	while (left <= right)
	{
		while (left <= right)
		{
			if (array[right] < ponit)
			{
				array[left] = array[right];
				++left;
				temp = right;
				break;
			}
			else
				--right;
		}
		while (left <= right)
		{
			if (array[left] > ponit)
			{
				array[right] = array[left];
				--right;
				temp = left;
				break;
			}
			else
				++left;
		}

	}
	array[temp] = ponit;
	return temp;
}

2.交换指针法(左右指针)

     1.和挖坑法一样定义两个指针(left和right)分别指向数组的首部和数组的尾部(left = 0,right = sz - 1)

     2.还是以数组第一个数为基准值 int ponit = array[left]

  3.首先移动right指针,如果right指针所指的值比基准值小,则right指针不动,去移动left指针。否则right指针向左移动,知道移动到比基准值大的地方停下。

    4.移动left指针,如果left指针所指的值比基准值大,则left不动,然后交换左右指针所指的值,否则left指针想右移动,知道找到比基准值大的值停下,然后交换两个指针所指的值。

      5.最后交换将ponit的值和left指针所指的值交换

我们以数组int array[ ] = { 4, 7, 6, 5, 3, 2, 8, 1 }; 为例

 

代码展示:

//指针交换法(前后指针)--->递归
void quickSort(int* array, int left, int right)
{
	if (left >= right)
		return;

	int Index = partsort(array, left, right);

	//分开而治
	//左半部分
	quickSort(array, left, Index - 1);

	//右半部分
	quickSort(array, Index + 1, right);
}


int partsort(int* array, int left,int right)
{

	int Index = left;
	int ponit = array[Index];

		while (left < right)
		{
			while (left < right)
			{
				if (array[right] < ponit)
					break;
				else
					--right;
			}

			while (left < right)
			{
				if (array[left] > ponit)
					break;
				else
					++left;
			}


			int temp = array[right];
			array[right] = array[left];
			array[left] = temp;
		}

		
		int num = array[left];
		array[left] = array[Index];
		array[Index] = num;
		return  left;
}

 

(2.)非递归法

在上边的三种方法中我们都用了递归的方法,当我们要用非递归时,要立刻想到栈,因为递归的原理符合栈的先进后出的规则,递归和栈在面试题和算法中总是联系在一起的。

代码展示:

int partsort(int* array, int left, int right)
{
	//基准值
	int ponit = array[left];
	
	//坑的位置
	int temp = left;

	while (left <= right)
	{
		while (left <= right)
		{
			if (array[right] < ponit)
			{
				array[left] = array[right];
				++left;
				temp = right;
				break;
			}
			else
				--right;
		}
		while (left <= right)
		{
			if (array[left] > ponit)
			{
				array[right] = array[left];
				--right;
				temp = left;
				break;
			}
			else
				++left;
		}

	}
	array[temp] = ponit;
	return temp;
}

void QuickSort(int* array, int left, int right)
{
    
	stack<int> s;
	
	s.push(left);
	
	s.push(right);
	
	while (!s.empty())
	{
		int right = s.top();
		s.pop();
		int left = s.top();
		s.pop();

		//划分左右部分的边界线
		int Index = partsort(array, left, right);

		//左半部分
		if (Index - 1 > left)
		{
			s.push(left);
			s.push(Index - 1);
		}

		//右半部分
		if (Index + 1 < right)
		{
			s.push(Index + 1);
			s.push(right);
		}
	}

}

三.快速排序的优化

对于快速排序,根据我们上边的代码我们可以看出最主要的是选取基准值,我上边的代码都是以左边的数为基准值。这样就会产生一种情况:如果数据是正序或者逆序时,我们这样选取的基准值显然是不对的,这样的其他数据都在基准值的一侧,我们是无法划分出两个区域来分开排序的。为了解决这个问题就要用到以下两个方法:

1.三数取中法

每次在排序之前,我们都无法知道数据是逆序或者是正序,如果每次去检测那就太麻烦,所以我们选取三个数值(left,right,mid)如果是正序或者逆序那么就直接选取中间的数值,反之任意选取两边任意一个数当作基准值。这样就很容易将数据等分开来,大大提高了代码的效率。

(1.)挖坑法的三数取中法

int GetMid(int* array, int left, int right)
{
	//获取中间位置
	int mid = left + ((right - left) >> 1);

	//假设是正序(升序)
	if (array[left] <= array[right])
	{
		//两个if判断到底是不是正序如果不是就返回边上的,如果是就返回中间的
		if (array[mid] < array[left])
			return left;
		else if (array[mid] > array[right])
			return right;
		else
			return mid;
	}
	//假设是逆序
	else
	{
		//两个if语句判断你是否为逆序,如果不是就返回边上的,如果是就返回中间的
		if (array[mid] > array[left])
			return left;
		else if (array[mid] < array[right])
			return right;
		else
			return mid;
	}

}

//挖坑法
void quickSort(int* array, int startIndex, int endIndex)
{
	if (startIndex >= endIndex)
		return;
	
	//边界
	int Index = partsort(array, startIndex, endIndex);
	
	//分开而治
	//左半部分
	quickSort(array, startIndex, Index - 1);

	//右半部分
	quickSort(array, Index + 1, endIndex);
}

int partsort(int* array, int left, int right)
{
	int Index = left;   //坑的位置
	int num = GetMid(array, left, right);
	int temp = array[num];
	array[num] = array[left];
	array[left] = temp;
	int ponit = array[left];  //基准数据
	while (left <= right)
	{
		while (left <= right)
		{
			if (array[right] < ponit)
			{
				array[left] = array[right];
				++left;
				Index = right;
				break;
			}
			else
				--right;
		}

		while (left <= right)
		{
			if (array[left] > ponit)
			{
				array[right] = array[left];
				--right;
				Index = left;
				break;
			}
			else
				++left;
		}

	}

	array[Index] = ponit;

	return Index;
}

(2.)指针替换法

int GetMid(int* array, int left, int right)
{
	//获取中间位置
	int mid = left + ((right - left) >> 1);

	//假设是正序(升序)
	if (array[left] <= array[right])
	{
		//两个if判断到底是不是正序如果不是就返回边上的,如果是就返回中间的
		if (array[mid] < array[left])
			return left;
		else if (array[mid] > array[right])
			return right;
		else
			return mid;
	}
	//假设是逆序
	else
	{
		//两个if语句判断你是否为逆序,如果不是就返回边上的,如果是就返回中间的
		if (array[mid] > array[left])
			return left;
		else if (array[mid] < array[right])
			return right;
		else
			return mid;
	}

}


void quickSort(int* array, int startIndex, int endIndex)
{
	if (startIndex >= endIndex)
		return;
	
	//边界
	int Index = partsort(array, startIndex, endIndex);
	
	//分开而治
	//左半部分
	quickSort(array, startIndex, Index - 1);

	//右半部分
	quickSort(array, Index + 1, endIndex);
}

int partsort(int* array, int left,int right)
{

	int Index = left;
	int arr = GetMid(array, left, right);
	int temp = array[arr];
	array[arr] = array[left];
	array[left] = temp;
	int ponit = array[Index];

	while (left < right)
	{
		while (left < right)
		{
			if (array[right] < ponit)
				break;
			else
				--right;
		}

		while (left < right)
		{
			if (array[left] > ponit)
				break;
			else
				++left;
		}


		int temp = array[right];
		array[right] = array[left];
		array[left] = temp;
	}


	int num = array[left];
	array[left] = array[Index];
	array[Index] = num;
	return  left;
}

2.直接插入法(这个代码在以后的排序中会展示)

由于是递归程序,每一次递归都要开辟栈帧,当递归到序列里的值不是很多时,我们可以采用直接插入排序来完成,从而避免这些栈帧的消耗

 

以上就是我自己总结的快速排序,如果有什么错误,欢迎大家指出,感激不尽!!!!或者有更好的方法,希望大家能评论告诉我,跪拜!!!!希望一起进步!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值