这些是非常经典的、非常基础的两个算法,属于数组的知识点,既有可能考编程题又有可能考选择题,问问你,这个比较了几次,某个数变换了几次,最后j是多少,这种问题。因此为了帮助大家巩固这些知识,专门做了这样一个推送。愿各位喜欢。
两个排序算法讲解
冒泡排序算法
先来讲讲基本思想吧,为了把一个无序数组排成升序,我们想的是把小的往前挪,但是不能挪到比它小的前面,大的同理。那么我们就应该依次比较相邻的两个数,把小的调到前面。
具体的过程如下,我将会演示第一轮的排序:
1.我们现在拥有了一个待排序数组{12,36,76,29,8,45}!过会我会用一个记号来表示当前在处理的那个位置,我们不妨叫它 j 。j应该先指向a[0],因为我们会从那里开始。
2.先比较前面两个,很好,12比36小,不用交换,直接把 j 推到下一个位置,也就是a[1]
3.太好了!36还是比76小,不用交换,我们的运气就这么好吗!j 继续向后走,赶紧看看后面要比较什么。
4.不巧,这次76比29大,因此29应该在76前面。那么我们应该把29和76交换,然后j移到 a[3] 的位置
5.现在 j 指向了 a[3] ,76比8大多了,那必然要交换。同样地,j 还要向后走。
6.j到了倒数第二个位置,76比45大,那就交换
7.现在 j 还要往后挪吗?不用了,再挪就最后一位了,上哪比较去?j应当回到0,准备下一轮比较
在了解了思想和过程后,就可以考虑程序实现了
请允许我归纳一下这个算法的实现要求:
要实现依次比较,但一次比较不可能排好序,还要实现多轮比较;
要实现元素的交换;
为了保证效率,不用去比较已经确定排好序的位置,而应直接进入下一轮。
对于第一个要求,我们需要两层循环,内层循环负责一个一个比较过去,就像上面的 j 一样,外层循环实现一轮一轮地比较。对于外层循环,每次比较都会在末尾增加一个“确定的”位置,下一轮不需比较这些位置了,因此有n个数,应当有n-1轮比较,i∈[0,n-1)。
第二个要求很容易实现,三段式交换就行了。
对于第三个要求,每完成一轮循环,就能确定一个位置上的数(从最后一个位置向前积累),直至完成排序。例如有n=6个数,第2轮排序完成后会确定两个位置(第5个和第6个),那么我们最后比较的两个位置应该是第3个(a[2])和第4个,j 到2即可。因此第三轮 j∈ [0,3]。容易推得,有n个数,编号是i的那一轮,j∈[0,n - ( i + 1 ))。
代码是不是呼之欲出了呢!
选择排序算法
冒泡排序算法要比较的次数太多了,选择排序算法可以减少排序时比较的次数,快来看看吧!
基本思想:把这个无序的数组看作一群候选人,而你要给他们安排位置,而且有个顺序。那么很自然的,如果是升序,第一个位置必然给最小的,那就喊出最小的那个坐进来,接着安排第二个,次小的,然后是第三个......直到完成排序。
选择排序算法可以这样表述,依次假设某个位置的数是未排序里最小的,然后去后面找有没有更小的,有的话就交换。
请旋转手机
查看代码与注释
Press
void Bubble_sort(int a[ ], int n){ //数组名做函数形参
int i , j ,t ; //定义三个变量
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} //三段式交换
}
}
}
那么,接下来我会具体展示一下它的大致过程:
1.还是上面那个数组,这次这个 j 表示目前我们要把unsorted中最小数放置的位置。(注意,后面程序里用的是i来实现这个功能,不要弄混了)
2.在a[0]~a[5]间逐个比较,找到最小的那个放到第一位,看到了,是8,交换!然后 j 后移,开始找a[1]
3.此时,a[0]的位置铁定比a[1]小了,我们的搜索范围也要进一步缩小,从a[1]~a[5]间可以很方便地找到12是最小的,然后交换
4.后面就是这个过程的重复了,就不再赘述,直接开始分析怎么实现吧。
请允许我归纳一下这个算法的实现要求:
要逐个处理数组,需要一个变量来控制;要有另一个数负责比较大小。
要实现元素的交换;
要有一种高效率的办法,如果在找那个“最小“的时候频繁交换会造成资源浪费
第一个要求很方便,弄个变量i ,用for循环即可,显然, i ∈[0,n-1],再弄个变量i,从i指向的那个数后面一路比较到n即可
第二个要求更方便,不多讲了。
第三个要求则应该好好分析分析:我们可以采用两种策略,第一种简单粗暴,找到一个比现在那个数小的就马上替换(比如a[0]上是17,我们在后面找到了6,那就马上替换),但是如果有这样一个数组:
{ 17 , 6 , 5 , 4 , 3 , 2 , 1 }
那就麻烦了!为了找到最小的数放在a[0]上,我们将要交换6次!效率够低。
我们还有一种方案,就是记录下最小数的下标p,最后再统一进入三段式交换一次,如果发现比a[p]更小的值就更新p。例如刚刚提到的那个数列,定义一个变量p,先记下第一个比17小的数(6)的下标1,然后发现5比6还小,就把p=2。交换数据要三个语句,而赋值只要一句,大大提高了效率。
按照这个思路,代码可以这样写:
请旋转手机
查看代码与注释
Press
void select_sort(int a[],int n){
int i,j,p,t; //定义四个变量,其中p负责记录最小数的下标
for(i = 0;i //外层循环,一个一个位处理过
p = i; //用当前位的下标初始化
for(j = 0;j < n; j++)
if(a[i] < a[p]) p=j; //寻找更小数的下标
if(p != i) {t = a[j] ;a[j] = a[j+1] ;a[j+1] = t}
//如果找到了更小的就执行三段式交换,这样设计提高效率
}
}
以上就是对两个算法讲解的全部内容,里面有有一些我个人的表达和理解,如果你不太适应,请见谅,也欢迎通过学支对我提出批评。希望这样的讲解风格能够让大家喜欢,谢谢。
END
文字:宣企部
制作:教员李嘉毅
图片:教员部
责任编辑:宣企部