快速排序、单向链表的快速排序(C++)

12 篇文章 0 订阅

一、快速排序

取数组的第一个值作为基准值key,将数组中小于key的值排在key的左边,将数组中大于key的值排在key的右边,然后递归处理以key为分割的两个序列

(1)挖洞法。

a、挖洞法就是将数组Array第一个值作为基准值key保存(索引left和right分别指向序列的头和尾元素),索引right从右至左移动找到第一个小于key值的元素或者left等于right(left==right)时,将 Array[left] 值填入 Array[left] 的位置。

value6(key)2497103
index0(left)123456(right)
value32497103
index0(left)123456(right)

 b、然后再从left开始,left从左向右移动找到第一个大于key值的元素或者left等于right(left==right),将该元素值赋值给 Array[right];

value32497109
index0123(left)456(right)

c、重复循环执行步骤a和b,直至left等于right(left==right)时结束循环,并将key赋值给 Array[left];

value3249->67109
index0123(left)(right)456

d、以left作为分割点,对left前后的两个序列递归执行上面步骤即完成排序。

void quicksort1(vector<int>& v, int left, int right) {
	if (left >= right)
		return;
	int rhs = right;
	int lhs = left;
	int key = v[left];
	while (lhs < rhs) {
		while (lhs < rhs && v[rhs] >= key) rhs--;
		v[lhs] = v[rhs];
		while (lhs < rhs && v[lhs] <= key) lhs++;
		v[rhs] = v[lhs];
	}
	v[lhs] = key;
	quicksort1(v, left, lhs - 1);
	quicksort1(v, lhs + 1, right);
}

(2)左右交换

将数组Array第一个值作为基准值key保存,索引 left 和 right 分别指向序列的头和尾元素;

a、从right开始,right从右向左移动查找到第一个小于key的元素或者left等于right(left==right);再从left开始,left从左向右移动查找到第一个大于key值的元素或者left等于right(left==right);

value6(key)2497103
index0(left)123456(right)
value6(key)2497103
index0123(left)456(right)

 b、如果left<right,就将这两个索引对应的元素交换;

value6(key)2437109
index0123(left)456(right)

c、当left小于right时,重复循环执行上面两个步骤,直至left等于right时结束循环。

value6(key)2437109
index0123(left)(right)456

d、将序列首元素与Array[left]交换,以left为分割点分成两个序列,对这两个序列进行递归即可完成排序;

value3246(key)7109
index0123(left)(right)456
void quicksort2(vector<int>& v, int left, int right){
	if (left >= right)
		return;
	int rhs = right;
	int lhs = left;
	int key = v[left];
	while (lhs < rhs) {
//将首元素作为基准值时,必须先从右向左开始查找小于key的元素,再从左向右开始查找大于key的元素,
//保证循环结束后,left与right相等,并且left所指元素值必定小于等于key值;
//将尾元素作为基准值时,情况相反;
		while (lhs < rhs && v[rhs] >= key) rhs--;
		while (lhs < rhs && v[lhs] <= key) lhs++;
		if (lhs < rhs)
			swap(v[lhs], v[rhs]);
	}
	swap(v[left], v[rhs]);
	quicksort2(v, left, lhs-1);
	quicksort2(v, lhs+1, right);
}

 二、单向链表的快速排序

单向链表不能像array、vector那样支持operator--的操作,所以不能从尾部向头部方向遍历元素。

a、首元素作为基准值key,使用两个Node指针slow和fast,slow初始时指向begin,fast初始时指向begin的下一个元素;

value6(key)2497103
index0(slow)1(fast)23456

b、当fast没有到达链表末尾时,若fast指向的值大于等于key,更新fast指向fast的下一个元素;若小于key,则将更新slow指向slow的下一个元素,并将slow与fast指向的值交换,更新fast指向fast的下一个元素。(slow始终指向小于等于key的节点,slow的下一个元素始终为大于key的节点或者为fast指向的节点)(实际上就是不断地将fast遍历到的小于key的节点交换到与slow节点的下一个节点进行交换,并移动slow

value6(key)2497103
index01(slow)2(fast)3456
value6(key)2497103
index012(slow)3(fast)456
value6(key)2497103
index012(slow)34(fast)56
value6(key)2497103
index012(slow)3456(fast)
value6(key)2497103
index0123(slow)45

6(fast)

value6(key)2437109
index0123(slow)45

6(fast)

value32467109
index0123(slow)45

6(fast)

c、循环执行b直至fast到达链表尾部;交换链表头元素与slow指向元素值。

d、以slow为分割点将链表分为两部分,两个序列递归调用上述步骤排序完成。

struct Node {
	int value;
	Node* next;
	Node(int val) {
		value = val;
		next = nullptr;
	}
};

Node* partition(Node* begin, Node* end) {
	Node* slow = begin;
	Node* fast = begin->next;
	int val = begin->value;
	while (fast != end) {
		if (fast->value < val) {
			slow = slow->next;
			swap(slow->value, fast->value);
		}
		fast = fast->next;
	}
	swap(begin->value, slow->value);
	return slow;
}

void quicksort(Node* begin, Node* end) {
	if (begin != end) {
		Node* temp = partition(begin, end);
		quicksort(begin, temp);
		quicksort(temp->next, end);
	}
}

三、快排复杂度

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。因此,对于一个N个数的随机排列,他的逆序数为O(N^2),对于冒泡排序、插入排序等排序方法,只是在相邻的两个元素为逆序的时候,交换这两个元素,这样仅仅消除了一个逆序。而快速排序的话,可以将两个间隔开来的两个逆序的元素进行交换,这样消除的逆序至少为一个,因为中间的元素若全小于或者全大于这两个元素,这是仅消除一个逆序,但若中间的元素有处于这两个元素大小之间,则此时一次交换消除的逆序就大于1。(详细可看:https://blog.csdn.net/zhaxianyelinxi/article/details/78976230

例:当nums[0]  > nums[2]

对于nums = {3,1,2},将nums[0]与nums[2]交换,逆序数: 2——>1(由2变为1);
对于nums = {2,3,1},将nums[0]与nums[2]交换,逆序数: 2——>1(由2变为1);
对于nums = {3,2,1},将nums[0]与nums[2]交换,逆序数: 3——>0(由3变为0)(2小于3、大于1);

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论 3

打赏作者

boom!!!

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值