【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)

04-14 1615阅读

【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)

文章目录

  • 📝快速排序
    • 🌠霍尔法
    • 🌉三指针法
    • 🌠挖坑法
      • ✏️优化快速排序
      • 🌠随机选key
        • 🌉三位数取中
        • 🌠小区间选择走插入,可以减少90%左右的递归
        • 🌉 快速排序改非递归版本
        • 🚩总结

          📝快速排序

          快速排序是一种分治算法。它通过一趟排序将数据分割成独立的两部分,然后再分别对这两部分数据进行快速排序。

          本文将用3种方法实现:

          🌠霍尔法

          霍尔法是一种快速排序中常用的单趟排序方法,由霍尔先发现。

          它通过选定一个基准数key(通常是第一个元素),然后利用双指针left和right的方式进行排序,right指针先找比key基准值小的数,left然后找比key基准值大的数,找到后将两个数交换位置,同时实现大数右移和小数左移,当left与right相遇就排序完成,然后将下标key的值与left交换,返回基准数key的下标,完成了单趟排序。这一过程使得基准数左侧的元素都比基准数小,右侧的元素都比基准数大。

          如图动图展示:

          【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)

          以下是单趟排序的详解图解过程:

          • begin和end记录区间的范围,left记录做下标,从左向右遍历,right记录右下标,从右向左遍历,以第一个数key作为基基准值

            【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)

          • 先让right出发,找比key值小的值,找到就停下来

            【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)

          • 然后left再出发,找比key大的值,若是找到则停下来,与right的值进行交换

            【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)

          • 接着right继续找key小的值,找到后才让left找比key大的值,直到left相遇right,此时left会指向同一个数

            【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)

            【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)

          • 将left与right指向的数与key进行交换,单趟排序就完成了,最后将基准值的下标返回

            【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)

            为啥相遇位置比key要小->右边先走保证的

            1. L遇R: R先走,R在比key小的位置停下来了,L没有找到比key大的,就会跟R相遇相遇位置R停下的位置,是比key小的位置
            2. R遇L:第一轮以后的,先交换了,L位置的值小于key,R位置的值大于key ,R启动找小,没有找到,跟L相遇了,相遇位置L停下位置,这个位置比key小
            • 第一轮R遇L,那么就是R没有找到小的,直接就一路左移,遇到L,也就是key的位置

              代码实现

              void Swap(int* px, int* py)
              {
              	int tmp = *px;
              	*px = *py;
              	*py = tmp;
              }
              //Hoare经典随机快排
              void QuickSort1(int* a, int left, int right)
              {
              	// 如果左指针大于等于右指针,表示数组为空或只有一个元素,直接返回
              	if (left >= right)
              		return;
              	// 区间只有一个值或者不存在就是最小子问题
              	int begin = left, end = right;// begin和end记录原始整个区间
              	// keyi为基准值下标,初始化为左指针
              	int keyi = left;
              	 // 循环从left到right
              	while (left  
              

              取中的返回函数接收:

              		int begin = left, end = right;
              		// 三数取中
              		int midi = GetMidi(a, left, right);
              		//printf("%d\n", midi);
              		Swap(&a[left], &a[midi]);
              

              整体函数实现:

              //三数取中  left  mid  right
              //大小居中的值,也就是不是最大,也不是最小的
              int GetMid(int* a, int left, int right)
              {
              	int mid = (left + right) / 2;
              	
              	if (a[left]  a[right])
              		{
              			return left;
              		}
              		else
              		{
              			return  right;
              		}
              	}
              	else//a[left] > a[mid]
              	{
              		if (a[mid] > a[right])
              		{
              			return mid;
              		}
              		else if (a[right] > a[left])
              		{
              			return left;
              		}
              		else
              		{
              			return right;
              		}
              	}
              }
              void QuickSort4(int* a, int left, int right)
              {
              	if (left >= right)
              		return;
              	int begin = left, end = right;
              	//三数取中
              	int midi = GetMid(a, left, right);
              	//printf("%d\n",midi);
              	Swap(&a[left], &a[midi]);
              	int keyi = left;
              	while (left = a[keyi])
              		{
              			--right;
              		}
              		while (left = a[keyi])
              			{
              				--right;
              			}
              			while (left top = 0;
              	ps->capacity = 0;
              }
              void STDestroy(ST* ps)
              {
              	assert(ps);
              	free(ps->a);
              	ps->a = NULL;
              	ps->top = ps->capacity = 0;
              }
              // 栈顶
              void STPush(ST* ps, STDataType x)
              {
              	assert(ps);
              	// 满了, 扩容
              	if (ps->top == ps->capacity)
              	{
              		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
              		STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
              		if (tmp == NULL)
              		{
              			perror("realloc fail");
              			return;
              		}
              		ps->a = tmp;
              		ps->capacity = newcapacity;
              	}
              	ps->a[ps->top] = x;
              	ps->top++;
              }
              void STPop(ST* ps)
              {
              	assert(ps);
              	assert(!STEmpty(ps));
              	ps->top--;
              }
              STDataType STTop(ST* ps)
              {
              	assert(ps);
              	assert(!STEmpty(ps));
              	return ps->a[ps->top - 1];
              }
              int STSize(ST* ps)
              {
              	assert(ps);
              	return ps->top;
              }
              bool STEmpty(ST* ps)
              {
              	assert(ps);
              	return ps->top == 0;
              }
              

              栈的头文件实现:

              #pragma once
              #include
              #include
              #include
              #include
              typedef int STDataType;
              typedef struct Stack
              {
              	STDataType* a;
              	int top;
              	int capacity;
              }ST;
              void STInit(ST* ps);
              void STDestroy(ST* ps);
              // 栈顶
              void STPush(ST* ps, STDataType x);
              void STPop(ST* ps);
              STDataType STTop(ST* ps);
              int STSize(ST* ps);
              bool STEmpty(ST* ps);
              

              🚩总结

              快速排序的特性总结:

              1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
              2. 时间复杂度:O(N*logN)
              3. 空间复杂度:O(logN)
              4. 稳定性:不稳定

              因此

              时间复杂度:O(N*logN)

              什么情况快排最坏:有序/接近有序 ->O(N^2)

              但是如果加上随机选key或者三数取中选key,最坏情况不会出现,所以这里不看最坏

              快排可以很快,你的点赞也可以很快,哈哈哈,感谢💓 💗 💕 💞,喜欢的话可以点个关注,也可以给博主点一个小小的赞😘呀

              【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]