c++实现十种排序算法

基础排序

冒泡排序

选择排序

插入排序

希尔排序

分治法

快速排序(关键在如何划分)

归并排序(关键在合并)

堆排序

桶排序

计数排序

基数排序


基础排序

冒泡排序

每一轮都是从头开始,大的去后面,时间复杂度O(N^2)

//冒泡排序:每一轮都从头开始,两两比较,大的去后面,时间复杂度O(N^2)
void bubbleSort0(int *arr, int len)
{
	if (arr == nullptr || len < 2)
		return;
	for (int i = 0; i < len; ++i)
	{
		for (int j = 0; j < len - i - 1; ++j)
		{
			if (arr[j] > arr[j + 1])
			{
				swap(arr[j], arr[j + 1]);
			}
		}
	}
	return;
}
//冒泡优化1:若某轮排序未发生交换,则已经有序,可直接终止
void bubbleSort1(int *arr, int len)
{
	if (arr == nullptr || len < 2)
		return;
	bool flag;
	for (int i = 0; i < len; ++i)
	{
		flag = false;//每轮排序开始时,将标志位置0
		for (int j = 0; j < len - i - 1; ++j)
		{
			if (arr[j] > arr[j + 1])
			{
				swap(arr[j], arr[j + 1]);
				flag = true;//只要有交换,就把标志位置1
			}
		}
		if (flag==0)
			return;
	}
	return;
}
//冒泡优化2:记录最后一次交换lastExchange的发生位置,则只有arr[0]—arr[lastExchange-1]是无序的
void bubbleSort2(int *arr, int len)
{
	if (arr == nullptr || len < 2)
		return;
	bool flag;
	int lastExchange = len - 1, pos = 0;
	for (int i = 0; i < len - 1; ++i)
	{
		flag = 0;
		for (int j = 0; j < lastExchange; ++j)
		{
			if (arr[j] > arr[j + 1])
			{
				swap(arr[j], arr[j + 1]);
				flag = true;
				pos = j;//需要使用一个中间变量,否则内层for循环的终止条件会变化
			}
		}
		lastExchange = pos;
		if (flag == 0)
			return;
	}
	return;
}

选择排序

每一轮找到最小的元素放在当前序列的最前面,时间复杂度O(N^2)

//选择排序:每一轮都找到最小的元素放在当前序列的最前面,时间复杂度O(N^2)
void selectSort0(int *arr, int len)
{
	if (arr == nullptr || len < 2)
		return;
	int index;
	for (int i = 0; i < len; ++i)
	{
		index = i;
		for (int j = i + 1; j < len; ++j)
		{
			if (arr[j] < arr[index])
			{
				index = j;
			}
		}
		if (index != i)//index不是最初的i,即其余数字中有更小者
		{
			int tmp = arr[i];
			arr[i] = arr[index];
			arr[index] = tmp;
		}
	}
	return;
}
//选择排序优化1:同时一轮遍历同时找到最大值和最小值
void selectSort1(int *arr, int len)
{
	if (arr == nullptr || len < 2)
		return;
	int left = 0, right = len - 1;
	int min, max;
	while (left < right)
	{
		min = left, max = right;
		for (int i = left; i <= right; ++i)
		{
			if (arr[i] < arr[min])
				min = i;
			if (arr[i] > arr[max])
				max = i;
		}
		if (max != right)
			swap(arr[max], arr[right]);
		//若最小值位置min=本轮最右侧位置right,上面一步最大值交换后max=right,所以应把max赋给min;
		//即便上面一步没有执行,即max=right,那么将max赋给min也是等价的
		if (min == right)
			min = max;
		if (min != left)
			swap(arr[min], arr[left]);
		left++;
		right--;
	}
	return;
}

插入排序

在一个数组中,将第一个元素看作有序元素序列里的唯一元素,然后依次插入后面的元素,时间复杂度O(N^2)

//插入排序:将一个记录插入到已经排好序的有序表中
void insertSort0(int *arr, int len)
{
	if (arr == nullptr || len < 2)
		return;
	for (int i = 1; i < len; ++i)
	{
		int j = i-1;
		int tar = arr[i];
		while (j > -1 && arr[j] > tar)//找插入位置的同时,大的元素往后挪
		{
			arr[j + 1] = arr[j];
			j--;
		}
		arr[j + 1] = tar;
	}
	return;
}
//插入排序优化1:使用二分查找在已有序部分找插入位置
void insertSort1(int *arr, int len)
{
	if (arr == nullptr || len < 2)
		return;
	int left, right;
	for (int i = 1; i < len; ++i)
	{
		int tar = arr[i];
		int insertIndex;
		left = 0, right = i - 1;
		while (left <= right)//二分查找插入位置insertIndex
		{
			insertIndex = left + (right - left) / 2;
			if (arr[insertIndex] > tar)
			{
				right = insertIndex - 1;
			}
			else
			{
				left = insertIndex + 1;
			}
		}
		//跳出while的状态是right在left左侧一个位置,
		//而下面for循环将从left到i-1挪到了left+1到i,所以空出的left就是要插入的位置
		for (int j = i-1; j >= left; --j)
			arr[j + 1] = arr[j];
		arr[left] = tar;
	}
	return;
}

希尔排序

缩小增量排序,一趟一增量interval,用增量来分组,组内执行插入排序,时间复杂度O(Nlog^{2}N)

//希尔排序:缩小增量排序,一趟一增量interval,用增量来分组,组内执行插入排序
void shellSort0(int *arr, int len)
{
	if (arr == NULL || len < 2)
		return;
	for (int interval = len / 2; interval > 0; interval /= 2)
	{
		for (int i = interval; i < len; ++i)
		{
			int tar = arr[i];
			int j = i - interval;//当前分组中的前一个元素
			while (j > -1 && arr[j] > tar)
			{
				arr[j + interval] = arr[j];
				j -= interval;
			}
			arr[j + interval] = tar;
		}
	}
	return;
}

分治法

1.拆分为子问题

2.递归求解子问题

3.合并子问题的解

注:partition和堆都能解决顺序统计量问题,堆更适合海量数据流

快速排序(关键在如何划分)

1.选主元,左侧都比主元小,右侧都比主元大(要保证最后一步与主元交换的值比主元小)

2.递归地对左右两个子序列排序

3.合并(快排是原址排序,不需合并)

//快速排序:
//双指针单向扫描
int quickPartition0(int *arr, int left, int right)
{
	int privot = arr[left];
	int pScan = left + 1, pRight = right;
	while (pScan<=pRight)
	{
		if (arr[pScan] > privot)//当前扫描值<主元,扫描值与右侧指针值交换,右侧指针左移
		{
			swap(arr[pRight], arr[pScan]);
			pRight--;
		}
		else
		{
			pScan++;
		}
	}
	swap(arr[left], arr[pRight]);//需要保证与privot交换的值比privot小,所以是pRight
	return pRight;
}
//双指针双向扫描
int quickPartition1(int *arr, int left, int right)
{
	//三点中值法优化,在left,mid,right中选择一个中值作为主元,尽可能避免“一边倒”的情况
	/*int mid = left + (right - left) / 2;
	if (arr[left] >= arr[right] && arr[left] <= arr[mid])
		mid = left;
	else if (arr[right] >= arr[left] && arr[right] <= arr[mid])
		mid = right;
	else
		mid = mid;
	swap(arr[left], arr[mid]);*/
	int privot = arr[left];
	int pScan = left + 1, pRight = right;
	while (pScan <= pRight)
	{
		//下面两个while中与主元的比较需要有一个带=
		//否则若遇到有几个值相等的情况,会死循环(下面两个while都进不去,两个指针大小无变化)
		while (pScan <= pRight && arr[pScan] <= privot)//从左往右扫描到第一个比主元大的数
		{
			pScan++;
		}
		while (pScan <= pRight && arr[pRight] > privot)//从右往左扫描到第一个比主元小的数
		{
			pRight--;
		}
		if (pScan < pRight)//如果大的在左,小的在右,就交换
		{
			swap(arr[pScan], arr[pRight]);
		}
	}
	swap(arr[left], arr[pRight]);
	return pRight;
}
void quickSort0(int *arr, int left,int right)
{
	if (arr == nullptr)
		return;
	if (left < right)
	{
		//int mainPos = quickPartition0(arr, left, right);
		int mainPos = quickPartition1(arr, left, right);
		quickSort0(arr, left, mainPos - 1);		//左半子序列做快排
		quickSort0(arr, mainPos + 1, right);	//右半子序列做快排
	}
	return;
}
//快排三指针分区扫描(了解即可)
void quickSort_3p(int arr[], int begin, int end)
{
	int privot = arr[begin];
	int scan = begin + 1;
	int equal = begin + 1;
	int big = end;
	if (scan > big)
		return;
	while (scan <= big)
	{
		if (arr[scan] < privot)
		{
			swap(arr, scan, equal);
			equal++;
			scan++;
		}
		else if (arr[scan] > privot)
		{
			swap(arr, big, scan);
			big--;
		}
		else
		{
			scan++;
		}
	}
	swap(arr, begin, equal - 1);

	partition_3p(arr, begin, equal - 2);
	partition_3p(arr, big + 1, end);

}

归并排序(关键在合并)

1.将n个元素分成各n/2的两个子序列

2.递归地对两个左右子序列排序

3.合并两个子序列(辅助空间拷贝到原序列)

//归并排序(二叉树后序遍历)
int mergeHelper[10];
void merge0(int *arr, int left, int mid, int right)
{
	for (int i = left; i <= right; ++i)
		mergeHelper[i] = arr[i];
	int leftBegin = left, rightBegin = mid + 1;
	int index = left;
	while (leftBegin <= mid && rightBegin <= right)
	{
		if (mergeHelper[leftBegin] <= mergeHelper[rightBegin])//左半子序列中当前值<=右半子序列当前值
		{
			arr[index++] = mergeHelper[leftBegin++];
		}
		else//左半子序列中当前值>右半子序列当前值
		{
			arr[index++] = mergeHelper[rightBegin++];
		}
	}
	while (leftBegin <= mid)//若左半子序列没过完,继续拷贝
	{
		arr[index++] = mergeHelper[leftBegin++];
	}
	//若右半子序列没过完,不用理会,因为mergeHelper是从arr拷贝过来的,右半子序列相同
	return;
}
void mergeSort0(int *arr, int left, int right)
{
	if (arr == nullptr)
		return;
	if (left < right)
	{
		int mid = left + (right - left) / 2;
		mergeSort0(arr, left, mid);			//左半边子序列归并排序
		mergeSort0(arr, mid + 1, right);	//右半边子序列归并排序
		merge0(arr, left, mid, right);		//合并
	}
	return;
}

堆排序

1.堆化,反向调整使每个子树都是大顶堆或者小顶堆

2.把堆顶0号元素与最后一个元素对调;缩小堆的范围,对堆顶元素进行向下调整

//堆排序
	//堆化,反向调整使每个子树都是大顶堆或者小顶堆
void minHeap(int arr[],int len)
{
	//从最后一个非叶子节点开始“下沉”
	for (int i = len / 2 - 1; i >= 0; i--)
	{
		minHeapFixDown(arr, i, len);
	}
	return;
}
void minHeapFixDown(int arr[], int i, int len)
{
	//找到左右孩子
	int min,temp;
	int left = 2 * i + 1;
	int right = 2 * i + 2;
	//找到两者中较小的下标
	if (left >= len)
	{
		return;
	}
	if (right >= len)
	{
		min = left;
	}
	else
	{
		if (arr[left] > arr[right])
			min = right;
		else
			min = left;
	}
	//如果arr[i]比两个孩子都小,不调整;否则与两个孩子中较小的做交换
	if (arr[i] <=arr[min])
	{
		return;
	}
	else
	{
		swap(arr, i, min);
	}
	//较小的孩子位置的值发生变化,i变更为较小孩子的位置,递归调整
	minHeapFixDown(arr, min, len);
	
}
	//堆排序
void heapSort(int arr[],int len)
{
	//先对arr进行堆化
	minHeap(arr, len);
	for (int j = 0; j < len; j++)
	{
		cout << arr[j] << endl << endl;
	}
	for (int i = len - 1; i > 0; i--)
	{
		//把堆顶0号元素与最后一个元素对调
		swap(arr, i, 0);
		//缩小堆的范围,对堆顶元素进行向下调整
		minHeapFixDown(arr, 0, i);
	}
	return;

}

桶排序

//每个桶中的插入
void insert(list<int>& bucket, int value)
{
	auto iter = bucket.begin();
	while (iter != bucket.end() && value >= *iter)
	{
		iter++;
	}
	bucket.insert(iter, value);
	return;
}
void bucketSort(vector<int>& arr)
{
	//数组长度不合格,return
	int len = arr.size();
	if (len <= 1)
	{
		return;
	}
	//找出最大最小值
	int min = arr[0], max = arr[0];
	for (auto value : arr)
	{
		if (min > value)
		{
			min = value;
		}
		if (max < value)
		{
			max = value;
		}
	}
	//确定桶的数量,定义桶
	int k = 10;//每个桶中的最大容量
	int bucketNum = (max - min) / k + 1;
	vector<list<int>> buckets(bucketNum);//把list放入vector中,每一个bucket都是一个list<int>
	//遍历,入桶
	for (auto value : arr)
	{
		int index = (value - min) / k;
		insert(buckets[index], value);
	}
	//将排序结果修改到原数组
	int index = 0;
	for (int i = 0; i < bucketNum; i++)
	{
		if (buckets[i].size())
		{
			for (auto value : buckets[i])
			{
				arr[index++] = value;
			}
		}
	}
	//遍历出桶
	/*for (auto value : arr)
	{
		cout << value << endl;
	}*/
	return;
}

计数排序

用辅助数组对数组中出现的数字计数,元素转下标,再下标转元素。若有负数,以下代码采用的方式是将负数放在正数之后

void countSort(int arr[],int len)
{
	//找出最大值和最小值
	int temp_min = 0;
	int temp_max = arr[0];
	for (int i = 0; i < len; i++)
	{
		if (arr[i] > temp_max)
			temp_max = arr[i];
		if (arr[i] < temp_min)
			temp_min = arr[i];
	}
	//开辟辅助空间
	int size = temp_max + 1 + abs(temp_min);
	int *helper = new int[size];
	memset(helper, 0, sizeof(int)*size);
	for (int i = 0; i < len; i++)
	{
		if (arr[i] >= 0)
			helper[arr[i]]++;
		else
			helper[abs(arr[i]) + temp_max]++;
	}
	int index = 0;
	//先将负数拷贝到原数组
	if (temp_min < 0)
	{
		for (int i = size - 1; i > temp_max; i--)
		{
			while (helper[i] != 0)
			{
				arr[index] = temp_max - i;
				index++;
				helper[i]--;
			}
		}
	}
	//再将正数拷贝到原数组
	for (int i = 0; i < temp_max + 1; i++)
	{
		while (helper[i] != 0)
		{
			arr[index] = i;
			index++;
			helper[i]--;
		}
	}
	
	
	delete[] helper;
	return;
}

 

基数排序

首先按低位有效数字进行排序,逐次向上一位排序,直到最高位。传送门解释地肥肠到位!

void radixSort(int arr[], int len)
{
	int MaxIndex = findMax(arr, len);   //得到最大数的位数
	int *buckets = new int[10]; //定义桶,创建10个是因为桶的编号对应当前位数0-9
	int *tmp = new int[len];
	int i, j, radix = 1;
	int temp;
	for (i = 0; i < MaxIndex; i++)  //进行位数次数的排序
	{
		// 给桶赋初值
		for (j = 0; j < 10; j++)
		{
			buckets[j] = 0;
		}
		// 统计每个桶里的元素出现的次数
		for (j = 0; j < len; j++)
		{
			temp = (arr[j] / radix) % 10;
			/*  ( A[j] / radix ) %10的作用
				例如第一个数是13,则 temp=(13/1)%10 == 3
				第四个数是100,
					第一轮循环radix = 1时   temp = (100/1)%10 == 0
					第二轮循环radix = 10时  temp = (100/10)%10 == 0
					第三轮循环radix = 100时  temp = (100/100)%10 == 1
				就此依次从低位到高位得到值
			*/
			buckets[temp]++;
		}
		// 更改buckets[i],目的:让更改后的buckets[i]的值,是在数据在tmp[]中的位置
		for (j = 1; j < 10; j++)
		{
			buckets[j] += buckets[j - 1];
			/* 例如
				buckets[0]=3
				buckets[1]=2
				buckets[2]=1
				那么经过这个循环之后
				buckets[0]=3
				buckets[1]=5
				buckets[2]=3
			*/
		}
		// 将桶内记录依次放到tmp数组中
		for (j = len - 1; j >= 0; j--)
		{
			temp = (arr[j] / radix) % 10;   //和上面作用相同,获得每个元素当前位数的值
			tmp[buckets[temp] - 1] = arr[j];
			buckets[temp]--;
		}


		// 将临时数组中的内容复制到原数组中,倒序是为了不打乱之前几轮的已经排好的顺序
		for (j = 0; j < len; j++)
		{
			arr[j] = tmp[j];
		}
		radix = radix * 10;     // radix 的目的是保值下一次取到更高位的值
	}
	//删除数组
	delete[]tmp;
	delete[]buckets;

}
	// 找到数据里最大的位数
int findMax(int arr[], int len)
{
	int MaxNum = arr[0];
	for (int i = 1; i < len; i++)
	{
		if (arr[i] > MaxNum)
			MaxNum = arr[i];
	}
	// 此时已找到最大数MaxNum
	// 接下来找到最大数的位数
	int MaxIndex = 1;   //初始值设置为1
	while (MaxNum >= 10)    //如果最大值不超过10,则MaxIndex就是1
	{
		MaxNum /= 10;
		MaxIndex++;
	}
	return MaxIndex;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值