【算法】最常用排序:快速排序优缺点及C++实现

快速排序特点

原理:通过一躺排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一不部分的所有数据都要小,然后再按次方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

优点:平均性能好,O(nlog2n),2为下标

缺点:不稳定,初始序列有序或基本有序时,时间复杂度降为O(n^2)。

递归版本的实现

方法一:填坑法

1.利用分化函数求第一个基准元素
2.递归调用快排
主要是分化函数partition的实现
分化函数步骤:
1.第一个位置作为基准,位置为初始坑
2.先从右边元素,找到小于基准的元素,填入坑中,更新坑位置,找到后转3
3.从左边找到大于基准的元素,填入坑中,更新坑位置;2,3循环直到left = right
4.最后的坑填入基准元素。

方法二:指针交换法

与填坑法相比,只有分化函数不同,分化函数中的元素交换次数少,更简单
交换思想:左边大于基准的元素和右边小于基准的元素交换
分化函数步骤:
1.交换
2.当left和right指针重合之时,交换pivot元素和left与right重合点的元素

!!!一定要从基准的对面开始比较

代码:

填坑法
 

#include <iostream>
using namespace std;

template<typename T> 
int length(T& arr)
{
	return sizeof(arr) / sizeof(arr[0]);
}
void quickSort(int arr[], int startIndex, int endIndex);
int partition(int arr[], int startIndex, int endIndex);
int main()
{
	int arr[12] = { 4,7,6,5,3,2,8,1,10,0,10,7 };
	quickSort(arr, 0, length(arr) - 1);
	for (int i = 0; i < length(arr);  i++)
		cout << arr[i] << " ";
	cout << endl;
	system("pause");
	return 0;
}

void quickSort(int arr[], int startIndex, int endIndex)
{
	if (startIndex < endIndex)
	{
		int pivot = partition(arr, startIndex, endIndex);
		quickSort(arr, startIndex, pivot);
		quickSort(arr, pivot + 1, endIndex);
	}

}
//填坑法
int partition(int arr[], int startIndex, int endIndex)
{
	int pivot = arr[startIndex];//第一个位置作为基准
	int left = startIndex;
	int right = endIndex;
	//坑的位置
	int index = startIndex;
	while (left < right)//大循环
	{
		while (right > left)//先从右向左
		{
			if (arr[right] < pivot)//小于基准,交换
			{
				arr[left] = arr[right];//填坑
				index = right;//更新坑位置
				left++;
				break;//跳出右边的小循环
			}
			right--;
		}
		while (left < right)//左到右
		{
			if (arr[left] > pivot)
			{
				arr[right] = arr[left];
				index = left;
				right--;
				break;
			}
			left++;
		}
	}
        arr[index] = pivot;
	return index;
}

指针交换法
 

//指针交换法:左边大于基准的元素和右边小于基准的元素交换
int  partition(int array[], int start_index, int end_index)
{
	int pivot = array[start_index];//基准
	int left_index = start_index;
	int right_index = end_index;
	while (left_index != right_index)
	{
		//一定要从基准对面的元素开始移动
        //一定要是array[right_index] >= pivot,否则会陷入死循环
		while (array[right_index] >= pivot && left_index < right_index)
		{
			right_index--;
		}
		while (array[left_index] <= pivot && left_index < right_index)
		{
			left_index++;
		}
		int tmp = array[left_index];
		array[left_index] = array[right_index];
		array[right_index] = tmp;
	}
	//当left_index和right_index指针重合之时,交换pivot元素和left_index与right_index重合点的元素
	int tmp = array[start_index];
	array[start_index] = array[left_index];
	array[left_index] = tmp;
	return left_index;
}

非递归实现

和刚才的递归实现相比,代码的变动仅仅在quickSort方法当中,partition函数可以用填坑法或者指针交换法。该方法中引入了一个存储Map类型元素的栈,用于存储每一次交换时的起始下标和结束下标。
用栈的方式,代码中一层一层的方法调用,本身就是一个函数栈。
每次进入一个新方法,就相当于入栈;每次有方法返回,就相当于出栈。
所以,我们可以把原本的递归实现转化成一个栈的实现,在栈当中存储每一次方法调用的参数:

 

 

//非递归实现
//用栈的方式,代码中一层一层的方法调用,本身就是一个函数栈。
//每次进入一个新方法,就相当于入栈;每次有方法返回,就相当于出栈。
void quickSort(int arr[], int startIndex, int endIndex)
{
	// 用一个集合栈来代替递归的函数栈
	stack<map<string, int>> quickSortStack;
	//数组的起止下标,以哈希的形式入栈
	map<string, int> rootParam;
	rootParam.insert(pair<string, int>("startIndex", startIndex));//利用insert插入pair类型的数据
	rootParam.insert(pair<string, int>("endIndex", endIndex));
	quickSortStack.push(rootParam);
	//循环结束条件:栈为空时结束
	while (!quickSortStack.empty())
	{
		map<string, int> param = quickSortStack.top();//获取栈顶:入栈是先左后右,所以出栈先右后左
		quickSortStack.pop();//栈顶出栈
		//int pivotIndex = partition(arr, param["startIndex"], param["endIndex"]);//利用map[key]获取键对应的值,不好,如果查找不到会插入
		int pivotIndex = partition(arr, param.find("startIndex")->second, param.find("endIndex")->second);//利用find返回的迭代器
		//根据基准元素分为两部分,把每一部分起止下标入栈
		if (param.find("startIndex")->second < pivotIndex - 1)
		{
			map<string, int> leftParam;
			leftParam.insert(pair<string, int>("startIndex", param.find("startIndex")->second));
			leftParam.insert(pair<string, int>("endIndex", pivotIndex - 1));
			quickSortStack.push(leftParam);
		}
		if (pivotIndex + 1 < param.find("endIndex")->second)
		{
			map<string, int> rightParam;
			rightParam.insert(pair<string, int>("startIndex", pivotIndex + 1));
			rightParam.insert(pair<string, int>("endIndex", param.find("endIndex")->second));
			quickSortStack.push(rightParam);
		}

	}
}

 

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++中有许多不同的排序算法,包括冒泡排序、选择排序、插入排序快速排序、希尔排序、归并排序等等。以下是其中一些算法实现示例: 1. 冒泡排序 ``` void bubbleSort(int arr[], int n) { int i, j; for (i = 0; i < n - 1; i++) for (j = 0; j < n - i - 1; j++) if (arr[j] > arr[j+1]) swap(arr[j], arr[j+1]); } ``` 2. 选择排序 ``` void selectionSort(int arr[], int n) { int i, j, min_idx; for (i = 0; i < n - 1; i++) { min_idx = i; for (j = i+1; j < n; j++) if (arr[j] < arr[min_idx]) min_idx = j; swap(arr[min_idx], arr[i]); } } ``` 3. 插入排序 ``` void insertionSort(int arr[], int n) { int i, key, j; for (i = 1; i < n; i++) { key = arr[i]; j = i - 1; while (j >= 0 && arr[j] > key) { arr[j+1] = arr[j]; j--; } arr[j+1] = key; } } ``` 4. 快速排序 ``` int partition(int arr[], int low, int high) { int pivot = arr[high]; int i = low - 1; for (int j = low; j <= high - 1; 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 pi = partition(arr, low, high); quickSort(arr, low, pi - 1); quickSort(arr, pi + 1, high); } } ``` 5. 归并排序 ``` void merge(int arr[], int l, int m, int r) { int n1 = m - l + 1; int n2 = r - m; int L[n1], R[n2]; for (int i = 0; i < n1; i++) L[i] = arr[l + i]; for (int j = 0; j < n2; j++) R[j] = arr[m + 1 + j]; int i = 0, j = 0, k = l; while (i < n1 && j < n2) { if (L[i] <= R[j]) { arr[k] = L[i]; i++; } else { arr[k] = R[j]; j++; } k++; } while (i < n1) { arr[k] = L[i]; i++; k++; } while (j < n2) { arr[k] = R[j]; j++; k++; } } void mergeSort(int arr[], int l, int r) { if (l < r) { int m = l + (r - l) / 2; mergeSort(arr, l, m); mergeSort(arr, m + 1, r); merge(arr, l, m, r); } } ``` 这些排序算法在实际应用中都有不同的优缺点,需要根据实际情况选择合适的算法

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值