VB程序设计
VB6.0似乎已经成为开发者的往事,但是Microsoft Office系列软件的控制和操作仍然是基于VB6.0的。本项目是为所有学习VB6.0的同学而建立的。
问题1: 排序算法
排序算法是计算机程序设计中最经典的算法,在计算机的专业课《数据结构》中有大量的讨论和分析。我们在这里只讨论最简单的选择排序和冒泡排序。讨论从一个填空题开始。
' 功能:将数组a()中的元素按从小到大排序'
' 参数:数组a()的下标索引为从1到30'
Sub bubble_sort(a() As Integer)
For i = 1 To 29
For j = 【2】
If a(i) > a(j) Then
m = a(i): a(i) = a(j): a(j) = m
End If
Next j
Next i
End Sub
问题:【2】这个地方应该填什么?是 i+1 To 30?还是 1 To 30-i?
- 首先,最正确的姿势是记住!甭管是冒泡还是选择排序,这里j的取值应该与i正好错开1,【2】处应该填:i+1 To 30
- 其次,理解排序的原理。为什么不是:1 To 30-i呢?
首先,我们要明确目标。因为要对数组a()从小到大排序,我们期望:依次扫描a()中的每个元素,每扫描到一个元素a(i),都使得它是剩下的元素(从a(i)到a(30))中最小的。怎么做到呢?可以从a(i)一直扫描到a(30),找到最小的元素a(p),令a(i)和a(p)的值交换(注意:这里不能直接让a(i)=a(p),否则,a(i)的原有值就不见了)。用下面的伪代码表示:
'代码块1
Sub selection_sort(a() As Integer)
For i = 1 To 30
'从a(i) 到 a(30)中找到最小的元素a(p)'
'将a(i)和a(p)的值交换'
Next i
End Sub
那么,怎么从a(i) 到 a(30)中找到最小的元素a(p)呢?可以这样做:
'代码块2
p = a(i) '首先,假设a(i)就是最小的'
For j = i To 30 '从a(i) 扫描到 a(30)'
If p > a(j) Then '一旦发现p比a(j)还大,就让p指向a(j)'
p = a(j)
End If
Next j
上述做法,就是选择排序的思想。我们可以将代码优化下:
首先,j 不必从 i 开始扫描,因为当 j = i 时,a(j)没有必要跟a(p)进行比较(这时,p 也等于 i)。
其次,p 指向a(i)的索引就可以了,没有必要指向a(i)。
'代码块3
p = i ' 假设a(i)就是最小的
For j = i+1 To 30
If a(p) > a(j) Then '一旦发现a(p)比a(j)还大,就让 p 指向 j
p = j
End If
Next j
这时,将代码块3嵌入代码块1,就可以得到完整的选择排序的代码了:
'代码块4
Sub selection_sort(a() As Integer)
For i = 1 To 30
'从a(i) 到 a(30)中找到最小的元素a(p)
p = i '假设a(i)就是最小的
For j = i+1 To 30
If a(p) > a(j) Then '一旦发现a(p)比a(j)还大,就让 p 指向 j
p = j
End If
Next j
'将a(i)和a(p)的值交换(代码略)'
Next i
End Sub
- 经过上面的推演,我们就得到了一个双重循环。反过来,如果直接拿到的就是这样一个双重循环的话,会不会感觉阅读起来很困难?实际上,只要把外层循环 i 固定为某个特定的值来理解内层循环 j 就可以了。
- 再来看冒泡排序法。它与选择排序法最大的不同在于:在其内层循环 j 中,一旦发现a(p)比a(j)还大,就让 a(p) 和 a(j) 的值交换,而 p 是始终指向 i 的(因为a(p)中一直存放最小的值,所以,p就没有必要再移动了)。这样,每经过一次内层循环,就相当于剩下元素中具有最小值的那个元素(具有最小的质量)从水底下浮出来了,因此称为“冒泡排序法”。具体代码如下:
' 代码块5
Sub selection_sort(a() As Integer)
For i = 1 To 30
' 从a(i) 到 a(30)中找到最小的元素a(p)
' 假设a(i)就是最小的,并且始终存放剩余元素中的最小值
For j = i+1 To 30
If a(i) > a(j) Then '一旦发现a(i)比a(j)还大,就让它们交换
m = a(i): a(i) = a(j) : a(j) = m
End If
Next j
Next i
End Sub
- 那么,上面的“选择排序”和“冒泡排序”哪个更优呢?也就是,哪个排序算法的速度更快呢?显然是选择排序。因为冒泡排序在最坏的情况下需要 30-(i+1)+1=30-i 次 a(j) 和 a(i) 的交换,而选择排序只要交换交换 1 次即可。
- 再来看同学的问题,同学尝试将内层循环 j 设置为 1 To 30-i 有什么问题呢?事实上,这样做就意味着:该做的没有做,不该做的却费力去做。第一,该做的没有做。你看,这样做只能找到从a(1)到a(30-i)中的最小值,那a(30-i)之后的元素呢?第二,不该做的却费力去做。为了让 a(i) 为剩下元素中的最小值,还需要做许多冗余的工作:它还要跟它之前的已经比它还小的元素依次比较,这完全是没有必要的,因为比它大的元素,只可能在它后边。
- 练习。如果我们非要将内层循环 j 设置为 1 To 30-i 呢?如果非要这样做,外层循环 i 的设置必须改变,它必须从 a(i) 的最后一个元素倒着走向 a(i) 的第一个元素,以保证:每次a(i)中都存放最大值。这样的程序该如何写?请同学们自行练习。
网友评论