简单排序算法
数据结构是科班同学们必学的课程,当然这门课程并不是那么好学,有些同学一点就透,有些同学就需要一段比较长的积累时间,当你在一个点被卡住以后,你没有其他的思路,那么你就会被卡很久,这点相信大家都身有体会,如此就体现出了遇见一篇好文章的重要性。本文主要为三种简单排序做一些比较详细的介绍,后续再对其他排序算法进行介绍。
一、直接插入排序
直接插入排序是一种简单的排序算法,当然有些同学刚刚接触到它会显得尤为复杂。
简单插入排序的具体做法是:在插入第i个记录时,R1、R2……Ri-1已经排好序,这时将关键字ki-1、ki-2等进行比较,从而找到应该插入的位置并将Ri插入,插入位置及其后记录依次向后移动.
当然这是一个比较官方的说法,可能不是特别好理解。我们画一个图以后会比较好理解一些。
这里以升序为例,如下所示
用我的话来简要叙述就是,当第i-1个数大于第i个数,那么我们将第i个数拿出来,将第i个数移动到i-1数的位置上,然后将i-2与第i个数继续比较,如果第i-2个数大于第i个数,那么i继续向下比较,直到大于为止,可能你还觉得不太好理解,那么我们直接上图:
我们以五个数为例子:
第一轮排序:因为我们以升序为例,所以如图,8>7、i>i-1。于是此步骤不做调整
第二轮排序:要记住的是不要自作聪明去比较i-1和i-2,在简单插入排序算法中最重要的是tmp,是临时取出来的值,算法的核心在于把它插入合适的地方。
第三轮排序:25784
第四轮排序:第四轮排序同理,于是结果为降序。
升序代码示例:
基本算法理解了之后我们试着将它转化为代码:
//这是用来输出数组的,照顾一下小白同学
void print_arr(int *arr, int len)
{
for (int i = 0; i < len; i++)
{
printf("%d\t", arr[i]);
}
printf("\n");
}
//传入参数数组,和排序的个数n
void Insertsort(int arr[], int n)
{
int tmp;
int i,j;
for (i = 0; i < n; i++) //外层循环
{
if (arr[i] < arr[i-1])
{
tmp = arr[i]; //当后一个数大于前一个数的时候,保存当前这个数
//arr[i] = arr[i-1]; //这句代码属于优化的一部分,作用在于,当i只用交换一次就可以完成这一轮的循环的时候,
//那么我们就可以避免这一次使用下面这层循环,从而降低一定的时间复杂度,从而进行一定的优化
for (j = i - 1; j >= 0 && tmp < arr[j]; j--) arr[j+1] = arr[j];
//这层循环的作用是比较拿出来的tmp和之前排好序的数组中的数做比较,顺序是从后往前,
//里层循环执行完毕以后,j还进行了一次减一,所以最后tmp插入的位置应该是arr[j+1];
arr[j+1] = tmp;
}
}
}
降序代码示例:
改变一下符号的方向这个很简单,我就直接截图了。
直接插入排序是一种稳定的排序方法,因为用了两层循环,所以时间复杂度为O(n)。在排序过程中只需要一个元素的辅助空间用于元素的交换,空间复杂度为O(1);
二、冒泡排序
void bubbleSort(int arr[], int n)
{
int i, j, tmp;
for (i = 0; i < n - 1; i++) //外层循环控制循环冒泡次数
{
for (j = 0; j < n - i - 1; j++) //内层循环两两冒泡
{
if (arr[j] > arr[j + 1]) //冒出的两个泡相互比较
{
// 交换两个值
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
冒泡排序是大家比较熟知的排序了,所以这边也就直接给代码了。
还是简单说一下如何执行的。
冒泡排序顾名思义需要冒泡,这里的冒泡概念在于冒了两个数出来作比较。
以升序为例,第几轮排序,也就是外层循环i的数值。内层循环也就是j,控制每层冒泡的个数,也就是椭圆的数量,当有五个数的时候每层需要冒泡四次,因此就比较好理解了。
以第一轮排序为例:
5和7比较,5<7,位置不变;
7和8比较,7<8,位置不变;
8和2比较,8>2,交换位置;
因为上一轮8、2交换了位置,因此应当是
8和4比较,8>4,交换位置;
所以第一轮排序结果为:5、7、2、4、8
依次类推:
第二轮排序结果为:5、2、4、7、8
第三轮排序结果为:2、4、5、7、8
这边进行到第三次循环就全部排序成功,因此不需要第四轮排序,所以这里浪费了一次循环的时间复杂度,因为n是最多循环次数,所以这个时间浪费是不可避免的,基于现在的硬件条件来说,这个浪费也可以忽略不计。
冒泡排序是一种稳定的排序方法,因为用了两层循环,所以时间复杂度为O(n)。在排序过程中只需要一个元素的辅助空间用于元素的交换,空间复杂度为O(1);
三、简单选择排序
简单选择排序,主要在于在选择一个合适的数,一个合适的下标。当我们选择升序排列的时候,我们需要选择一个数组中最小的数,将它和第一个数交换,依次类推。当我们选择升序排列的时候,我们需要选择最大的数排在最前方。
思路很简单,如何实现需要详细看代码,如下所示:
void SelectSort(int arr[], int n)
{
int i, j, tmp, k;
for (i = 0; i < n - 1; i++)
{
k = i; //记录最小值
for (j = i + 1; j < n; j++)
{
if (arr[k] > arr[j]) k = j; //因为是升序,所以要选择出最小值
}
if (k != i)
//如果k下标的值改变了,证明k下标这个值被替换为了最小值,于是需要交换两个数的位置
//当然也可以不写这个if条件,但是如果k值未改变,那么依然会进行值交换,
//那么当数据量过大就会影响性能
{
tmp = arr[i];
arr[i] = arr[k];
arr[k] = tmp;
}
}
}
简单选择排序的核心思想在于,里层循环执行完毕以后才会进行一次值交换,这也是它的优势,里层循环的作用便在于找出最值,最大值或者最小值,找出以后再进行交换。
k值在每一次外层循环开始的时候都会重新进行赋值,每次的k值都是i+1,找到最值之后k值被重新赋值,因此最后的值交换直接和k下标的值交换即可。
如果每个算法都画图的话内容量太大,如果还不能理解的话,建议大家去视频网站上去看看教程,教程中一般情况下都会有动态的排序演示,这样便于理解。