55-八大排序之——④快速排序

本文详细介绍了快速排序算法的三种实现方式:随机化法、三位取中法和非递归形式,包括递归算法原理、时间复杂度分析、空间复杂度以及各种划分策略。通过实例演示了如何应用这些方法进行快速排序,适合理解排序算法优化和非递归编程技巧。

快速排序

递归算法:
①随机化法
②三位取中法
③单向划分法
④带头节点的单链表快排

1.算法:定义一个基准(第一个数据arr[low]),从后往前找比基准小的数字,找到往前挪;从前往后找比基准大的数字,找到往后挪,重复上述操作,直到low==high,然后arr[low]=tmp; returnlow;

3.时间复杂度最好情况为O(nlogn),完全有序为O(n^2),空间复杂度O(logn)(因为要递归logn次),稳定性不好(有跨越式交换)

快速排序的一次划分(Partition)的时间复杂度为O(n),空间复杂度为O(1)

快速排序(Quick)的时间复杂为O(logn),空间复杂度为O(logn),需要递归logn次

4.快速排序最大的缺点:越有序越慢,完全有序为O(n^2)

5.快速排序的一次划分(Partition) Quick(int *arr,int low,int high)产生的原因就是因为QuickSort(int *arr,int len)的参数少,通过Quick进行中间转换

6.快速排序是对冒泡(起泡)排序的一种改进,他的思想是通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字(左边)均比另一部分记录的关键字(右边)小,则可分别对这两部分记录继续进行排序,以达到整个序列有序

7.内部排序:待排序记录存放在计算机随机存储器中进行的排序过程

外部排序:待排序记录的数量很大,以致内存一次不能容纳全部记录,在排序过程中需要对外存进行访问的排序过程

//快速排序的一次划分,时间复杂度O(n),空间复杂度O(1)
//一次需要遍历所有数据,所以时间复杂度就是O(n)
int Partition(int *arr,int low,int high)
{
	int tmp = arr[low];//基准
	while(low<high)
	{
		//从后往前找比基准小的数字,往前移动
		while(low < high && arr[high]>tmp)//比基准大,往前走
		{
			high--;
		}
		if(low<high)
		{
			arr[low] =arr[high];
		}
		//从前往后找比基准大的数字,往后移动
		while(low<high && arr[low]<=tmp)
		{
			low++;
		}
		if(low<high)
		{
			arr[high] = arr[low];
		}
	}
	arr[low] = tmp;
	return low;
}

//时间复杂度O(logn),空间复杂度O(logn)
void Quick(int *arr,int low,int high)
{
	int par = Partition(arr,low,high);
	if(par-1>low)//左边数据超过一个
	{
		Quick(arr,low,par-1);
	}
	if(par+1<high)//右边数据超过一个
	{
		Quick(arr,par+1,high);
	}
}

//时间复杂度O(nlogn),空间复杂度O(logn),稳定性不好(有跳跃式的交换)
void QuickSort(int *arr,int len)
{
	Quick(arr,0,len-1);
}

快速排序测试

int main()
{
	int arr[] = {6,5,4,3,2,1};
	QuickSort(arr,sizeof(arr)/sizeof(arr[0]));
	Show(arr,sizeof(arr)/sizeof(arr[0]));
	
	return 0;
}

递归写法

1.随机化法

算法描述:随机产生一个下标(pos),和下标为left的数据进行交换,以left的值进行划分

void Show(int* arr, int len)
{
	for (int i = 0; i < len; ++i)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}
int Partition(int* arr, int low, int high)
{
	int tmp = arr[low];
	while (low < high)
	{
		while (low<high && arr[high]>tmp)
		{
			high--;
		}
		if (low < high)
		{
			arr[low] = arr[high];
		}
		while (low < high && arr[low] <= tmp)
		{
			low++;
		}
		if (low < high)
		{
			arr[high] = arr[low];
		}
	}
	arr[low] = tmp;
	return low;
}
int RandPartition(int* arr, int low, int high)
{
	srand(time(NULL));
	int pos = rand() % (high-low + 1) + low;
	std::swap(arr[pos], arr[low]);
	return Partition(arr, low, high);
}
void PassQuick(int* arr, int low, int high)
{
	if (low < high)
	{
		int pos = RandPartition(arr, low, high);
		PassQuick(arr, low, pos - 1);
		PassQuick(arr, pos + 1, high);
	}
}
void QuickSort(int* arr, int len)
{
	if (arr == NULL || len < 2)
	{
		return;
	}
	PassQuick(arr, 0, len - 1);
}


int main()
{
	int arr[] = {1,5,9,6,7,3,8,2,4};
	QuickSort(arr, sizeof(arr) / sizeof(arr[0]));
	Show(arr, sizeof(arr) / sizeof(arr[0]));
}

2.三位取中法

算法:先算得中间位置的值,然后比较下标为left的值,中间位置的值,下标为right的值,用三个数的中间值和下标为letf的值进行交换,就是三位取中法

void Show(int* arr, int len)
{
	for (int i = 0; i < len; ++i)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}
int Partition(int* arr, int low, int high)
{
	int tmp = arr[low];
	while (low < high)
	{
		while (low<high && arr[high]>tmp)
		{
			high--;
		}
		if (low < high)
		{
			arr[low] = arr[high];
		}
		while (low < high && arr[low] <= tmp)
		{
			low++;
		}
		if (low < high)
		{
			arr[high] = arr[low];
		}
	}
	arr[low] = tmp;
	return low;
}
//三位取中法
int MidParitition(int* br, int left, int right)
{
	int mid = (right - left) / 2 + left;
	struct IndexNode
	{
		int key;
		int index;

		operator int() const { return key; }
	};
	struct IndexNode KL = { br[left],left };
	struct IndexNode KM = { br[mid],mid };
	struct IndexNode KR = { br[right],right };

	std::priority_queue<IndexNode> hp;
	hp.push(KL);
	hp.push(KM);
	hp.push(KR);
	hp.pop();//删除第一个
	struct IndexNode pos = hp.top();//取中间的值

	std::swap(br[KL.index], br[pos.index]);//中间的值和left进行交换
	return Partition(br, left, right);
}
void PassQuick(int* br, int left, int right)
{
	if (left < right)
	{
		int pos = MidParitition(br, left, right);
		PassQuick(br, left, pos - 1);
		PassQuick(br, pos + 1, right);
	}
}
void QuickSort(int* br, int n)
{
	if (br == NULL || n < 2) return;
	PassQuick(br, 0, n - 1);
}
int main()
{
	int arr[] = {1,5,9,6,7,3,8,2,4};
	QuickSort(arr, sizeof(arr) / sizeof(arr[0]));
	Show(arr, sizeof(arr) / sizeof(arr[0]));
}

3.单向划分法(从左到右)

void Show(int* arr, int len)
{
	for (int i = 0; i < len; ++i)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}
//从左向右一次划分
int LeftPartition(int* arr, int left, int right)
{
	int i = left;
	int j = left - 1;
	int tmp = arr[i];
	while (i <= right)
	{
		if (arr[i] <= tmp)
		{
			j = j + 1;
			swap(arr[j], arr[i]);
		}
		++i;
	}
	swap(arr[left], arr[j]);
	return j;
}
void PassQuick(int* arr, int left, int right)
{
	if (left < right)
	{
		int pos = LeftPartition(arr, left, right);
		PassQuick(arr, left, pos - 1);
		PassQuick(arr, pos + 1, right);
	}
}
void QuickSort(int* arr, int len)
{
	if (arr == NULL || len < 2) return;
	PassQuick(arr, 0, len - 1);
}
int main()
{
	int arr[] = { 56,12,78,9,34,23,100,56,45,67,89 };
	int n = sizeof(arr) / sizeof(arr[0]);

	QuickSort(arr, n);
	Show(arr, n);

	return 0;
}

4.如何将上面的快排写成非递归的形式?

void Show(int* arr, int len)
{
	for (int i = 0; i < len; ++i)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}
int Partition(int* arr, int low, int high)
{
	int tmp = arr[low];
	while (low < high)
	{
		while (low<high && arr[high]>tmp)
		{
			high--;
		}
		if (low < high)
		{
			arr[low] = arr[high];
		}
		while (low < high && arr[low] <= tmp)
		{
			low++;
		}
		if (low < high)
		{
			arr[high] = arr[low];
		}
	}
	arr[low] = tmp;
	return low;
}
void QuickSort(int* br, int len)
{
	if (br == NULL || len < 2) return;
	queue<int> qu;
	qu.push(0);
	qu.push(len - 1);
	while (!qu.empty())
	{
		int left = qu.front(); qu.pop();
		int right = qu.front(); qu.pop();
		int pos = Partition(br, left, right);
		if (left < pos - 1)
		{
			qu.push(left);
			qu.push(pos - 1);
		}
		if (right > pos + 1)
		{
			qu.push(pos + 1);
			qu.push(right);
		}
	}
}
int main()
{
	int arr[] = { 56,12,78,9,34,23,100,56,45,67,89 };
	int n = sizeof(arr) / sizeof(arr[0]);

	QuickSort(arr, n);
	Show(arr, n);

	return 0;
}

或者用“对”

void Show(int* arr, int len)
{
	for (int i = 0; i < len; ++i)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}
int Partition(int* arr, int low, int high)
{
	int tmp = arr[low];
	while (low < high)
	{
		while (low<high && arr[high]>tmp)
		{
			high--;
		}
		if (low < high)
		{
			arr[low] = arr[high];
		}
		while (low < high && arr[low] <= tmp)
		{
			low++;
		}
		if (low < high)
		{
			arr[high] = arr[low];
		}
	}
	arr[low] = tmp;
	return low;
}
void QuickSort(int* br, int n)
{
	if (br == NULL || n < 2) return;
	typedef std::pair<int, int> Pair;
	queue<Pair> qu;
	qu.push(Pair(0, n - 1));
	while (!qu.empty())
	{
		Pair pos = qu.front(); qu.pop();
		int mid = Partition(br, pos.first, pos.second);
		if (pos.first < mid - 1)
		{
			qu.push(Pair(pos.first, mid - 1));
		}
		if (mid + 1 < pos.second)
		{
			qu.push(Pair(mid + 1, pos.second));
		}
	}
}
int main()
{
	int arr[] = { 56,12,78,9,34,23,100,56,45,67,89 };
	int n = sizeof(arr) / sizeof(arr[0]);

	QuickSort(arr, n);
	Show(arr, n);

	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值