分治法-快速排序

1.快速排序

快速排序是冒泡排序的一种改进,它平均的时间复杂度为O(nlogn)

快速排序的思路如下:对于输入的数组a[p: r]

  1. 分解:以a[p]作为基准值,将数组分为三段 a[p:q-1] a[q]和a[q+1: r],并且满足a[p: q-1]的值小于等于a[q],而a[q+1:r]的值大于等于a[q]。下标q在划分过程中确定。
  2. 递归:递归调用快速排序分别对a[p:q-1]和a[q+1:r]进行排序。
  3. 合并:由于快速排序是原地排序,所以不需要合并。

由于快速排序是对冒泡排序的改进,所以快速排序在每次排序都会确认一个元素的最终值。

快速排序的代码并不算困难:

 

/**
 * 对a[p:r]进行划分 扩展两个区域a[p:i]和a[j:r]
 * @param nums: 数组
 * @param start: 基准值索引 在这里也是第一个值的索引
 * @param end: 数组最后一个值的索引
*/
template<class Type>
int partition(Type nums[], int start, int end) {
	int left = start, right = end + 1;
	Type x = nums[start];
	//将 < x的元素交换到左边区域 > x的元素交换到右边区域
	while (true) {
		while (nums[++left] < x && left < end);
		while (nums[--right] > x);
		if (left >= right) break;

		Type temp = nums[left];
		nums[left] = nums[right];
		nums[right] = temp;
	}
	nums[start] = nums[right];
	nums[right] = x;
	return right;
}

partition函数定死start为基准值,同时也是数组的起始位置。

 

按照快速排序的思路,快速排序的一趟遍历就是让小于等于基准值的元素在基准值左边;大于等于基准值的元素在基准值的右边。partition函数所做的工作如下:

  1. left从左向右遍历,找到一个大于基准值的元素;
  2. right从右向左,找到一个小于基准值的元素,然后两个元素相互交换。重复上述过程直至left >= right。
  3. 之后让基准值和right所在的元素进行交换。

 

/**
 * 快速排序
 * @param nums: 数组
 * @param p: 基准值索引
 * @param r: 数组最后一个值的索引
*/
template<class Type>
void quickSort(Type nums[], int p, int r) {
	if (p < r) {
		int q = partition(nums, p, r);
		quickSort(nums, p, q - 1);
		quickSort(nums, q + 1, r);
	}
}

之后是主函数:

int main() {
	int a[] = { 4, 2, 6, 7, 5, 3, 2};
	int len = sizeof(a) / sizeof(a[0]);
	
	quickSort(a, 0, len - 1);

	for (int i = 0; i < len; i++)
		cout << a[i] << " ";
	cout << endl;
	return 0;
}

2.快速排序的改进

快速排序的性能就在于基准值的选择,最坏情况下就是每次选择的基准值都是当前的最值,此时分成了n-1个元素和1个元素,其最坏时间复杂度为O(n^{2})。所以基准值的选择也是比较重要的。

2.1 随机选择一个元素

随机选择一个元素作为基准值。

/**
 * 随机选择一个基准值index属于[start, end],之后nums[start] 交换 nums[index],
 * @params nums: 数组
 * @params start: 起始索引
 * @params end: 结束索引
*/
template<class Type>
int randomPartition(Type nums[], int start, int end) {
    //随机数
	random_device rd;
	default_random_engine random(rd());
	//随机数分布对象 [start, end]
	uniform_int_distribution<unsigned> uniform(start, end);
	int index = uniform(random);
	//交换值
	Type temp = nums[start];
	nums[start] = nums[index];
	nums[index] = temp;
 
	return partition(nums, start, end);
}
	

这个函数在a[start: end]中随机选择一个元素作为基准值。

default_random_engine为c++11提供的 随机数生成函数,其包含在#include <random>库中。

template<class Type>
void quickSort(Type nums[], int p, int r) {
	if (p < r) {
		int q = randomPartition(nums, p, r);
		quickSort(nums, p, q - 1);
		quickSort(nums, q + 1, r);
	}
}

接着要在快速排序中调用这个函数。 

3.测试

测试基于LeetCode的排序数组

快速排序的性能测试:

 2.1的快速排序的性能测试:

4. 参考

分治法是一种常用的算法设计策略,快速排序是其中一种经典的应用之一。下面是用C++实现快速排序分治法的步骤: 1. 选择一个基准元素(pivot),可以是数组中的任意一个元素。 2. 将数组分成两个子数组,使得左边的子数组中的元素都小于等于基准元素,右边的子数组中的元素都大于等于基准元素。这个过程称为分区(partition)。 3. 对左右两个子数组分别递归地进行快速排序。 4. 合并左右两个子数组,得到最终的排序结果。 下面是用C++实现快速排序的代码示例: ```cpp #include <iostream> using namespace std; // 分区函数,将数组分成两个子数组 int partition(int arr[], int low, int high) { int pivot = arr[high]; // 选择最后一个元素作为基准元素 int i = low - 1; // i表示小于等于基准元素的子数组的边界 for (int j = low; j < high; j++) { if (arr[j] <= pivot) { i++; swap(arr[i], arr[j]); // 将小于等于基准元素的元素交换到左边 } } swap(arr[i + 1], arr[high]); // 将基准元素放到正确的位置上 return i + 1; // 返回基准元素的索引 } // 快速排序函数 void quickSort(int arr[], int low, int high) { if (low < high) { int pivotIndex = partition(arr, low, high); // 分区,得到基准元素的索引 quickSort(arr, low, pivotIndex - 1); // 对左子数组进行快速排序 quickSort(arr, pivotIndex + 1, high); // 对右子数组进行快速排序 } } // 测试代码 int main() { int arr[] = {5, 2, 9, 1, 6, 3}; int n = sizeof(arr) / sizeof(arr); quickSort(arr, 0, n - 1); cout << "排序结果:"; for (int i = 0; i < n; i++) { cout << arr[i] << " "; } cout << endl; return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值