排序的那些事

Posted Jack Chao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了排序的那些事相关的知识,希望对你有一定的参考价值。

排序的那些事(一)

排序可以说事计算机算法入门的一件事情。各种各样的排序,有的好有的差。
那让我们来看看这些排序的各种魅力吧。
在这里插入图片描述
这张图片是我从网上借鉴过来的。这张图片很好的介绍了所有的基本排序。

直接插入排序

直接插入排序是一种最简单的排序方法,它的基本操作是将一个记录插入到已经排好序的有序表中,从而得到一个新的记录增加1的有序表。也就是选一个放一个。这个的时间复杂都市O(n^2);
但是当我学到后面可以发现,这个O(n^2)比冒泡和选择的要快一点。
话不多说先上代码:

void InsortSort(int* a, int n) //插入排序  a 是数组 , n是数组的大小
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;                   //定义一个变量指向排好数组的最后一个变量。
		int temp = a[end + 1];       // 定义一个变量,这个变量是排好数组的后面一个变量。用来插入到有序数组的变量。
		while (end >= 0)    //这个循环就是整个排序最重要的地方。整个循环就是让我们的还没排序的变量找到自己应该在的地方。
		{
			if (a[end] > temp)  //这边采用的是升序,如果想降序就可以改变这里。
			{
				a[end + 1] = a[end];     //如果没排序的值小于已经排序好数组的最后一个,那么就让排好序的最后一个数字放到后面,让出自己的位置。
				end--;    //end--是为了跟前面的数字依次比较。
			}
			else
			{
				break;
			}
		}
		a[end + 1] = temp;     //将没排序的放到应该在的位置。然后循环继续,将排好序的数组不断扩大。
	}
}

关于直接插入排序的解释大都放到代码那边了。相信文字的力量还是没办法让你理解。在这里插入图片描述

希尔插入排序

希尔排序是对直接插入排序的优化。我们知道,前面这个直接插入排序是一个个比较的。而插入 排序对于接近顺序的数组排序是很快的。那么,希尔就找到了这个突破口。在一个个排序之前先进行预处理。
话不多说,先看代码

//希尔排序,是对直接插入排序的优化
//第一步:预处理,让数组接近有序
//第二步:进行插入排序
void ShellSort(int* a, int n)
{
	int gap = n;    //先定义一个变量,来方便预处理。
	while(gap>1)
	{
		gap /= 2;  //在习惯上,我们会每次缩小两倍来改变变量
		for (int i = 0; i < n - gap; i++)  //这边循环的解释同直接插入排序
		{
			int end = i; 
			int temp = a[end + gap];
			while (end >= 0)
			{
				if (a[end] > temp)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = temp;
		}
	}
}

我们可以看到大部分的代码和直接插入差不多。那么这个希尔排序是怎么实现的呢?
看图:
在这里插入图片描述
这边的d就是代码里面的gap;希尔排序就是多次的直接插入排序。
如果我们按照普通思维,我们会发现,希尔排序的时间花费好像比直接插入排序高,其实不然。希尔排序的时间复杂度是O(log2n);其实希尔排序还有其他的变形,这边只是带大家了解一下,之后会出,关于这些排序代码的详细代码。

选择排序

这个应该是最容易理解的一个排序了。就是从一大堆的数字中找到最大(最小)然后把他们放到最开始或最后面,实现逆序或者顺序。
话不多说,先看代码:

void selectSort(int* a, int n)
{
	int t = 0;  //用于交换的变量
	int i = 0;  //控制趟数的变量
	int j = 0;  //用于元素之间比较的代码
	int flag = 0; //用于标记最大值的下标
	for (i = 0; i < n-1; i++)
	{
		int max = a[i];   //假定一个最大值
		for (j = i+1; j < n; j++)
		{
			if (max < a[j])
			{
				max = a[j];     //如果最大值不是max就改变
				flag = j;
			}
		}
		if (max != a[i])
		{
			t = a[i];
			a[i] = a[flag];   //如果最大值和最开始的值不一样了就交换
			a[flag] = t;
		}
	}
}

这个代码相信大家都有了解,不过我还是找了图给大家加深了解
在这里插入图片描述

堆排序

堆排序是什么呢?那我们先了解堆是什么,在了解堆是什么之前,先了解什么是树。什么是二叉树。这些名词的提出,是不是把大家吓坏了。其实这个很好理解。
在这里插入图片描述
形状像这样的就是二叉树。就是每一个节点下面都有两个节点。每个初始节点就像家长一样,底下两个就是他的孩子
那什么是堆呢?
就是二叉树的一部分
在这里插入图片描述
红圈标识的就是堆。
其实堆排序体现了分治的思想。就是把大问题分解成一个个小问题。然后解决小问题,最后解决大问题。
话不多说
先看代码

void Swap(int* a, int* b)
{
	int t = 0;
	t = *a;           //这个函数用来交换
	*a = *b;
	*b = t;
}
void AdjustDwon(int* a, int n, int root)
{
	int parent = root;    //先确定家长
	int child = parent * 2 + 1;  //利用家长节点求出孩子节点。这个地方的寻找是有公式的。公式就是这个表达式。不过这个求出来的是左孩子,右孩子要加1;
	while (child<n)
	{
		if (child < n-1 && a[child + 1] > a[child]) //这边就是让一个个小堆能有序。
		{
			child++;
		}
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);  
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void HeapSort(int* a, int n)
{
	for (int i = (n - 2) / 2; i >= 0; i--)
	{
		AdjustDwon(a, n, i);
	}
	//排升序建大堆。那么什么是大堆呢?那就是孩子小于家长,右孩子大于左孩子;小堆相反。
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDwon(a, end, 0);   //实现排序的实现。
		--end;
	}
}

堆排序的思想比较抽象,而且要一定的代码储备量;为了更好的理解,我也配上了图:
在这里插入图片描述

冒泡排序

这个排序型如其名。是一个一个跟身边的比较,然后实现排序的。
话不多说,先上代码:

void popSort(int* a, int n)
{
	int i = 0;   //用于控制趟数;
	int j = 0;    //用于控制比较的两个数字
	int t = 0;    //用于交换
	for (i = 0; i < n - 1; i++)
	{
		for (j = 0; j < n - i - 1; j++)
		{
			if (a[j] > a[j + 1])        //用前一个和后一个比较,就像泡泡一样逐步变大。
			{
				t = a[j];  
				a[j] = a[j + 1];    //交换的代码
				a[j + 1] = t;
			}
		}
	}
}

这个代码像直接插入,直接选择一样是比较简单的代码,同样,这三者的时间复杂度类似。都是O(n^2);
图片如下:
在这里插入图片描述

快速排序

快速排序如他的名字一样十分快速。
快到什么地步呢?
代码详见

void QuickSort(int* a, int left ,int right)
{
	if (left >= right)  //判断是不是只有一个元素了
	{
		return;
	}
	int begin = left, end = right;
	int pivot = begin;
	int key = a[begin];   //选出关键值
	while (begin < end)
	{
		while (begin < end && a[end] >= key)  //观察关键值和最后一个值的大小关系。
		{
			end--;
		}
		a[pivot] = a[end];
		pivot = end;
		while (begin < end && a[begin] <= key)  //观察关键值和最开始一个值的大小关系。
		{
			begin++;
		}
		a[pivot] = a[begin];
		pivot = begin;
	}       //以上两个比较都是为了,关键值的左边都是小于他的,右边都是大于他的。
	pivot = begin;
	a[pivot] = key;
	QuickSort(a, left, pivot - 1);    //这边用到了递归和分治的思想。
	QuickSort(a, pivot + 1, right);
}

快速排序的大体就是利用关键值的左边都是小于他的,右边都是大于他的。然后形成升序。
图片:
在这里插入图片描述
快速排序有三种变形。这个我们下次分享。
剩下的两种排序,我将会在下个文章里面介绍一下。
最后感谢大家的阅读,如果有帮到你,那是我的荣幸。谢谢大家的支持。我就怕我哪边没解释清楚,最后误人子弟。如果有不懂的也可以问我。我有空一定回复。最后,让我们一起成长吧!!!!

以上是关于排序的那些事的主要内容,如果未能解决你的问题,请参考以下文章

排序的那些事

SQL开发实战技巧系列:SQL排序的那些事

聊聊视频播放那些事1

MySQL和B+树的那些事&mysql 索引原理

JAVA容器的那些事—集合

SQL开发实战技巧系列:关于SQL不得不说的那些事