归并排序,快速排序,堆排序(小数之和,TOPK问题。。)

本文详细介绍了三种常见的排序算法:归并排序、快速排序和堆排序。归并排序通过递归和合并操作实现有序;快速排序采用三数取中策略优化了分区过程;堆排序则在构建和调整堆的过程中寻找最大或最小元素。这些算法各有特点,适用于不同的场景,对于理解排序原理和优化算法具有重要意义。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

一、小数和(归并)

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using std::cout;
using std::cin;
bool is_true(int* nums, int* ret, int n)
{
	for (int i = 0; i < n; i++)
	{
		if (nums[i] != ret[i])
		{
			return false;
		}
	}
	return true;
}
void merge(int* nums, int left, int mid, int right)
{
	if (left >= right)
	{
		return;
	}
	int* ret = (int*)malloc(sizeof(int) * (right - left + 1));
	int i = left;
	int j = mid + 1;
	int indx = 0;
	while (i <= mid && j <= right)
	{
		nums[i] < nums[j] ? ret[indx++] = nums[i++] : ret[indx++] = nums[j++];
	}
	while (i <= mid)
	{
		ret[indx++] = nums[i++];
	}
	while (j <= right)
	{
		ret[indx++] = nums[j++];
	}
	memcpy(nums + left, ret, sizeof(int) * (right - left + 1));
	free(ret);
}
//递归版本
void process1(int* nums, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int mid = (left + right) >> 1;
	process1(nums, left, mid);
	process1(nums, mid + 1, right);
	merge(nums, left, mid, right);

}
//非递归版本
void process2(int* nums, int left, int N)
{
	int MergSize = 1;
	//循环不变量:进行merge的左边,右边都是有序的。
	while (MergSize < N)
	{
		int i = 0;
		while (i < N)
		{
			//左组i....mid
			int mid = i + MergSize - 1;
			if (mid >= N)
			{
				break;
			}
			//右组mid+1....right
			int right = fmin(N - 1, mid + MergSize);
			merge(nums, i, mid, right);
			i = right + 1;
		}
		if (MergSize > N / 2)
		{
			break;
		}
		MergSize <<= 1;
	}
}
//题目1.在一个个数组中,一个数左边比它小的数的总和,叫数的小和,所有数的小和累加起来,叫数组小和,求数组小和
//如:
//例子: [1,3,4,2,5]
//1左边比1小的数:没有
//3左边比3小的数: 1
//4左边比4小的数: 1、3
//2左边比2小的数: 1
//5左边比5小的数: 1、 3、4、2
//所以数组的小和为1+1+3+1+1+3+4+2=16
int merge1(int* nums, int left, int mid, int right)
{
	if (left >= right)
	{
		return 0;
	}
	int count = 0;
	int* ret = (int*)malloc(sizeof(int) * (right - left + 1));
	int i = left;
	int j = mid + 1;
	int indx = 0;
	while (i <= mid && j <= right)
	{
		if (nums[i] < nums[j])
		{
			ret[indx++] = nums[i++];
			count =count+ (right - j + 1)*nums[i-1];
		}
		else
		{
			ret[indx++] = nums[j++];
		}
	}
	while (i <= mid)
	{
		ret[indx++] = nums[i++];
	}
	while (j <= right)
	{
		ret[indx++] = nums[j++];
	}
	memcpy(nums + left, ret, sizeof(int) * (right - left + 1));
	free(ret);
	return count;
}

int process3(int* nums, int left, int right)//改动版本
{
	if (left >= right)
	{
		return 0;
	}
	int mid = (left + right) >> 1;
	return process3(nums, left, mid)
		+ process3(nums,mid+1, right)
		+ merge1(nums, left,mid, right);
}
int main()
{
	//int n = 1000;
	int n = 5;
	int nums[5] = { 1,3,4,2,5};
	int a	= process3(nums, 0, 4);
	printf("%d", a);
}

二、快速排序

#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using std::cin;
using std::cout;
using std::endl;
void swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
int GetMid(int* nums, int left, int right)//三数取中
{
	int mid = (left + right) / 2;
	if (nums[left] > nums[right])
	{
		if (nums[mid] <= nums[right])
		{
			return nums[right];
		}
		if (nums[mid] >= nums[left])
		{
			return nums[left];
		}

	}
	else if (nums[left] <= nums[right])
	{
		if (nums[mid] >= nums[right])
		{
			return nums[right];
		}
		if (nums[mid] <= nums[left])
		{
			return nums[left];
		}
	}
	return nums[mid];
}
int process(int* nums, int left, int right)
{
	if (left >= right)
	{
		return left;
	}
	int target = GetMid(nums, left, right);     //快排优化,三数取中,使区间划分更加的均衡
	int i = left - 1;
	int j = right + 1;
	int indx = left;
	while (indx < j)
	{
		if (nums[indx] < target)
		{
			swap(&nums[indx++], &nums[++i]);
		}
		else if (nums[indx] == target)
		{
			indx++;
		}
		else
		{
			swap(&nums[--j], &nums[indx]);
		}
	}
	return i + 1;
}
void QucikSort(int* nums, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int m = process(nums, left, right);
	QucikSort(nums, left, m - 1);//区间[left,m-1]
	QucikSort(nums, m + 1, right);//区间[m+1,right]
}
int main()
{
	int nums[] = { 6,2,1,5,4,-1,3,4,2,12,3,-213,0 ,23,1,32,321 };
	int n = 17;

	QucikSort(nums, 0, n - 1);
	for (int i = 0; i < n; i++)
	{
		printf("%d\n", nums[i]);
	}
}

3.ToK(堆)

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using std::cout;
using std::cin;
using std::endl;
void swap(int& a, int& b)
{
	if (&a == &b)
	{
		return;
	}
	a = a ^ b;
	b = b ^ a;
	a = b ^ a;
}
void HeapDown(int* nums,int indx,int n)//向下调整
{
	int i = indx;//当前节点
	int leftChild = i * 2 + 1;//左孩子
	while (leftChild < n)
	{
		leftChild = leftChild + 1 < n && nums[leftChild] > nums[leftChild + 1] ? leftChild + 1 : leftChild;
		if (nums[leftChild] >= nums[i])
		{
			break;
		}
		swap(nums[leftChild], nums[i]);
		i = leftChild;
		leftChild = i * 2 + 1;
	}

}
void HeapUp(int* nums,int indx,int n)//向上调整
{
	int i = indx;
	while ( nums[i]< nums[(i-1)/ 2])
	{
		swap(nums[i], nums[(i - 1) / 2]);
		i = (i - 1) / 2;
	}

}
//1.题目1
//已知一个几乎有序的数组。
//几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离一定不超过k, 并且k相对于数组长度来说是比较小的。
//请选择- -个合适的排序策略,对这个数组进行排序。
//2.题目2
//TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。
//比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。
//对于Top - K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:
//
//用数据集合中前K个元素来建堆
//前k个最大的元素,则建小堆
//前k个最小的元素,则建大堆
//用剩余的N - K个元素依次与堆顶元素来比较,不满足则替换堆顶元素
//
//将剩余N - K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。
void HeapSort(int* nums, int n);
void test2()
{
	int n = 100000;
	int* nums = new int[n];
	for (int i = 0; i < n; i++)
	{
		nums[i] = rand() % n + 1;
	}
	//求最大前10个元素
	int k = 10;
	int* ret = new int[k];
	for (int i = 0; i < k; i++)
	{
		ret[i] = nums[i];
		//HeapUp(ret, i, k);
	}
	for (int i = 0; i<k; i++)
	{
		HeapUp(ret, i, k);
	}
	//建小堆
	for (int i = k ; i < n; i++)
	{
		if (nums[i] > ret[0])
		{
			ret[0] = nums[i];
			HeapDown(ret, 0, k);
		}
	}
	HeapSort(ret, k);
	for (int i = 0; i < k; i++)
	{
		printf("%d\n", ret[i]);
	}

}
void HeapSort(int* nums, int n)
{
	for (int i = n - 1; i > 0; i--)
	{
		swap(nums[0], nums[i]);
		HeapDown(nums, 0, i);
	}
}
void test1()
{

	int nums[] = { 5,1,3,8,32,23,1,2,231,3,21,31,23,34,3,43,423,4,-1321,22913,213};
	int n = sizeof(nums) / sizeof(int);
	for (int i = n-1; i >=0; i--)
	{
		HeapDown(nums, i,n);
	}//建堆
	for (int i = n-1; i>0; i--)
	{
		swap(nums[0], nums[i]);
		HeapDown(nums, 0, i);
	}
	for (int i = 0; i < n; i++)
	{
		printf("%d\n", nums[i]);
	}

}
//题目1:
int main()
{
	test2();
}

总结

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

1无名之辈1

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

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值