ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2021-06-15 09:05:07  阅读:142  来源: 互联网

标签:arr int 元素 C++ ++ 算法 数组 排序


#include <iostream>
#include<vector>          //桶排序用到
#include<time.h>          //生成随机数用到


using namespace std;

/*十大排序算法*/            //https://mp.weixin.qq.com/s/IAZnN00i65Ad3BicZy5kzQ

// 1、稳定排序:如果 a 原本在 b 的前面,且 a == b,排序之后 a 仍然在 b 的前面,则为稳定排序。
// 2、非稳定排序:如果 a 原本在 b 的前面,且 a == b,排序之后 a 可能不在 b 的前面,则为非稳定排序。
// 3、原地排序:原地排序就是指在排序过程中不申请多余的存储空间,只利用原来存储待排数据的存储空间进行比较和交换的数据排序。
// 4、非原地排序:需要利用额外的数组来辅助排序。
// 5、时间复杂度:一个算法执行所消耗的时间。
// 6、空间复杂度:运行完一个算法所需的内存大小


/*1.选择排序*/
//(1)从第一个元素开始往后遍历,找到最小的那个,将其与第一个元素交换位置,
//(2)再从第二个元素开始向后遍历,找到最小的那个元素,将其交换之第二个元素的位置,依次类推
//n+(n-1)+(n-2)+....      
// 1、时间复杂度:O(n^2)    
// 2、空间复杂度:O(1)     
// 3、非稳定排序
// 4、原地排序

int selectSort(int a[], int n)
{
	cout << "This is selectSort \n";
	for (int i = 0; i < n; i++)
	{
		int min = i;
		for (int j = i + 1; j < n; j++)
		{
			if (a[min]>a[j])
			{
				min = j;                 //找到值最小的下标
			}
		}
		int tmp = a[i];                  //将最小的值交换到最前面来
		a[i] = a[min];
		a[min] = tmp;
	}
	return 0;
}



/*2.插入排序*/
//(1)从数组第2个元素开始抽取元素。
//(2)把它与左边第一个元素比较,如果左边第一个元素比它大,则继续与左边第二个元素比较下去,
//   直到遇到不比它大的元素,然后插到这个元素的右边。
//(3)继续选取第3,4,….n个元素,重复步骤 2 ,选择适当的位置插入。
//1、时间复杂度:O(n^2)  
//2、空间复杂度:O(1)
//3、稳定排序
//4、原地排序

int insertSort(int a[], int n)           //从左边第二个开始往前插
{
	cout << "This is insertSort \n";
	for (int i = 1; i < n; i++)
	{
		int tmp = a[i];
		int k = i - 1;
		while (                           //先确定要插入的位置
			k >= 0 && a[k]>tmp
			)
		{
			k--;
		}
		for (int j = i; j > k + 1; j--)    //确定位置右侧全部右移一位
		{
			a[j] = a[j - 1];
		}
		a[k + 1] = tmp;
	}
	return 0;
}


/*3.冒泡排序*/
// 把第一个元素与第二个元素比较,如果第一个比第二个大,则交换他们的位置。
// 接着继续比较第二个与第三个元素,如果第二个比第三个大,则交换他们的位置...
//1、时间复杂度:O(n^2) 
//2、空间复杂度:O(1)  
//3、稳定排序  
//4、原地排序

int bubbleSort(int a[], int n)
{
	cout << "This is bubbleSort \n";
	for (int i = n; i > 0; i--)
	{
		for (int j = 0; j < i; j++)
		{
			if (a[j]>a[j + 1])
			{
				int tmp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = tmp;
			}
		}
	}
	return 0;
}


/*4.希尔排序*/
//插入排序的一个变种,解决原数组的一个元素如果距离它正确的位置很远的话,则需要与相邻元素交换很多次才能到达正确的位置的问题
//希尔排序就是为了加快速度简单地改进了插入排序,交换不相邻的元素以对数组的局部进行排序。
//先让数组中任意间隔为 h 的元素有序,
//刚开始 h 的大小可以是 h = n / 2,接着让 h = n / 4,让 h 一直缩小,
//当 h = 1 时,也就是此时数组中任意间隔为1的元素有序,此时的数组就是有序的了。
//1、时间复杂度:O(nlogn)  
//2、空间复杂度:O(1)  
//3、非稳定排序  
//4、原地排序

int shellSort(int a[], int n)
{
	cout << "This is shellSort \n";
	for (int h = n / 2; h > 0; h = h / 2)   //对每组间隔为h的分组进行排序,刚开始h=n/2           h也是分组的个数   
	{                                                           //分组间距越小每个分组的元素越多
		for (int i = h; i < n; i++)                             //0,1,2,3,4,5,6,7,8,9,10,
		{                                                       //间隔为3的分组:0,3,6,9                
			int temp = a[i];                                    //               1,4,7,10
			int k;                                              //               2,5,8
			for (k = i - h; k >= 0 && temp<a[k]; k = k - h)
			{
				a[k + h] = a[k];
			}
			a[k + h] = temp;
		}
	}
	return 0;
}


/*5.归并排序*/
//将一个大的无序数组有序,我们可以把大的数组分成两个,
//然后对这两个数组分别进行排序,之后在把这两个数组合并成一个有序的数组。
//由于两个小的数组都是有序的,所以在合并的时候是很快的。
//通过递归的方式将大的数组一直分割,直到数组的大小为 1,
//此时只有一个元素,那么该数组就是有序的了,
//之后再把两个数组大小为1的合并成一个大小为2的,再把两个大小为2的合并成4的 ….. 
//直到全部小的数组合并起来
//1、时间复杂度:O(nlogn)  
//2、空间复杂度:O(n)  
//3、稳定排序  
//4、非原地排序

//合并两个序列
void mergeArray(int arr[], int first, int mid, int last, int temp[])  //合并数组
{
	int i = first;
	int j = mid + 1;
	int m = mid;
	int n = last;
	int k = 0;
	while (i <= m && j <= n)
	{
		if (arr[i] <= arr[j])
			temp[k++] = arr[i++];
		else
			temp[k++] = arr[j++];
	}
	while (i <= m)
		temp[k++] = arr[i++];
	while (j <= n)
		temp[k++] = arr[j++];
	for (i = 0; i < k; i++)
		arr[first + i] = temp[i];
}
void mySort(int arr[], int first, int last, int temp[])  //递归
{
	if (first < last)
	{
		int mid = (first + last) / 2;
		mySort(arr, first, mid, temp);
		mySort(arr, mid + 1, last, temp);
		mergeArray(arr, first, mid, last, temp);
	}
}
bool mergeSort(int arr[], int len)   //归并排序
{
	cout << "This is mergeSort \n";
	int*p = new int[len];           //申请新的空间
	if (NULL == p)
		return false;
	mySort(arr, 0, len - 1, p);
	delete[] p;
	return true;
}

//非递归式归并排序

//合并函数
void merge(int arr[], int left, int mid, int right)
{
	//先用一个临时数组把它们合并汇总起来
	int *a = new int[right - left + 1];
	int i = left;
	int j = mid + 1;
	int k = 0;
	while (i <= mid && j <= right)
	{
		if (arr[i] < arr[j])
		{
			a[k++] = arr[i++];
		}
		else
		{
			a[k++] = arr[j++];
		}
	}
	while (i <= mid)   a[k++] = arr[i++];
	while (j<=right)   a[k++] = arr[j++];
	//把临时数组复制到原数组
	for (i = 0; i < k; i++)   arr[left++] = a[i];
	delete[] a;
}


int mergeSortWithoutRecursion(int arr[], int len)
{
	cout << "This is mergeSortWithoutRecursion \n";
	//子数组大小分别为1,2,4,8...
	//刚开始合并的数组大小是1,接着是2,接着4....
	for (int i = 1; i < len; i++)
	{
		//数组进行划分
		int left = 0;
		int mid = left + i - 1;
		int right = mid + i;

		//进行合并,对数组大小为i的数组进行两两合并

		while (right < len)
		{//合并函数和递归型的合并函数一样
			merge(arr, left, mid, right);
			left = right + 1;
			mid = left + i - 1;
			right = mid + i;
		}

		//还有一些被遗漏的数组没合并,千万别忘了
		//因为不可能每个子数组的大小刚好为i
		if (left < len && mid < len)
		{
			merge(arr, left, mid, len - 1);
		}		
	}
	return 1;
}




/*6.快速排序*/
//我们从数组中选择一个元素,我们把这个元素称之为中轴元素吧,然后把数组中所有小于中轴元素的元素放在其左边,
//所有大于或等于中轴元素的元素放在其右边,显然,此时中轴元素所处的位置就是最终排序后的位置。
//从中轴元素那开始将数组分为两个小数组,在递归地方式在小数组中选择一个中轴元素重复上述步骤,最终整个数组变得有序。
// 1、时间复杂度:O(nlogn)  
// 2、空间复杂度:O(logn)  
// 3、非稳定排序  
// 4、原地排序

int Partion(int arr[], int low, int high)     //一次找基准过程
{
	int temp = arr[low];
	while (low<high)                        //当low=high时退出循环,此时的位置为基准位置
	{
		while (low<high && arr[high] >= temp)
		{
			high--;
		}
		if (low >= high)
		{
			break;
		}
		else
		{
			arr[low] = arr[high];
		}
		while (low<high &&arr[low] <= temp)
		{
			low++;
		}
		if (low >= high)
		{
			break;
		}
		else
		{
			arr[high] = arr[low];
		}

	}
	arr[low] = temp;
	return low;
}
void Quick(int arr[], int start, int end)
{
	int par = Partion(arr, start, end);        //第一次找基准
	if (par>start + 1)
	{
		Quick(arr, start, par - 1);
	}
	if (par<end - 1)
	{
		Quick(arr, par + 1, end);
	}
}
void quickSort(int arr[], int len)
{
	cout << "This is quickSort \n";
	Quick(arr, 0, len - 1);
}



//7.堆排序
//堆的特点就是堆顶的元素是一个最值,大顶堆的堆顶是最大值,小顶堆则是最小值。
// 堆排序就是把堆顶的元素与最后一个元素交换,交换之后破坏了堆的特性,我们再把堆中剩余的元素再次构成一个堆,
// 然后再把堆顶元素与最后第二个元素交换….如此往复下去,等到剩余的元素只有一个的时候,此时的数组就是有序的了。
// 1、时间复杂度:O(nlogn)  
// 2、空间复杂度:O(1)  
// 3、非稳定排序  
// 4、原地排序

void downAdjust(int arr[], int parent, int n)
{
	//临时保存要下沉的元素
	int temp = arr[parent];
	//定位左孩子节点的位置
	int child = 2 * parent + 1;
	
	//开始下沉
	while (child<=n)
	{	//如果右孩子比左孩子大,则定位到右孩子
		if (child + 1 <= n && arr[child] < arr[child + 1])     child++;
		//如果孩子节点小于或等于父亲节点,则下沉结束
		if (arr[child] <= temp)  break;
		//父节点进行下沉
		arr[parent] = arr[child];
		parent = child;
		child = 2 * parent + 1;
	}
	arr[parent] = temp;
}


void heapSort(int arr[], int n)
{
	cout << "This is heapSort \n";
	//构建大顶堆
	for (int i = (n-2)/2; i >= 0; i--)
	{
		downAdjust(arr, i, n - 1);
	}
	//进行堆排序
	for (int i = n-1; i >= 1 ; i--)
	{
		//把堆顶元素与最后一个元素交换
		int temp = arr[i];
		arr[i] = arr[0];
		arr[0] = temp;
		//把打乱的堆进行调整恢复堆的特性
		downAdjust(arr, 0, i - 1);
	}
}

//8.计数排序
//计数排序是一种适合于最大值和最小值的差值不是不是很大的排序。
//基本思想:就是把数组元素作为数组的下标,然后用一个临时数组统计该元素出现的次数,
//例如 temp[i] = m, 表示元素 i 一共出现了 m 次。最后再把临时数组统计的数据从小到大
//汇总起来,此时汇总起来是数据是有序的。
//1、时间复杂度:O(n + k)  
//2、空间复杂度:O(k)  
//3、稳定排序  
//4、非原地排序

void countSort(int arr[], int n)
{
	cout << "This is countSort \n";
	if (nullptr == arr || n < 2)  return;

	int min = arr[0];
	int max = arr[0];

	//寻找数组最大值与最小值
	for (int i = 1; i < n; i++)
	{
		if (max < arr[i])   max = arr[i];
		if (min > arr[i])   min = arr[i];
	}
	
	//创建大小为max - min + 1的临时数组
	int d = max - min + 1;
	int *temp = new int[d];
	for (int i = 0; i < n; ++i)    temp[i] = 0;        //C++要赋初值不然无法排序的
	//统计元素i出现的次数
	for (int  i = 0; i < n; i++)
	{
		temp[arr[i]-min]++;
	}
	int k = 0;
	//把临时数组统计好的数据汇总到原数组
	for (int i = 0; i <= d; i++)
	{
		for (int j = temp[i]; j >0; j--)
		{
			arr[k++] = i+min;
		}
	}
}

//9.桶排序
//桶排序就是把最大值和最小值之间的数进行瓜分,例如
//分成 10 个区间,10个区间对应10个桶,我们把各元素
//放到对应区间的桶中去,再对每个桶中的数进行排序,
//可以采用归并排序,也可以采用快速排序之类的。
//之后每个桶里面的数据就是有序的了,我们在进行合并汇总。
//1、时间复杂度:O(n + k)  
//2、空间复杂度:O(n + k)  
//3、稳定排序  
//4、非原地排序

const int BUCKET_NUM = 10;

struct ListNode {
	explicit ListNode(int i = 0) :mData(i), mNext(NULL) {}
	ListNode* mNext;
	int mData;
};

ListNode* insert(ListNode* head, int val) {
	ListNode dummyNode;
	ListNode *newNode = new ListNode(val);
	ListNode *pre, *curr;
	dummyNode.mNext = head;
	pre = &dummyNode;
	curr = head;
	while (NULL != curr && curr->mData <= val) {
		pre = curr;
		curr = curr->mNext;
	}
	newNode->mNext = curr;
	pre->mNext = newNode;
	return dummyNode.mNext;
}


ListNode* Merge(ListNode *head1, ListNode *head2) {
	ListNode dummyNode;
	ListNode *dummy = &dummyNode;
	while (NULL != head1 && NULL != head2) {
		if (head1->mData <= head2->mData) {
			dummy->mNext = head1;
			head1 = head1->mNext;
		}
		else {
			dummy->mNext = head2;
			head2 = head2->mNext;
		}
		dummy = dummy->mNext;
	}
	if (NULL != head1) dummy->mNext = head1;
	if (NULL != head2) dummy->mNext = head2;

	return dummyNode.mNext;
}

void bucketSort(int arr[],int n ) {
	cout << "This is bucketSort \n";
	vector<ListNode*> buckets(BUCKET_NUM, (ListNode*)(0));
	for (int i = 0; i<n; ++i) {
		int index = arr[i] / BUCKET_NUM;
		ListNode *head = buckets.at(index);
		buckets.at(index) = insert(head, arr[i]);
	}
	ListNode *head = buckets.at(0);
	for (int i = 1; i<BUCKET_NUM; ++i) {
		head = Merge(head, buckets.at(i));
	}
	for (int i = 0; i<n; ++i) {
		arr[i] = head->mData;
		head = head->mNext;
	}
}

//10.基数排序
//先以个位数的大小来对数据进行排序,接着以十位数的大小来多数进行排序,接着以百位数的大小……
//排到最后,就是一组有序的元素了。不过,他在以某位数进行排序的时候,是用“桶”来排序的。

int maxbit(int data[], int n) //辅助函数,求数据的最大位数
{
	int maxData = data[0];              ///< 最大数
										/// 先求出最大数,再求其位数,这样有原先依次每个数判断其位数,稍微优化点。
	for (int i = 1; i < n; ++i)
	{
		if (maxData < data[i])
			maxData = data[i];
	}
	int d = 1;
	int p = 10;
	while (maxData >= p)
	{
		//p *= 10; // Maybe overflow
		maxData /= 10;
		++d;
	}
	return d;
	/*    int d = 1; //保存最大的位数
	int p = 10;
	for(int i = 0; i < n; ++i)
	{
	while(data[i] >= p)
	{
	p *= 10;
	++d;
	}
	}
	return d;*/
}
void radioSort(int data[], int n)
{
	cout << "This is radioSort \n";
	int d = maxbit(data, n);
	int *tmp = new int[n];
	int *count = new int[10]; //计数器
	int i, j, k;
	int radix = 1;
	for (i = 1; i <= d; i++) //进行d次排序
	{
		for (j = 0; j < 10; j++)
			count[j] = 0; //每次分配前清空计数器
		for (j = 0; j < n; j++)
		{
			k = (data[j] / radix) % 10; //统计每个桶中的记录数
			count[k]++;
		}
		for (j = 1; j < 10; j++)
			count[j] = count[j - 1] + count[j]; //将tmp中的位置依次分配给每个桶
		for (j = n - 1; j >= 0; j--) //将所有桶中记录依次收集到tmp中
		{
			k = (data[j] / radix) % 10;
			tmp[count[k] - 1] = data[j];
			count[k]--;
		}
		for (j = 0; j < n; j++) //将临时数组的内容复制到data中
			data[j] = tmp[j];
		radix = radix * 10;
	}
	delete[]tmp;
	delete[]count;
}




int main()
{
	int n,select,a[100];
	cout << "This is sort tutorial ! " << endl;
	cout << "How many numbers do you want to set ? " << endl;
	cin >> n;
	
	srand((int)time(NULL)); // 设置时间为随机点

	for (int i = 0; i < n; i++) // 产生n个随机数
		a[i] = rand() % 100;
	cout << "Those numbers before sort is : \n";
	for (int i = 0; i < n; i++)
	{
		cout << a[i] << "  ";
		if (0 == (i+1) % 10) cout << endl;
	}
	cout << "\n Please select the method: \n";
	cout << "1.selectSort \t 2.insertSort \t 3.bubbleSort \t 4.shellSort \n";
	cout << "5.mergeSort \t 6.mergeSortWithoutRecursion \t 7.quickSort  \n";
	cout << "8.heapSort \t 9.countSort \t 10.bucketSort \t 11.radioSort \n";

	cin >> select;
	switch (select)
	{
	case 1:selectSort(a, n); break;                                //选择排序
	case 2:insertSort(a, n); break;                                //插入排序
	case 3:bubbleSort(a, n); break;								   //冒泡排序
	case 4:shellSort(a, n); break;								   //希尔排序
	case 5:mergeSort(a, n); break;								   //归并排序
	case 6:mergeSortWithoutRecursion(a, 10); break;				   //非递归归并排序
	case 7:quickSort(a, n); break;								   //快速排序
	case 8:heapSort(a, n); break;								   //堆排序
	case 9:countSort(a, n); break;								   //计数排序
	case 10:bucketSort(a, n); break;							   //桶排序
	case 11:radioSort(a, n); break;								   //基数排序
	default:cout << "method select error";
		break;
	}

	// selectSort(a,10);                        //选择排序
	// insertSort(a,10);                        //插入排序
	// bubbleSort(a,10);                        //冒泡排序
	// shellSort(a,10);                         //希尔排序
	// mergeSort(a,10);                         //归并排序
	// mergeSortWithoutRecursion(a, 10);        //非递归归并排序
	// quickSort(a,10);                         //快速排序
	// heapSort(a, 10);                         //堆排序
	// countSort(a, 10);                        //计数排序
	// bucketSort(a, 10);                       //桶排序
	// radioSort(a, 10);                        //基数排序
	cout << "after sort :" << endl;
	for (int i = 0; i < n; i++)
	{
		cout << a[i] << "  ";
		if (0 == (i + 1) % 10) cout << endl;
	}
	cout << endl;
	return 0;
}

标签:arr,int,元素,C++,++,算法,数组,排序
来源: https://blog.csdn.net/weixin_45393946/article/details/117917714

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有