排序算法(有注释)

目录

一、冒泡排序

规则:

代码:

二、选择排序

规则:

代码:

三、插入排序

规则:

代码:

四、希尔排序

规则:

代码:

五、快速排序

规则:

代码:

六、基数排序(桶排序):

规则:

代码:

七、计数排序

规则:

代码:

八、归并排序

规则:

代码:


一、冒泡排序

规则:

n 个数据进行冒泡排序,首先将第一个数据和第二个数据进行比较,如果为逆序就交 换两个数据的值,然后比较第二个和第三个数据,依此类推,直到第最后一个和倒数第二个比较完了为 止。上述过程为冒泡排序的第一趟冒泡排序,其结果是最大或者最小的数据被放置在末尾的位置。然后 进行第二趟排序,把第二大或者第二小的数放置在倒数第二的位置,之后每一趟排序都会使一个数据有 序,直到此序列的全部数据都有序为止。

注意

每一次比较都把最大的放到最后面。

代码:

void bubble_sort(int x[], int n)
{
	for (int i = 0; i < n-1; i++)
	{
		for (int j = 0; j < n - 1-i; j++)  //每一轮比较都把最大的数放到最后,然后下一轮一直排到最大的数之前(这时,已经排列出来的每轮最大的数的个数为i)。
		{
			if (x[j] > x[j + 1])
			{
				swap(x[j], x[j + 1]);
			}
		}
	}
}

二、选择排序

规则:

对一个序列进行选择排序,首先通过一轮循环比较,从 n 个数据中找出最大或者最小的那个数据的位 置,然后按照递增或者递减的顺序,将此数据与第一个或最后一个数据进行交换。然后再找第二大或者 第二小的数据进行交换,以此类推,直到序列全部有序为止。

代码:

void select_sort(int x[], int n)
{
	int min;
	for (int i = 0; i < n; i++)
	{
		min = i;
		for (int j = i; j < n; j++)
		{
			if (x[min] > x[j])
				min = j;
		}
		swap(x[i], x[min]);
	}
}

三、插入排序

规则:

  • 基本思想:将一个记录插入到已经排好序的有序表中,从而产生一个新的、记录数增1的有序表。
  • 实现过程:双循环;
  1. 外层循环对除了第一个元素以外的所有元素;
  2. 内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。

一共n个元素,需要插入的元素n-1个

代码:

void insert_sort(int arr[], int len)
{
	int temp;
	int j;
	for (int i = 1; i < len; i++)
	{
		temp = arr[i];//保存待插入的值,
		j = i - 1;//将要插入的数与它前面的数分别比较
		while (j >= 0 && temp < arr[j])
		{
			arr[j + 1] = arr[j];//取出(待插入)的那个数位置空,它前面的数顶替进来,前面所有大于它的数挨个往后面挪,直到第一个小于取出来的数为止。
			--j;
		}
		arr[j + 1] = temp;
	}
}

四、希尔排序

规则:

把记录按下标的一定增量分组,对每组使用直接插入排序算法排序:随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

每一组同一个位置相比较,相交换。靠前组的数据比后面的数据小。

插入排序的优化。

插入排序是所有数跟它前面的数比较,希尔排序是每个数跟它前面分组中的相应同一位置的数相比较。

代码:

void shell_sort(int x[], int n)
{
	int temp;
	int j;
	int jump = n >> 1;//n/2
	while (jump != 0)
	{
		for (int i = jump; i < n; i++)
		{
			temp = x[i];
			j = i - jump;
			while (j >= 0 && temp < x[j])
			{
				x[j + jump] = x[j];//每个数组同一位置的数相比较。取出(待插入)的那个数位置空,它前面每隔jump的数顶替进来,前面所有大于待插数的数挨个往后面挪,直到第一个小于待插数的数为止。
				j = j - jump;
			}
			x[j + jump] = temp;
		}
		jump >>= 1;  //jump=jump/2 
	}
}

五、快速排序

规则:

通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
 

代码:

void quick_sort(int arr[], int low, int hight)
{
	int t = arr[low];//标杆。
	int f = low + 1;
	int b = hight;
	if (low >= hight) return;
	while (f <= b)
	{
		while (f <= b && arr[f] <= t) f++;//遇到大于t的数停止移动。
		while (f <= b && arr[b] >= t) b--;//遇到小于t的数停止移动。
		//都停止移动,交换标记数。
		if (f < b)
		{
			swap(arr[f], arr[b]); //交换arr[f],arr[b]使arr[f]<t.arr[b]>t
			f++;//f继续向右找小于t的数。
			b--;//b继续向左找大于t的数。
		}
	}
	//f>b的时候跳出循环,这时arr[b]<arr[f] 
	swap(arr[low], arr[b]);

	//对两部分进行递归操作。
	quick_sort(arr, low, b - 1);
	quick_sort(arr, b + 1, hight);
}

六、基数排序(桶排序):

规则:

       基数排序又称为"桶子法”,从低位开始将待排序的数按照这一位的值放到相应的编号为0~9的桶中。等到低位排完得到一个子序列,再将这个序列按照次低位的大小进入相应的桶中,一直排到最高位为止,数组排序完成。

      基数排序法是属于稳定性的排序。
 

代码:

void radix_sort(int arr[], size_t len)
{
	int temp[10][10] = {}; //第二个位置的数字取决于数组大小。
	for (int n = 1; n < 10000; n *= 10)//这个10000由最高位数决定
	{
		memset(temp, -1, sizeof(temp));//每次用完把桶子洗干净,初始化。(初始化为桶里不可能含有的数,0有可能存在于桶里。)
		for (size_t i = 0; i < len; ++i)
		{
			int tempIndex = (arr[i] / n) % 10;//从个位开始每一位的数。
			temp[tempIndex][i] = arr[i];//元素入桶。
		}
		int k = 0;
		//元素出桶。
		for (int x = 0; x < 10; ++x)
		{
			for (int y = 0; y < 10; ++y)
			{
				if (temp[x][y] != -1)
					arr[k++] = temp[x][y];
			}
		}
	}
}

七、计数排序

规则:

开一个初始化为0的内存较大的数组,把要排序的数组里  所有数对应到大数组的相应下标,大数组对应位置的数+1。然后再遍历的大数组。

代码:

int cnt[100000];
void count_sort(int x[], int len)
{
	memset(cnt, 0, sizeof(cnt));//把大数组初始化为0
	for (int i = 0; i < len; i++)
		++cnt[x[i]];//把要排序的数组里 所有数对应到大数组的相应下标,大数组对应位置的数+1
	int top = 0;
	for (int i = 0; i < 100000; i++)//遍历大数组
	{
		while (cnt[i])
		{
			x[top++] = i;
			--cnt[i];
		}
		if (top == len)
			break;
	}
}

八、归并排序

规则:

归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治策略。(分治法将问题分成一些 小的问题然后递归求解,而治的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之。)将已有序的子序列合并,得到完全有序的序列; 即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
归并排序是一种稳定的排序方法。

 

                                 图源:麦克老师讲算法

代码:

void _merge(int x[], int left, int right);//递归的声明
void _unite(int x[], int left, int mid, int right);//合并的声明
void merge_sort(int x[], int n)//算法入口函数
{
	//先递归。
	_merge(x, 0,n-1 );
}
void _merge(int x[], int left, int right)//递归操作。
{
	//递归结束条件 有穷性。 元素只有一个了。
	if (left >= right)
		return;
	int mid = ((right - left) >> 1)+left;//确定中间下标。  >>1的意思是除以2
	_merge(x, left, mid);//递归左区间
	_merge(x, mid + 1, right);//递归右区间
	_unite(x, left, mid, right);
}
void _unite(int x[], int left,int mid, int right)
{
	//每次合并数组长度要求不一致。
	int length=right-left+1;//数组长度
	int* pData = new int[length];//辅助数组
	memset(pData, 0, sizeof(int)*length);//初始化数组
	//游标。分别指向两个需要合并的数组以及辅助数组
	int low = left;//左区间
	int hig = mid + 1;//右区间
	int index = 0;
	//合并
	while (low<=mid &&hig<=right)//两边区间还存在元素
	{   //*****实现排序。
		//左区间存在元素并且当前元素比右区间小。
		while (low <= mid && x[low] <= x[hig]) //允许相等,稳定性。当左边等于右边的时候,左边的还在左边。
			pData[index++] = x[low++];	//while里low<=mid,因为这个循环里面的low值是变化的,不能只在外层while循环里设定low<=mid条件。
		//右区间存在元素并且当前元素比左区间大。
		while (hig<=right && x[low] > x[hig])
			pData[index++] = x[hig++];
	}
	//出循环了,至少有一个区间出完了。
	//剩下区间的元素直接拷贝。
	if (low <= mid)//说明左区间还没有出完
		memmove(&pData[index],&x[low] ,sizeof(int)*(mid-low+1));//memmove(去的地方,来的地方,取出的长度)
	if (hig <= right)//说明右区间没有出完
		memmove(&pData[index], &x[hig], sizeof(int)*(right-hig+ 1));
	memmove(&x[left], &pData[0],sizeof(int)*length);//最后一步 把辅助数组里面的数据覆盖给原数组 还回去 清空辅助数组
	delete[] pData;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值