【算法】快排的多种实现

1. 左右指针法实现快排

方法一:左右指针

算法思想

  1. 先选出数组最后元素作为key。
  2. 左边的指针从左开始找到大于等于key的值,右边的指针从右边开始找到小于key的值,在做交换,使得大于等于key的值放在数组的右边,小于key的值放在数组的左边。
  3. 注意最后的时候,要把最后的key值换到左边指针的下一个位置,保证了key值右边的数比key大,左边的数比key小。
  4. 借助递归,不断的缩小区间,使得小区间有序,最后就会整体有序

总结出来就是:左边找大,右边找小。

代码实现

int Partition1(vector<int>& nums, int left, int right)
{
	int l = left;
	int r = right;
	int key = nums[right];               // 选出最右边的数作为key值
	while (l < r)
	{
		while (l < r && nums[r] >= key)  // r只停留在比key值小的数
		{
			r--;
		}
		while (l < r && nums[l] < key)   // l只停留在 >= key值的位置
		{
			l++;
		}
		if (l < r)                       // 如果此时的 l < r 就会把 小于key的值交换到左边, 大于等于key的值交换到右边
		{
			swap(nums[l++], nums[r--]);  // 交换完之后l++ 、 r--
		}
	}
	swap(nums[l+1], nums[right]);
	return l + 1;
}
void QuickSort(vector<int>& nums, int left, int right)
{
	if (left >= right)
		return;
	int mid = Partition1(nums, left, right);    // 选出边界,在继续递归,使得小区间有序
	QuickSort(nums, left, mid - 1);             // [left, mid - 1]  使有序
	QuickSort(nums, mid + 1, right);            // [mid + 1, right] 使有序
}
int main()
{
	vector<int> nums = { 6, 4, 5, 4, 6 };
	QuickSort(nums, 0, nums.size() - 1);
	for (auto e : nums)
	{
		cout << e << " ";
	}
	cout << endl;
	system("pause");
}

结果
在这里插入图片描述

方法二:前后指针

算法思想

  1. 选出数组最右边的数作为key值
  2. 也是两个指针fast和slow,快慢指针。快指针fast一直往前走,慢指针slow维护着从left开始到slow全是小于key值的区间。即【left,slow】都是小于key的值
  3. 快指针fast找小于key值位置,与慢指针slow的下一个位置进行交换。使得小于key值在数组的左边,大于key值的数在数组右边
  4. 最后交换key值到slow的下一个位置,保证左边的值比key值小,右边的值比key值大
  5. 通过递归,使得小区间有序,到大区间有序

代码实现

int Partition2(vector<int>& nums, int left, int right)
{
	int key = nums[right];
	int slow = left - 1;          //刚开始的时候为left的前一个位置
	for (int fast = left; fast < right; ++fast)  //fast一直往前走,去找小于等于key的数
	{
		if (nums[fast] < key)      // 如果fast指针指向的值比key值小,则slow指针就可以向前扩一个,在将slow位置与fast位置交换
		{
			slow = slow + 1;
			swap(nums[slow], nums[fast]);
		}
	}
	//slow向前扩一下,slow + 1最后将key值交换到slow+1的位置,保证key值左边的比它下,右边大于等于它
	swap(nums[slow + 1], nums[right]);    
	return slow + 1;
}
void QuickSort(vector<int>& nums, int left, int right)
{
	if (left >= right)
		return;
	int mid = Partition2(nums, left, right);
	QuickSort(nums, left, mid - 1);
	QuickSort(nums, mid + 1, right);
}

int main()
{
	vector<int> nums = { 6, 3,8,1,7,1,6 };
	QuickSort(nums, 0, nums.size() - 1);
	for (auto e : nums)
	{
		cout << e << " ";
	}
	cout << endl;
	system("pause");
}

结果
在这里插入图片描述

2. 挖坑法实现快排

算法思想

  1. 我们选择数组中最右边的元素作为坑(hole),先将这个值保存为key。
  2. left指针找大于key的数,假设找到x,用x先把坑(hole)填上,此时x位置留下一个坑
  3. right指针找小于key的数,假设找到y,则用y将上一次x的坑填上
  4. 步骤2,3循环往复,最后肯定还有一个坑没有填充,我们要把最开始选定的hole填到坑的位置。
    代码实现
void swap(int *a, int *b)
{
	int temp = *a;
	*a = *b;
	*b = temp;
}
int PartSort(int* a, int left, int right)
{
	int key = a[right];           //选定最后一个值作为坑,保存坑的值
	while (left < right)
	{
		while (left < right && a[left] <= key)  //找到大于key的数
		{
			left++;
		}
		a[right] = a[left];               //此时填充上一次的坑
		while (left < right && a[right] >= key)  // 找到小于key的数
		{
			right--;
		}
		a[left] = a[right];              // 填坑的过程      
	}
	a[right] = key;                      // 将刚开始保存的key填到最后一个坑上
	return left;
}


void QuickSort(int*a, int left, int right)
{
	if (left >= right)
		return;
	int mid = PartSort(a, left, right);
	QuickSort(a, 0, mid - 1);
	QuickSort(a, mid + 1, right);
}
void Print(int *a, int n)
{
	for (int i = 0; i < n ; i++)
	{
		printf("%d ", a[i]);
	}
}
int main()
{
	
	int a[] = { 6,2,1,3,4,5,6};
	int sz = sizeof(a) / sizeof(a[0]);
	QuickSort(a, 0, sz - 1);
	Print(a,sz);
	system("pause");
}

结果
在这里插入图片描述

3. 前后指针法实现快排

算法思想

  1. 刚开始的话 : prev处于cur的前一个位置,也就是cur下标-1的位置
  2. 移动的过程 : prev永远指着比key值大的数的前一个位置 ;cur一直向前遍历找到比key值小的数
  3. cur没有遇到比key大的数,不交换。
  4. cur遇到比key大的数, 将cur对应的数据与prev对应的数据进行交换

整体的过程就是: 推着大的数据往前走,把小的翻到左边

代码实现

void swap(int *a, int *b)
{
	int temp = *a;
	*a = *b;
	*b = temp;
}
int PartSort(int* a, int left, int right)
{
	int key = a[right];
	int prev = left - 1;
	int cur = left;
	while (cur < right)
	{
		if (a[cur] < key && ++prev != cur)
		{
			swap(&a[prev], &a[cur]);
		}
		cur++;
	}
	++prev;
	swap(&a[prev], &a[right]);

	return prev;

}
void QuickSort(int*a, int left, int right)
{
	if (left >= right)
		return;
	int mid = PartSort(a, left, right);
	QuickSort(a, 0, mid - 1);
	QuickSort(a, mid + 1, right);
}
void Print(int *a, int n)
{
	for (int i = 0; i < n ; i++)
	{
		printf("%d ", a[i]);
	}
}
int main()
{
	
	int a[] = { 3, 8, 2, 4 };
	int sz = sizeof(a) / sizeof(a[0]);
	QuickSort(a, 0, sz - 1);
	Print(a,sz);
	system("pause");
}

结果
在这里插入图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
快速排序是一种常用的排序算法,其实现思路有多种方式,以下是其中两种常见的实现思路: 1. 基准元素划分(Lomuto partition scheme): - 选择一个基准元素,一般是待排序数组的最后一个元素。 - 设置两个指针i和j,分别指向待排序数组的起始位置和结束位置。 - 从左到右遍历数组,将小于等于基准元素的元素交换到i的位置,并将i右移一位。 - 最后将基准元素交换到i的位置,这样基准元素左边的元素都小于等于它,右边的元素都大于它。 - 对基准元素左边和右边的两个子数组分别递归进行快速排序。 2. 枢轴元素划分(Hoare partition scheme): - 选择一个基准元素,一般是待排序数组的第一个元素。 - 设置两个指针i和j,分别指向待排序数组的起始位置和结束位置。 - 从左到右找到一个大于基准元素的元素,从右到左找到一个小于基准元素的元素,然后交换它们的位置。 - 重复上述步骤,直到i和j相遇。 - 将基准元素与相遇位置的元素交换位置,这样基准元素左边的元素都小于等于它,右边的元素都大于它。 - 对基准元素左边和右边的两个子数组分别递归进行快速排序。 无论使用哪种实现思路,快速排序的时间复杂度平均为O(nlogn),最坏情况下为O(n^2),空间复杂度为O(logn)。快速排序是一种原地排序算法,不需要额外的辅助空间。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值