插入排序算法及其变例分析
Posted Python算法之旅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了插入排序算法及其变例分析相关的知识,希望对你有一定的参考价值。
本文源于“选考VB算法解析”知识星球一个老师的提问,她说她在做《2019学年第一学期浙南名校联盟第一次联考》第15题时感觉有点怪怪的,尤其是第一空,似乎有点问题,但是又不知道错在哪里。我仔细研究了一下题目,发现这位老师的直觉是很灵敏的,这里确实有一些问题。
本题考查的是插入排序算法的一个变例。插入排序算法在中隐隐出现过,其他各种模拟卷中也多次出现,因此很有必要对其进行深入研究,搞清楚它的基本模型、变例和优化途径,甚至连也有必要有所了解。
2019 学年第一学期浙南名校联盟第一次联考第15题解析(勘误前)
15.小明学了排序和查找算法后,编写了一个处理成绩的程序。单击“获取成绩”按钮得到 n 个非降序数保存在数组 a(1)—a(n)中,并显示在 List1 中。在文本框 Text1 中输入成绩 key,单击“查找”按钮,则在标签 label1 中显示共有多少位同学的成绩大于等于该成绩。
(1)加框1处的程序代码有错,应改为 。
(2)请在划线处填入合适代码:
Dim a(1000) As Integer, n As Integer
Private Sub Command1_Click()
'从数据库获取 n 个成绩存储在 a(1)---a(n)组中,代码略
For i = 2 To n '排序
tmp = a(i)
j = 1
Do While tmp > a(j)'勘误后改为tmp>=a(j)
j = j + 1
If ① Then Exit Do
Loop
For k = i To j + 1 Step -1
a(k) = a(k - 1)
Next k
a(k-1) = tmp '改错 (1)
Next i
For i = 1 To n
List1.AddItem Str(a(i))
Next i
End Sub
Private Sub Command2_Click()
Dim key As Integer, i As Integer, j As Integer, m As Integer
key = Val(Text1.Text)
i = 1: j = n
Do While i <= j
m = (i + j) \ 2
If ② Then
j = m - 1
Else
i = m + 1
End If
Loop
Label1.Caption = "共有" + ③ + "位同学大于等于该成绩。"
End Sub
插入排序及其变例、对分查找算法。要求学生熟练掌握插入排序和对分查找算法的基本思想,并在经典算法的基础上理解其变例。
本题是一道实用性很强的程序题,题目提供的代码总体来说是很精彩的,尤其是使用对分查找算法统计满足条件的同学数量,效率很高。遗憾的是出题老师对插入排序算法的改编不是很成功。
经典的插入排序算法是从右向左,边比较边移动元素,找到不大于tmp的元素后,在其右侧插入tmp,相关代码如下(算法1):
For i = 2 To n
tmp = a(i): j = i - 1
Do While tmp < a(j) ’边比较边移动元素
a(j + 1) = a(j): j = j - 1
If j = 0 Then Exit Do
Loop
a(j + 1) = tmp
Next i
一个常见的变例是先向左扫描找到插入位置,再逐个右移元素,腾出插入位置,最后用tmp覆盖该元素,相关代码如下(算法2):
For i = 2 To n
tmp = a(i): j = i - 1
Do While tmp < a(j) ’向左扫描寻找插入位置,最终j+1指向插入位置
j = j - 1
If j = 0 Then Exit Do
Loop
For k = i - 1 To j + 1 Step -1 ’逐个右移元素,腾出插入位置
a(k + 1) = a(k)
Next k
a(k + 1) = tmp
Next i
向左扫描时,因为j有可能取到0,要考虑数组下标越界的问题,所以循环体中需要加入语句:If j = 0 Then Exit Do。
本题也采用了先查找插入位置,再逐个右移元素,腾出插入位置的方法,与算法2的区别在于扫描方向不同,题目是从左向右扫描查找插入位置。因为tmp = a(i),故j最大取到i,无需考虑下标越界的情况。但出题老师由于思维惯性,还是给出了语句If j = i Then Exit Do,并将其作为问题的第一空,这显然不够妥当。
事实上当j=i时, Do While tmp > a(j)的循环条件不满足,不会执行循环体内的语句,所以第一空所在的语句纯属画蛇添足。我们可以写出更简洁的代码(算法3):
For i = 2 To n
tmp = a(i): j = 1
Do While tmp > a(j) ’向右扫描寻找插入位置,最终j指向插入位置
j = j + 1
Loop
For k = i - 1 To j Step -1 ’逐个右移元素,腾出插入位置
a(k + 1) = a(k)
Next k
a(k + 1) = tmp
Next i
本题使用For k = i To j + 1 Step -1循环来实现右移元素,腾出插入位置功能,当循环结束时,k=j,因为此时j指向插入位置,所以改错题的答案为:a(j) = tmp或者a(k) = tmp。
本题中出现的对分查找算法也和经典的对分查找不一样,它不是查找与key值相等的元素位置,而是查找第一个不小于key值的元素位置,故当a(m)>=key时,需要修改右边界的值j=m-1。因为循环条件是i<= j,所以当循环结束时i=j+1,a(i)就是要找的那个元素,故满足条件的同学数量为n-j或者n-i+1。
(1)a(k)=tmp 或者 a(j) = tmp
(2) ① j=i
② a(m)>=key
③ str(n-j) 或者 str(n-i+1)Dim a(1000) As Integer, n As Integer
'对分查找返回第一个不小于key的元素位置,i和j分别代表待查找区域的左右边界
Function BinarySearch(ByVal key As Integer, ByVal i As Integer, ByVal j As Integer) As Integer
Dim m As Integer
Do While i <= j
m = (i + j) \ 2
If a(m) >= key Then
j = m - 1
Else
i = m + 1
End If
Loop
BinarySearch = ① '返回第一个不小于key的元素位置
End Function
Private Sub Command3_Click()
Dim i As Integer, j As Integer, k As Integer
Dim tmp As Integer
For i = 2 To n '插入排序
tmp = a(i)
j = BinarySearch( ② )
For k = i To j + 1 Step -1
a(k) = a(k - 1)
Next k
a(k) = tmp
Next i
For i = 1 To n
List1.AddItem Str(a(i))
Next i
End Sub
Private Sub Command4_Click()
Dim i As Integer
i = BinarySearch(Val(Text1.Text), 1, n)
Label1.Caption = "共有" + ③ + "位同学大于等于该成绩。"
End Sub
① i
② tmp, 1, i - 1
③ Str(n - i + 1)
为了保证解析的原创性和思维的独特性,我都是独立解题后,先不看答案(除非题目不会做),直接把解析写好,再去看答案。
当然,如果发现参考答案有更好的思路,我还是很乐于学习和借鉴的。同时,由于本人水平有限,解析中难免出现疏漏甚至错误之处,敬请谅解。
无论是赞同还是反对我的看法,都请你给我留言。如果你有新的想法,千万不要憋在心里,请发出来大家一起讨论。让我们相互学习,共同进步!
需要本文word版的,可以加入“选考VB算法解析”知识星球参与讨论和下载文件,“选考VB算法解析”知识星球汇集了数量众多的同好,更多有趣的话题在这里讨论,更多有用的资料在这里分享。
我们专注选考VB算法,感兴趣就一起来!
相关优秀文章:
以上是关于插入排序算法及其变例分析的主要内容,如果未能解决你的问题,请参考以下文章