算法与数据机构入门经典-学习笔记2.2(排序算法之快速排序)

 

  • 本周的学习内容包括:

     1.堆排序

   主要包括完全二叉树概念及其数组表示、堆的概念与堆的建立实现、堆排序、优先级队列

    2.快速排序

   主要包括快速排序的算法设计思路、快速排序的递归实现、快速排序的优化

   3.归并排序

   主要归并排序的设计思路、递归实现、循环实现

   4.STL中排序sort

                                                                                                 正文 


今天学习:主要包括快速排序的算法设计思路、快速排序的递归实现、快速排序的优化

1、算法设计思路

 快速排序一般是基于递归实现的排序算法。在一趟排序中,将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再递归重新对这两部分进行快速排序。

示意图:

给定一个数组,将其变为右边区间内每一个元素均比左边大,选定一个元素,通过调整,使当前元素左边均比他小,右边元素均比他大。

 

给定一个数组,将其变为右边区间内的每一个元素均比左边大

  • 选定第一个元素为pivot;
  • 给定两个数组指针,low和high,low从数组第一个元素,high为数组最后一个元素。
  • high不断向前查找,发现比选定元素小,就与low指向元素交换;
  • low不断向后查找,发现比选定元素大,就与high指向元素交换
  • 不断循环3和4 ,直到low>=high

举例:

代码实现上面算法:

void swap(int* a, int* b)
{
	int item = *a;
	*a = *b;
	*b = item;
}//交换函数,a,b为指针

//对a数组从low到high区间拆分成大小两半
int pivot_sperate(int *a, int low, int high)
{
	int pivot = a[low];//区间内第一个元素设置为pivot
	while (low < high)
	{
		while (low < high && a[high] >= pivot) high--;
		swap(&a[low], &a[high]);
		while (low < high && a[low] <= pivot) low++;
		swap(&a[high], &a[low]);
	}//将比pivot大的交换到右面,小的放在左边
	return low;
}//返回最终pivot值对应数组下标

2、快速排序

  • 快速排序一般基于递归实现的排序算法。具体思路如下:
  • 选定一个合适的值(理想情况下中值最好,但实现中一般使用数组第一个值),称为piovt。
  • 基于povit,将数组分为两个部分,较小的分在左边,较大的分在右边。
  • 对两个子数组分别递归重复上述过程,直到每个数组只有一个元素。
  • 排序完成

  快速排序具体分析

 

快速排序代码实现:

void quick_sort(int *a, int low, int high)
{
	int pivot;
	if (low < high)
	{
		pivot = pivot_sperate(a, low, high);
		quick_sort(a, low, pivot - 1);
		quick_sort(a, pivot + 1, high);
	}//继续递归排序pivot值左右区间
}

 快速排序代码测试:

#include<stdio.h>
#include<malloc.h>
#include<string.h>

void swap(int* a, int* b)
{
	int item = *a;
	*a = *b;
	*b = item;
}//交换函数,a,b为指针

//对a数组从low到high区间拆分成大小两半
int pivot_sperate(int *a, int low, int high)
{
	int pivot = a[low];//区间内第一个元素设置为pivot
	while (low < high)
	{
		while (low < high && a[high] >= pivot) high--;
		swap(&a[low], &a[high]);
		while (low < high && a[low] <= pivot) low++;
		swap(&a[high], &a[low]);
	}//将比pivot大的交换到右面,小的放在左边
	return low;
}//返回最终pivot值对应数组下标

void quick_sort(int *a, int low, int high)
{
	int pivot;
	if (low < high)
	{
		pivot = pivot_sperate(a, low, high);
		quick_sort(a, low, pivot - 1);
		quick_sort(a, pivot + 1, high);
	}//继续递归排序pivot值左右区间
}

int main()
{
	int a[105] = { 4, 9, 1, 3, 5, 7, 6, 2, 8 };
	int i;
	quick_sort(a, 0, 8);//快速排序
	for (i = 0; i < 9; i++)
	{
		printf("%d ", a[i]);//输出
	}
	printf("\n");
	return 0;
}

测试结果:

快速排序复杂度分析:

  • 最好的情况:

每次取得的数字刚刚好能够平分数组

递归遍历过程算法复杂度O(logn)

依据pivot的数组移动复杂度O(n)

综上,最优算法复杂度O(nlogn)

  • 最差情况:

每次取得数组刚刚好的最大或最小数,排序过程趋向于冒泡排序

故最差算法复杂度O(n^2)

空间复杂度:O(1)

他是不稳定的排序算法:

3、快速排序优化

分三种情况,随机数组,升序数组,降序数组,重复数组。

在一些特定情况下,快速排序的时间复杂依然趋于n^2. 像升序和降序以及重复数组。

所以如何将之从n^2过度到nlog n呢?

可以从以下几个方面进行思考,从随机选取pivot、三位取中选取pivot、聚集相等元素等方面考虑,也是三个方法。

  • 随机选取pivot

考虑到对于升序和降序数组,由于我们选取pivot的时候,默认选取第一个元素,使得序列划分非常不均等。

代码:

void swap(int* a, int* b)
{
	int item = *a;
	*a = *b;
	*b = item;
}

int pivot_sperate(int *a, int low, int high)
{
	int pivot_idx = (rand() % (high - low)) + low;//从low到high中随机选取一个数字作为pivot
	int pivot;
	swap(&a[pivot_idx], &a[low]);//交换随机选取的pivot和low的值
	pivot = a[low]; //将随机选取的值作为pivot
	while (low < high)
	{
		while (low < high && a[high] >= pivot) high--;//当low<high且high的值>pivot时,high左移
		swap(&a[low], &a[high]);//发现high的值<pivot时,交换两则的值;
		while (low < high && a[low] <= pivot) low++;//当low<high且low的值<pivot时,low右移
		swap(&a[high], &a[low]);//发现low的值>pivot时,交换两则的值;
	}
	return low;//返回此时pivot的在数组中的下标
}

效果: 

  • 三位取中选取pivot

代码:

void swap(int* a, int* b)
{
	int item = *a;
	*a = *b;
	*b = item;
}

int pivot_sperate(int *a, int low, int high)
{
	int mid = (high + low) / 2;
	int pivot;
	if (a[mid] > a[high])
	{
		swap(&a[mid], &a[high]);
	}
	if (a[low] > a[high])
	{
		swap(&a[low], &a[high]);
	}
	if (a[mid] > a[low])
	{
		swap(&a[mid], &a[low]);
	}

	pivot = a[low];//将三位取中的值作为pivot
	while (low < high)
	{
		while (low < high && a[high] >= pivot) high--;
		swap(&a[low], &a[high]);
		while (low < high && a[low] <= pivot) low++;
		swap(&a[high], &a[low]);
	}
	return low;//返回pivot的下标作为分成两半的算法结果
}

void quick_sort(int *a, int low, int high)
{
	int pivot;
	if (low < high)
	{
		pivot = pivot_sperate(a, low, high);
		quick_sort(a, low, pivot - 1);
		quick_sort(a, pivot + 1, high);
	}
}

效果:

  • 聚集相等元素

示意图:

代码:

void swap(int* a, int* b)
{
	int item = *a;
	*a = *b;
	*b = item;
}

int pivot_sperate(int *a, int low, int high)
{
	int pivot;
	int mid = (high + low) / 2;
	if (a[mid] > a[high])
	{
		swap(&a[mid], &a[high]);
	}
	if (a[low] > a[high])
	{
		swap(&a[low], &a[high]);
	}
	if (a[mid] > a[low])
	{
		swap(&a[mid], &a[low]);
	}

	pivot = a[low];
	while (low < high)
	{
		while (low < high && a[high] >= pivot) high--;
		swap(&a[low], &a[high]);
		while (low < high && a[low] <= pivot) low++;
		swap(&a[high], &a[low]);
	}
	return low;
}

void quick_sort(int *a, int low, int high)
{
	int i, pivot, recursion_pos, item;
	if (low < high)
	{
		pivot = pivot_sperate(a, low, high);
		recursion_pos = pivot - 1;
		for (i = pivot - 1; i >= low; i--)
		{
			if (a[i] == a[pivot])
			{
				a[i] = a[recursion_pos];
				a[recursion_pos--] = a[pivot];
			}
		}//左半边与pivot相等的元素靠右边
		//只递归从low到不与pivot相对应值相等的最好一位
		quick_sort(a, low, recursion_pos - 1);

		recursion_pos = pivot + 1;
		for (i = pivot + 1; i < high; i++)
		{
			if (a[i] == a[pivot])
			{
				a[i] = a[recursion_pos];
				a[recursion_pos++] = a[pivot];
			}//右半边与pivot对应值相等元素靠左放
		}
		quick_sort(a, recursion_pos + 1, high);
	}//只递归pivot对应值不相等的位置到high的部分
}

效果:

这种方式对各种数组都是最好的方法!!!万德福!终于明白为啥有些算法毫无根据喜欢取中值啦 ,哈哈哈。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值