冒泡 / 选择 / 插入排序
小编学习数据结构也有一阵子了,对于萌新而言最重要的两种算法便是排序与查找,小编结合自己所学经历来给大家分享一下,希望能对你有所帮助
???排序
在讲经典的简单排序之前,小编想分享一下初学者们经常会写的一种排序
int main()
{
char arr[5] = {5,2,3,1,4};
for(int i=0; i<5; ++i)
{
for(int j=i+1; j<5; ++j)
{
int temp = 0;
if(arr[j]<arr[i])
{
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
这种写法看似与简单的排序算法有点相像,但是其实有着本质上的区别,上面的例子中第一次排序得到的结果是 1 5 3 2 4
,其中第二小的数字2反而从原先的第二位被换到了倒数第二位,直到i = 5
时才结束循环获得排序后的最终结果。可见这样的排序并不高效!
冒泡排序
冒泡排序是一种交换排序,它的基本思想是:两两比较相邻的关键字,若它们反序则交换
#define swap(a,b) {typeof(a) t=a;a=b;b=a;}
void bubble_sort(int *arr ,size_t len)
{
for(int i = len-1; i>0; --i)
{
bool flag = true;
for(int j = 0; j<i; j++)
{
if(arr[j]>arr[j+1])
{
swap(arr[j],arr[j+1]);
flag = false;
}
}
if(flag) break;
}
}
举数组arr[5] = {5,1,2,3,4}
为例
冒泡排序经过一次循环便得到了排序的结果,并通过标志位flag
直接跳出循环,而且不难发现在冒泡排序的过程中每次都在将小数上移,大数后移。面对部分有序的情况下,冒泡排序可以敏锐的察觉到它的有序性,且在得到最终结果后也能能过标志位flag
及时避免不必要的比较
选择排序
选择排序在我心中其实是对???排序的一种升级,在???排序中,数字在一趟内层循环中可能会不停的发生交换,那我们能不能让它做一个能够抓住机会的数组,在找到合适的位置时才让数据发生交换?这就是选择排序的思想
#define swap(a,b) {typeof(a) t=a;a=b;b=t;}
void select_sort(int *arr, size_t len)
{
for(int i=len-1; i>0; --i)
{
int max = i;
for(int j=0; j<i; ++j)
{
if(arr[j]>arr[max])
max = j;
}
if(i!=max)
swap(arr[max],arr[i]);
}
}
举数组arr[5] = {2,3,4,5,1}
为例
选择排序的比较次数与???排序是一样的,但是每次在比较完后不急着交换数据,正所谓放长线钓大鱼,选择排序等到确认了最后的目标时才进行数据交换
插入排序
插入排序的原理是将一个关键字插入到已经排序好的表中,构成新的有序表。插入排序在依次找到合适的位置时并不急着进行交换,而是选择与选择排序相同的方式记录下标
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#define swap(a,b) {typeof(a) t=a;a=b;b=t;}
void insert_sort(int *arr, size_t len)
{
for(int i=1; i<len-1; ++i)
{
int t = arr[i],k=i;//k用来记录位置
for(int j=i-1; j>=0 && arr[j]>t; j--)
{
arr[j+1] = arr[j];
k=j;
}
arr[k] = t;
}
}
举数组arr[5] = {1,5,2,3,4}
为例
插入排序在在序列本身就大致有序的时候非常简单,并且结合了选择排序的思想使得交换的次数也得到了优化
总结
三种简单的排序算法在函数的样子上十分相似,小编认为重要的是三种排序算法的循环条件,小编在写冒泡排序与选择排序时,外层循环均由数组的最后一位开始,目的是为了让每一次外层循环获得数组中的最大值并放到最后。小编将在后续继续分享更好的排序算法:堆排序、归并排序、快速排序