七大主要排序C实现

排序算法

七大排序,个人理解为五大排序,其中两对排序思维是一致的
这是一次自己学习和手敲排序的过程。首先上主函数

#include "sort.h"

int main()
{
	int arr[15]= { 8,6,65,4,98,10,15,48,27,61,20,16,49,72,81 };

	//bubble_Sort(arr, 15);  //冒泡排序
	//quick_Sort(arr,15);    //快速排序
	//insert_sort(arr, 15);  //插入排序
	//shell_sort(arr, 15);   //希尔排序
	//select_sort(arr, 15);  //选择排序
	//mergesort(arr, 15);    //归并排序
	heapsort(arr, 15);       //堆排序

    //输出排序后的数组
	for (int i = 0; i < 15; i++)
	{
		cout << arr[i]<<" ,";
	}
	cout << endl;
	getchar();
	return 0;
}

1、冒泡排序

两两比较大小,每一次遍历将未排序数组中最大的一个数传递到最后。

#pragma once
#include <iostream>
using namespace std;

//冒泡   两两比较大小,每一次遍历将未排序数组中最大的一个数传递到最后
void bubble_Sort(int *arr,int arrsize)
{
	bool exchange = true;
	if (arrsize < 2) { cout << "数组元素少于2个" << endl; return; }
	for (int i = arrsize - 1 ; i > 0 && exchange; i--)
	{
		exchange = false;    //添加一个对未完成排序的数组是否已经有序的判断
		for (int j = 0; j < i ; j++)
		{
			if (arr[j] <= arr[j + 1]) {  continue; }
			int temp = arr[j + 1];
			arr[j + 1] = arr[j];
			arr[j] = temp;
			exchange = true;
		}
		//记录冒泡过程
		cout << "这是第" <<arrsize-i<<"次"<< endl;
		for (int ii = 0; ii < 15; ii++)
		{
			cout << arr[ii] << " ,";
		}
		cout << endl;
	}
	cout << "\n\n";
	return;
}

2、堆排序(冒泡进阶)

/*堆排序

*/
// 交换两个元素的值。
void swap(int *a, int *b) { int temp = *b; *b = *a; *a = temp; }

// 采用循环实现heapify(元素下沉)。
// arr-待排序数组的地址,start-待heapify节点的下标,end-待排序数组最后一个元素的下标。
void heapify(int *arr, int start, int end)
{
	// 确定父节点和左子节点的数组下标。
	int dad = start;
	int son = dad * 2 + 1;

	// 如果子节点的下标没有超出范围,循环继续。
	while (son <= end)
	{
		// 先比较两個子节点大小,选择最大的。
		if ((son + 1 <= end) && (arr[son] < arr[son + 1])) son++;

		// 如果父节点大于子节点代表调整完毕,直接跳出函数。
		if (arr[dad] > arr[son]) return;

		// 否则交换父子內容再继续子节点和孙节点比较。
		swap(&arr[dad], &arr[son]);
		dad = son;
		son = dad * 2 + 1;
	}
}

// 采用递归实现heapify。
void heapify1(int *arr, int start, int end)
{
	// 确定父节点和左子节点的数组下标。
	int dad = start;
	int son = dad * 2 + 1;

	// 如果子节点的下标没有超出范围,循环继续。
	if (son > end) return;

	// 先比较两個子节点大小,选择最大的。
	if ((son + 1 <= end) && (arr[son] < arr[son + 1])) son++;

	// 如果父节点大于子节点代表调整完毕,直接跳出函数。
	if (arr[dad] > arr[son]) return;

	// 否则交换父子內容再继续子节点和孙节点比较。
	swap(&arr[dad], &arr[son]);

	heapify(arr, son, end);
}

void heapsort(int *arr, int len)
{
	int ii;

	// 初始化堆,从最后一個父节点开始调整。
	for (ii = (len - 1) / 2; ii >= 0; ii--) heapify(arr, ii, len - 1);

	// 把第一个元素和堆最后一个元素交换,然后重新调整,直到排序完毕。
	for (ii = len - 1; ii > 0; ii--)
	{
		swap(&arr[0], &arr[ii]);//其实堆排序采用的就是冒泡排序的思维,每次将大顶堆的根节点后最后一个元素交换,最后一个元素不再进入堆调整
		heapify(arr, 0, ii - 1);
	}
}

堆排序中,heapify()和heapify1()实现的是一样的功能,只是实现的方式一个是循环,一个是递归。
冒泡和堆排序思维都是每次遍历将最大(或者最小,后面一致默认升序排序)传递到数组末尾,之后不再进入遍历循环。

3、插入排序

插入排序的思维是将第0个元素当作有序序列,从下标为1的元素开始插入有序序列中。

void insert_sort(int *arr, int arrsize)
{
	if (arrsize < 2)
	{
		//cout << "输入数组成员小于2" << endl;
		return;
	}
	for (int i = 1;i<arrsize;i++)
	{
		int temp = arr[i];
		int j = i - 1;
		bool exchange = false;
		if (arr[j] > temp)
		{
			for (; j >= 0 && arr[j] > temp; j--)
			{
				arr[j + 1] = arr[j];
				//对比于希尔排序,插入排序可能存在距离太远导致元素移动成本太高,特别是数组成员数量级比较大时。
			}
			arr[j+1] = temp;
			exchange = true;
		}
		
		if(exchange)
		{
			//记录插入过程
			cout << "这是第" << i << "次排序" << endl;
			for (int ii = 0; ii < 15; ii++)
			{
				cout << arr[ii] << " ,";
			}
			cout << endl;
		}
	}
	return;
}

4、希尔排序(插入排序进阶版)

插入排序可能有这种情况:待插入元素需要插入的位置距离原位置太远,导致要移动的元素太多,移动成本太高,特别是数组成员数量级比较大时。
希尔排序利用分治的思维将数组等差间隔分组,分而治之。

//每次确定步长和此步长下的单个分组进行排序
void shell_insert(int *arr, int arrlength, int istep, int groupflag)
{
	for (int i = istep + groupflag; i < arrlength ; i +=istep)//步长+分组标志
	{
		int temp = arr[i];
		int j = i - istep;
		if (arr[j] > temp)//只有temp小于分组前一个数,才进行移动,否则直接移动到后一个元素
		{
			for (; j >= 0 && arr[j] > temp; j -= istep)//进行插入排序
			{
				arr[j + istep] = arr[j];
				//对比于希尔排序,插入排序可能存在距离太远导致元素移动成本太高,特别是数组成员数量级比较大时。
			}
			arr[j + istep] = temp;
		}
	}
}

//确定步长和步长之后的小分组
void shell_sort(int *arr, int arrsize)
{
	int istep = arrsize / 2,i;
	for (;istep > 0;istep/=2)//确定步长
	{
		for (i = 0; i < istep; i++)//获取步长确定之后的分组标志
		{
			shell_insert(arr, arrsize, istep,i);
		}
		//记录当前步长排序之后数组序列;
		cout << "这是步长" << istep << "分组排序后的数组" << endl;
		for (int ii = 0; ii < 15; ii++)
		{
			cout << arr[ii] << " ,";
		}
		cout << endl;
	}
}

5、选择排序

和冒泡都是选择最值,将最值放入有序序列中,但是选择排序没有相邻传递的特性。

//选择排序
void select_sort (int *arr,int arrlength)
{
	int min_temp,min_flag;
	bool exchange;
	for (int i = 0; i < arrlength; i++)//初设定最小值
	{
		min_temp = arr[i];
		min_flag = i;
		exchange = false;
		for(int j=i;j<arrlength;j++)//向数组后面遍历是否有更小的值
		{
			if (arr[j] < min_temp) 
			{
				min_temp = arr[j]; 
				min_flag = j; 
				exchange = true;//最小值被重置标志
			}
		}
		if (exchange) { arr[min_flag] = arr[i]; arr[i] = min_temp; }//如果最小值被更新过,进行替换
	}
}

6、快速排序

挖出一个数作为基准,大的放入基准左边,小的放入基准右边,然后在两端形成的新数组,再次分边站,直到数组小于2。
带有选择排序挖坑的思维,也有希尔排序分治的思想。

//快排   挖出一个数作为基准,大的放入基准左边,小的放入基准右边,然后在两端形成的新数组,再次分边站,直到数组小于2
void quick_Sort(int *arr1, int arr1size)//挖数,填坑,分治
{
	if (arr1size < 2)
	{
		//cout << "输入数组成员小于2" << endl;
		return;
	}
	int temp = arr1[0];
	int left = 0, right = arr1size - 1;//左右下标
	bool rightmv = true;//目前是不是右下标移动
	while (left < right)
	{
		if (rightmv)
		{
			if (arr1[right] >= temp) { right--; continue; }
			arr1[left] = arr1[right];     //把right的数据填进左边的坑
			left++;                     //空出的right不动,left加一
			rightmv = false;
			continue;
		}
		if (!rightmv)
		{
			if (arr1[left] <= temp) { left++; continue; }
			arr1[right] = arr1[left];    //把left的数据填进右边的坑
			right--;                   //空出的left不动,right减一
			rightmv = true;
			continue;
		}
	}
	arr1[left] = temp;                  //将中心轴的数值填入

	quick_Sort(arr1, right);
	quick_Sort(&arr1[right + 1], arr1size - right - 1);
}

7、归并排序

归并排序是将单个元素看作一个个有序序列,再将相邻两个有序序列归并成一个有序序列,进行递归

//归并排序
void _mergesort(int *arr, int *arrtmp, int start, int end)
{
	// 如果start>=end,表示该区间的元素少于两个,递归终止。
	if (start >= end) return;

	int mid = start + (end - start) / 2;  // 计算排序区间中间的位置。

	/*核心在于理解这两句递归
	使用内联的思维将两个递归函数展开
	*/
	int istart1 = start, iend1 = mid;  // 区间左边元素的第一和最后一个元素的位置。
	int istart2 = mid + 1, iend2 = end;  // 区间右边元素的第一和最后一个元素的位置。

	_mergesort(arr, arrtmp, istart1, iend1);   // 对区间左边元素递归排序。
	_mergesort(arr, arrtmp, istart2, iend2);   // 对区间右边元素递归排序。

	int ii = start; // 已排序数组arrtmp的计数器。

	// 把区间左右两边数列合并到已排序数组arrtmp中。
	while (istart1 <= iend1 && istart2 <= iend2)
		arrtmp[ii++] = arr[istart1] < arr[istart2] ? arr[istart1++] : arr[istart2++];

	// 把左边数列其它的元素追加到已排序数组。
	while (istart1 <= iend1) arrtmp[ii++] = arr[istart1++];

	// 把右边数列其它的元素追加到已排序数组。
	while (istart2 <= iend2) arrtmp[ii++] = arr[istart2++];

	// 把已排序数组arrtmp中的元素复制到arr中。
	memcpy(arr + start, arrtmp + start, (end - start + 1) * sizeof(int));
}

// 排序主函数,arr为待排序的数组的地址,len为数组的长度。
void mergesort(int *arr, unsigned int len)
{
	if (len < 2) return;  // 小于两个元素不需要排序。
	int *arrtmp=new int[len];  // 分配一个与待排序数组相同大小的数组。
	_mergesort(arr, arrtmp, 0, len - 1);  // 调用递归函数进行排序。
	delete []arrtmp;
}

8、排序算法对比

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值