各种排序算法(冒泡、选择、快排、插入、希尔、堆排、归并、计数、基数)

各种排序算法(冒泡、选择、快排、插入、希尔、堆排、归并、计数、基数)_第1张图片各种排序算法(冒泡、选择、快排、插入、希尔、堆排、归并、计数、基数)_第2张图片

计数排序:时间复杂度:O(N), 空间复杂度O(最大数-最小数)
基数排序:时间复杂度:O(N*位数),空间辅助度O(N)

 

 

冒泡排序(BubbleSort)

       冒泡排序(BubbleSort)是一种简单的排序算法。它重复地遍历过要排序的数列,一次比较两个元素,如果他们的顺序错误他们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。

冒泡排序算法的步骤:

      1.比较相邻的元素。如果第一个比第二个大,就交换他们两个。

      2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。

      3.针对所有的元素重复以上的步骤,除了最后一个。

      4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

选择排序(SelectSort)

       选择排序(SelecSort)是一种简单直观的排序算法。它的工作原理如下:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

简单选择排序算法改进

       传统的简单选择排序,每趟循环只能确定一个元素排序后的定位。我们可以考虑改进为每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。改进后对n个数据进行排序,最多只需进行[n/2]趟循环即可。

快速排序(QuickSort)

       快速排序是冒泡排序的一种改进,冒泡排序排完一趟是最大值冒出来了,那么可不可以先选定一个值,然后扫描待排序序列,把小于该值的记录和大于该值的记录分成两个单独的序列,然后分别对这两个序列进行上述操作。这就是快速排序,我们把选定的那个值称为枢纽值,如果枢纽值为序列中的最大值,那么一趟快速排序就变成了一趟冒泡排序。

快速排序算法的步骤:

       1.从数列中挑出一个元素,称为 "基准",重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。

       2.递归地把小于基准值元素的子数列和大于基准值元素的子数列排序。

       3.递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。

插入排序(InsertSort)

       插入排序就是每一步都将一个待排数据按其大小插入到已经排序的数据中的适当位置,直到全部插入完毕。 插入排序方法分直接插入排序和折半插入排序两种。

希尔排序(ShellSort)

       希尔排序(ShellSort)又称为“缩小增量排序”。该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

       1.插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率。

       2.插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位,步长的选择是希尔排序的重要部分。只要最终步长为1任何步长序列都可以工作。

堆排序(HeapSort)

        移除位在第一个数据的根结点,并做最大堆调整的递归运算。

堆的存储

        一般都用数组来表示堆,i结点的父结点下标就为(i – 1) / 2。它的左右子结点下标分别为2 * i + 1和2 * i + 2。

归并排序(MergeSort)算法

        归并排序(MergeSort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法的一个非常典型的应用。

归并排序的步骤如下:

        1. 把长度为n的输入序列分成两个长度为n/2的子序列。

        2. 对这两个子序列分别采用递归归并排序。

        3. 将两个排序好的子序列递归合并成一个最终的排序序列。

基数排序(RadixSort)

       基数排序(RadixSort)是对桶排序的改进和推广。唯一的区别是基数排序强调多关键字,而桶排序没有这个概念,换句话说基数排序对每一种关键字都进行桶排序,而桶排序同一个桶内排序可以任意或直接排好序。基数排序的方式可以采用LSD或MSD,LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。

基数排序的基本思想是:从低位到高位依次对Kj(j=d-1,d-2,…,0)进行箱排序。

计数排序(CountSort)

       计数排序(CountSort)是一种稳定的排序算法,和基数排序一样都是桶排序的变体。计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值小于等于i的元素的个数。然后根据数组C来将A中的元素排到正确的位置。

计数排序的原理

       设被排序的数组为A,排序后存储到B,C为临时数组。所谓计数,首先是通过一个数组C[i]计算大小等于i的元素个数,此过程需要一次循环遍历就可以;在此基础上,计算小于或者等于i的元素个数,也是一重循环就完成。下一步是关键:逆序循环,length[A]到1,将A[i]放到B中第C[A[i]]个位置上。原理是:C[A[i]]表示小于等于a[i]的元素个数,正好是A[i]排序后应该在的位置。而且从length[A]到1逆序循环,可以保证相同元素间的相对顺序不变,这也是计数排序稳定性的体现。在数组A有附件属性的时候,稳定性是非常重要的。

 

#include
#include
#include
using namespace std;

//冒泡排序   O(N^2)
void BubbleSort(int *arr, int len)
{
	for (int i = 0; i < len - 1; ++i)
	{
		bool exchange = false;
		for (int j = 1; j < len - i; ++j)
		{
			if (arr[j - 1]>arr[j])
			{
				swap(arr[j-1],arr[j]);
				exchange = true;
			}
		}
		if (exchange == false)
		{
			return;
		}
	}
}

//选择排序  O(N^2)
void SelectSort1(int *arr, int len)
{
	for (int i = 0; i < len-1; ++i)
	{
		int k = i;//k保存最小的数
		for (int j =i+1; j < len; ++j)
		{
			if (arr[k]>arr[j])
			{
				k=j;
			}
		}
		if (k != i)
		{
			swap(arr[k], arr[i]);
		}
	}
}
//选择排序的优化  O(N^2)
void SelectSort(int *arr, int len)
{
	int begin = 0;
	int end = len - 1;
	while (begin < end)
	{
		int min = begin;
		int max = end;
		for (int i = begin; i <= end; ++i)
		{
			if (arr[min]>arr[i])
				swap(arr[min], arr[i]);
			if (arr[max] < arr[i])
				swap(arr[max], arr[i]);
		}
		++begin;
		--end;
	}
}

//快速排序  O(N*lgN)
void _QuickSort1(int *arr,int left,int right)
{
	assert(arr);
	if (left >= right)
		return;
	int begin = left;
	int end = right - 1;
	int key = arr[right];
	while (begin < end)
	{
		if (begin < end && arr[begin] <= key)//找大
		{
			++begin;
		}
		if (begin < end && arr[end] >= key)//找小
		{
			--end;
		}
		if (begin = right)
		return;
	assert(arr);
	int key = arr[right];
	int cur = left;
	int prev = left - 1;
	while (cur < right)
	{
		//找小,停下进行翻转
		if (arr[cur] < key &&  ++prev!=cur)
		{
			swap(arr[cur], arr[prev]);
		}
		++cur;
	}
	swap(arr[++prev], arr[right]);

	_QuickSort2(arr,left,prev-1);
	_QuickSort2(arr,prev+1,right);
}
void QuickSort(int *arr, int len)
{
	//_QuickSort1(arr,0,len-1);
	_QuickSort2(arr,0,len-1);
}
int Partion(int *arr, int left, int right)
{
	assert(arr);
	if (left >= right)
		return -1;
	int key = arr[right];
	int cur = left;
	int prev = left - 1;
	while (cur < right)
	{
		if (arr[cur] < key&& ++prev != cur)
		{
			swap(arr[cur], arr[prev]);
		}
		++cur;
	}
	swap(arr[++prev],arr[right]);

	return prev;
}
//处理栈溢出问题
void QuickSort_NonR(int *arr, int len)
{
	stack s;
	s.push(len-1);
	s.push(0);

	while (!s.empty())
	{
		int left = s.top();
		s.pop();
		int right = s.top();
		s.pop();

		int key = Partion(arr,left,right);
		if (key == -1)
		{
			continue;
		}
		if (key - 1 > left)
		{
			s.push(key-1);
			s.push(left);
		}
		if (key + 1 < right)
		{
			s.push(right);
			s.push(key+1);
		}
	}
}
//插入排序      O(N^2)
void InsertSort(int *arr, int len)
{
	for (int i = 1; i < len; ++i)
	{
		//挪动数据
		int tmp = arr[i];//待插入数据
		int end = i - 1;
		while (arr[end]>tmp&&end >= 0)
		{
			arr[end + 1] = arr[end];
			--end;
		}
		arr[end + 1] = tmp;
	}
}
//希尔排序    当N大时,平均的时间复杂度,大约在N^1.25--1.6N^1.25之间。
void ShellSort(int *arr,int len)
{
	int gap = len;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int i = gap; i < len; i += gap)
		{
			int tmp = arr[i];//待插入数据
			// 挪动数据
			int end = i - gap;
			while (arr[end]>tmp&&end >= 0)
			{
				arr[gap+end] = arr[end];
				end -= gap;
			}
			arr[end + gap] = tmp;
		}
	}
}

//堆排序     O(N*lgN)
void _AdjustDown(int *arr, int root,int len)
{
	int parent = root;
	int left = 2 * parent+1;
	int right = left + 1;
	while (left = 0; --i)
	{
		_AdjustDown(arr,i,len);
	}
	//选择堆顶数据进行排序
	int end = len - 1;
	while (end > 0)
	{
		swap(arr[0], arr[end]);
		_AdjustDown(arr,0,end);
		--end;
	}
}
//归并排序
void _Merge(int *src, int *dst, int begin, int mid, int end)
{
	int begin1 = begin;
	int end1 = mid;
	int begin2 = mid;
	int end2 = end;
	int index = begin;
	while (begin1 < end1 && begin2 < end2)
	{
		if (src[begin1] = right)
		return;
	
	int mid = (left + right) / 2;
	// 递归排序左右区间,
	_MergeSort(arr,tmp, left, mid);
	_MergeSort(arr,tmp, mid, right);
	// 合并左右区间
	// [left,mid) [mid,right)
	_Merge(arr, tmp, left, mid, right);
}
void MergeSort(int *arr,int len)
{
	int *tmp = new int[len]();
	_MergeSort(arr,tmp,0,len);
	delete []tmp;
}
//计数排序
void CountSort(int *arr,int len)
{
	int max = arr[0];
	int min = arr[0];
	for (int i = 1; i < len; ++i)
	{
		if (arr[i]>max)
		{
			max = arr[i];
		}
		if (arr[i] < min)
		{
			min = arr[i];
		}
	}
	int *tmp = new int[max-min + 1];
	memset(tmp,0,sizeof(int)*(max-min+1));

	for (int i = 0; i < len; ++i)
	{
		tmp[arr[i] - min]++;
	}
}
//基数排序
void RadixSort_LSD(int* arr, int len)
{
	int maxBit = 2;
	int* count = new int[10]();
	int* bucket = new int[len]();

	int radix = 1;
	for (int bit = 1; bit <= maxBit; ++bit)
	{
		memset(count, 0, sizeof(int)* 10);

		// 统计每个桶的数据个数
		for (int i = 0; i < len; ++i)
		{
			int k = (arr[i] / radix) % 10;
			count[k]++;
		}

		// 统计在桶中的位置
	    for (int i = 1; i < len; ++i)
		{
			count[i] = count[i] + count[i - 1];
		}

		for (int i = len - 1; i >= 0; --i)
		{
			int k = (arr[i] / radix) % 10;
			bucket[--count[k]] = arr[i];
		}

		memcpy(bucket, arr, sizeof(int)*len);

		radix *= 10;
	}
}
void Printf(int *arr,int len)
{
	for (int i = 0; i < len; ++i)
	{
		printf(" %d ", arr[i]);
	}
	printf("\n");
}
int main()
{
	int arr[] = {10,8,1,4,6,5,0,10,9};
	int len = sizeof(arr) / sizeof(arr[0]);
	//BubbleSort(arr, len);
	//SelectSort1(arr, len);
	//QuickSort(arr, len);
	//QuickSort_NonR(arr,len);
	//InsertSort(arr, len);
	//ShellSort(arr,len);
        //HeapSort(arr,len);
	//MergeSort(arr,len);
	//CountSort(arr,len);
	//RadixSort_LSD(arr,len);
	Printf(arr,len);
	return 0;
}


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(C/C++)