十大排序算法--C++实现(下)

6.快速排序

1.方法

       属于分治法,主要思想是选择一个基准,把小于等于基准的放置在左边,把大于等于基准的放置在右边,然后再对左边和右边递归排序,则完成排序。

2.步骤

  • 从数列中挑出一个元素,称为 “基准”(pivot);
  • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
  • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

3.动图演示:

 

4.代码实现

void QuickSort(int arr[], int left,int right) {
	if (left >= right) { 
		return;
	}
	int pivot = arr[left];//选最左侧为中枢
	int i = left, j = right;
	while (i < j) {
        //选数组第一个作为基元,那么应该从数组尾端开始进行比较,
        //否则下面程序行不通,因为从头部开始会丢失基元。
		while (i < j&&arr[j] >= pivot)//从右向左找<中枢的B
			j--;
		while (i < j&&arr[i] <= pivot) //从左向右找>中枢的A
			i++;
		if (i < j) {//如果A在B的左侧,则交换
			int tmp = arr[i];
			arr[i] = arr[j];
			arr[j] = tmp;
		}
	}
	//让中枢和最后一个<=换位
	arr[left] = arr[i];
	arr[i] = pivot;
	//递归处理中枢左右两侧
	QuickSort(arr, left, i - 1);
	QuickSort(arr, i + 1, right);
}

7.堆排序

1. 方法

        主要利用堆的数据结构,大顶堆,即每个节点的值都大于或等于其子节点的值,小顶堆则相反。将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。

      对堆中的结点按层进行编号,将这种逻辑结构映射到数组中 :

       大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]  ;小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]  

构造初始堆

2. 步骤

  • 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
  • 将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
  • 由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
  • 排序(6):堆排序

 

4.代码实现

void adjustMaxHead(int arr[], int i, int n) {
	int maxIndex = i;//以i为父节点调整大顶堆
	if (i * 2 <= n && arr[i * 2] > arr[maxIndex]) {//如果存在左子树 且比当前节点大
		maxIndex = i * 2;
	}
	if (i * 2 + 1 <= n && arr[i * 2 + 1] > arr[maxIndex]) {//如果存在右子树 且比当前节点大
		maxIndex = i * 2 + 1;
	}
	if (maxIndex != i) {//如果当前父节点不是最大值,则父节点和子节点交换
		int tmp = arr[maxIndex];
		arr[maxIndex] = arr[i];
		arr[i] = tmp;
		adjustMaxHead(arr, maxIndex, n);//交换后,递归处理新的子节点,保证新的子节点是大顶堆
	}
}
void HeadSort(int arr[], int n) {
	//从最后一个非叶子节点开始,arr.size()/2-1,往前遍历,构造大顶堆
	for (int i = n / 2 - 1; i >= 0; i--) {
		adjustMaxHead(arr, i, n);
	}
	while (n > 0) {//把大顶堆的顶(最大值)放在数组最后
		int tmp = arr[0];
		arr[0] = arr[n];
		arr[n] = tmp;
		n--;
		adjustMaxHead(arr, 0, n);//对剩下的n-1个元素做同样操作
	}
}

8.计数排序

1. 方法

       为每个元素初始化一个计数器,按出现的次数累加,最后按计数结果保存在数组位置。

2. 步骤

  • 根据待排序集合中最大元素和最小元素的差值范围,申请额外空间;
  • 遍历待排序集合,将每一个元素出现的次数记录到元素值对应的额外空间内;
  • 对额外空间内数据进行计算,得出每一个元素的正确位置;
  • 将待排序集合每一个元素移动到计算得出的正确位置上

3. 图解示例:

 4. 代码实现:

void  CountingSort(int arr[], int n) {
	if (n == 0)  return;
	//1. 找数组的最大最小值
	int max = arr[0], min = arr[0];
	for (int i = 0; i < n; i++) {   
		max = max > arr[i] ? max : arr[i];
		min = min < arr[i] ? min : arr[i];
	}
	//2.为每一个可能元素初始化一个计数器,按出现次数累加
	vector<int> res(max - min + 1, 0);
	for (int i = 0; i < n; i++) {
		res[arr[i] - min]++;
	}
	//3.根据计数器的顺序和元素出现的次数,写回原数组
	int counter = 0;
	for (int j = 0; j < res.size(); j++) {
		for (int k = 0; k < res[j]; k++) {
			arr[counter++] = j + min;
		}
	}
}

9.桶排序

1. 方法

        计数排序的升级版,计数排序相当于每个元素一个桶,但是桶排序是若干个元素一个桶。将元素分桶后,每个桶再自行排序(有可能使用别的排序算法或是以递归方式继续使用桶排序),最后将排序好的结果结合到一起。

2. 步骤

  • 设置一个定量的数组当作空桶;
  • 遍历输入数据,并且把数据一个一个放到对应的桶里去;
  • 对每个不是空的桶进行排序;
  • 从不是空的桶里把排好序的数据拼接起来。

3. 动图演示

4.代码实现

void BucketSort(vector<int>& v, int bucketsize) {
	if (v.size() <= 1)   return;
	int n = v.size();
	// 找出最大值和最小值
	int min = v[0], max = v[0];
	for (int i = 0; i < n; i++) {
		max = max >= v[i] ? max : v[i];
		min = min <= v[i] ? min : v[i];
	}
	//根据桶的尺寸,初始化桶的数量
	int bucketCount = (max - min) / bucketsize + 1;
	vector<vector<int>> bucket(bucketCount, vector<int>());
	for (int i = 0; i < n; i++) {   按照元素的值装入各自的桶中
		int bucketIndex = (v[i] - min) / bucketsize;
		bucket[bucketIndex].push_back(v[i]);
	}
	int index = 0;
	for (int i = 0; i < bucketCount; i++) {
        //此处可以用其他排序算法,这里用递归实现
		if (bucketsize > 1) {//如果桶的尺寸大于1,正民府可以递归再次排序,直到桶的尺寸为1;
			if (bucketCount == 1) {
				bucketsize = 1;//当前只有一个桶了,把桶尺寸收缩为1,  进行最后一次排序
			}
			BucketSort(bucket[i], bucketsize);
		}
		//把已经排好序的桶写回数组
		for (int j = 0; j < bucket[i].size(); j++) {
			v[index++] = bucket[i][j];
		}
	}
}

10.基数排序

1. 方法

       先低位后高位:先以个位数的大小来对数据进行排序,接着以十位数的大小来对数据进行排序,接着以百位数的大小……由于某位数(个位/十位….,不是一整个数)的大小范围为0-9,所以我们需要10个桶,然后把具有相同数值的数放进同一个桶里,之后再把桶里的数按照0号桶到9号桶的顺序取出来,这样一趟下来,按照某位数的排序就完成了

2. 步骤

  • 取得数组中的最大数,并取得位数;
  • arr为原始数组,从最低位开始取每个位组成radix数组;
  • 对radix进行计数排序(利用计数排序适用于小范围数的特点);

3. 动图演示:

4. 代码实现

int maxdigit(int data[], int n)//求数据的最大位数
{
	int d = 1;//保存最大的位数
	int p = 10;
	for (int i = 0; i < n; ++i)
	{
		while (data[i] >= p)
		{
			p *= 10;//100?1000?三位,四位
			++d;
		}
	}
	return d;
}
void Radixsort(int data[], int n)//基数排序
{
	list<int> list[10];
	int digit = maxdigit(data, n);
	int i, factor, j, k;
	for (i = 1, factor = 1; i <= digit; factor *= 10, ++i)
	{
		for (j = 0; j < n; ++j)
		{
			list[(data[j] / factor) % 10].push_back(data[j]);
		}
		for (j = k = 0; j < 10; ++j)
		{
			while (!list[j].empty())//链表非空
			{
				data[k++] = list[j].front();//复制回数组
				list[j].pop_front();//取一个删一个
			}
		}
		//输出每次排序的中间结果
		for (int m = 0; m < n; ++m)
			cout << data[m] << " ";
		cout << endl;
	}
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值