今天把它发布完,今后这类东东还是发附件痛快些!
三、冒泡排序
也称大数下沉。第一遍的过程:第一、二两个数比较,如果第一个数大,则交换否则不换;第二、三个数比较,如果第二个数大,则交换否则不换;第三、四个数比较,如果第三个数大,则交换否则不换;……最后两个数比较,倒数第二个数大则交换否则不换。这样最大的数就被一步步的换到了最后一个位置。第二遍的过程:与第一遍相同,只是到比较的个数少一个(最后一个不比较,因为他已经是最大的了),这样次大的数就被一步步的换到了倒数第二的位置。以此类推,直到所有的都进行完成则数就排好了。特别是:如果某遍比较过程中,如果没有进行过数据交换,则说明数据已提前排好,可以结束比较了。这一思路的代码如下:
Sub mppx() '冒泡排序1
Dim ar(),fla as boolean
ar = range(“a1:a100”).Value
For i% = 1 To 99
fla = True ‘这一变量用来做标志,初值为真
For k% = 1 To 100-i
If ar(k, 1) >ar(k + 1, 1) Then‘如果需要进行交换
fla = False‘则标志变量为假
tem% = ar(k, 1)‘进行交换
ar(k, 1) = ar(k + 1, 1)
ar(k + 1, 1) = tem
End If
Next
If fla Then Exit For‘如果标志变量为真,说明这一遍循环没有数据进行交换,则退出
Next
[g1:g100] = ar
End Sub
设置了标志变量后,当待排数据大部有序时起到了非常大的作用。但同时,待排数据大部有序时内层循环还是嫌执行多了,为此又有了改进做法:再设置一个变量,用于记录内层循环最后一个进行过数据交换的位置,此位置之后的数据没进行过交换,说明他们都已排好了,为此下一回进行数据比较交换时已排好的就不需要再进行比较了嘛,也就是说循环只需到上回最后一个进行过数据交换的位置就行了。实现这一思想的代码如下:
Sub mppx2() '冒泡排序2
Dim ar(),fla as boolean
ar = range(“a1:a100”).Value
mro% = 99
Do While mro> 1
fla = True
i% = mro
For k% = 1 To i
If ar(k, 1) >ar(k + 1, 1) Then
fla = False
tem% = ar(k, 1)
ar(k, 1) = ar(k + 1, 1)
ar(k + 1, 1) = tem
mro = k ‘记录进行交换的位置,其最后一个值作为下一回for循环的终值
End If
Next
If fla Then mro = 0
Loop
[h1:h100] = ar
End Sub
还有其它很多很多的排序:归并排序、希尔排序、快速排序等等,大家可以去参考相关资料。所有这些排序(包括前面写到的)都各有优缺点,不见得改进的就一定比未改进的好(主要是数据源情况不同时其表现就不同,特定情况下甚至会使改进成为负担)。
所有的这些方法,都是教科书上的,仅供训练思维使用。其实excel自带的排序就非常非常好,如前面的是排a1:a100只需Range("a1:a100").Sort ([a1])就行,而且速度远比前面写的要快很多(据说有能写出比excel自带的排序更快代码的,但是我不能)
前面说了堆积木的种种规则,但那是程序设计的。Excel中的vba必然要与excel打交道,这就有相应的规则在里面,同时还有他所独有的一些积木块,这些都是学VBA所不容回避的。规则我们要记住,但是那些积木块却太多太多,又都是英语的,要全部记住几乎是不可能的(反正我只记住了很少很少的一部份),那怎么办?没关系,有几种方法:录制宏,到那里面去找;查帮助;网上搜索;收录一些文件,如
http://www.excelpx.com/thread-58127-1-4.html就是前人收集在一起的一个较全的集子;等等方式。下面我们就用个实例来看看如何从录制宏中找出我们想要的东西并恰当地运用到我们的程序中去:
实例一:当前excel文件中的sheet1中有形如下图的成绩表,
需要在sheet2中做出如下图所示的成绩条以便打印出来发给学生:
(当然你也可以将它改成单位工资表打印工资条)此问题用函数、技巧我们是可以完成,但学了VBA,我们当然希望用VBA来做。为此我们还是按贯例先来看手工下是如何做的:
第一个人:1. A1:J1合并并居中。 2. 将A3:J4设置所有框线。3.输入数据
第二个人:与第一个人的雷同
第三个人:与第一个人的雷同
……
这不就是循环吗?这我们是有经验的,为此我们只要将第一个人的完成了,在外层增加个循环,就可将所有的人都做完。那么如何来做这第一个学生的呢?下面我们来慢慢完成:
A1:J1合并居中,在手工下就是选中A1:J1,然后点合并居中按钮,现在我们用录制宏将这一手工过程记录下来:
Sub 宏1()
'
' 宏1 宏
'
'Range("A1:J1").Select
With Selection
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlCenter
.WrapText = False
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
.ReadingOrder = xlContext
.MergeCells = False
End With
Selection.Merge
End Sub
简单的一个合并居中所录制的宏代码就是这么一大堆?对不懂鸟文的人来说,V就这么难呀?怕怕(这里面绝大部份鸟文是他不认识我我更不认识他)。
其实他实现的不仅仅是合并居中,因为是录制宏,他要将将方方面面的东西都记录下来,而我们所需要的只是合并居中这么一点。那如何从中找出我们所需要的呢?我是这样干的:
将sheet2中的内容全部删除(不是清除内容,而是选中整张表-------点右键------点删除),这样就将整张sheet恢复到初始状态,在A1中输入任意内容,将VBE窗口缩小并移到合适的位置,只要能看到sheet中的A1:J1就可以了。这时单步执行刚录的宏来观察每一行所起的作用,这时可看到Range("A1:J1").Select是选中单元格区域A1:J1,.HorizontalAlignment = xlCenter是水平居中,如果行高有点高的话还可以明显看到.VerticalAlignment = xlCenter是垂直居中,后面的看不出什么变化,直到Selection.Merge时可看到A1:J1合并了。可见,要合并单元格核心的语句就是Selection.Merge,其它的都是此处所不需要的(注意,仅仅是此处合并所不需要的),为了验证这一想法,我们试试将其它语句都注释掉(或干脆删掉):
Sub 宏1()
Selection.Merge
End Sub
再执行一下看是不是。错了错了,没合并呢?原来是Range("A1:J1").Select没了,仅有Selection.Merge,而当前只选中了一个单元格,一个单元格怎么合并?那我们就任意选中几个单元格,再执行一下看。怎么样?合并了吧?哈哈,找到了,合并就是他:Merge
可是还有点不对的地方:要不就有单元格区域. Select,要不就是当前选中有单元格区域,才能Selection.Merge,可见是先选中再合并,能不能再简单点呢?为此,经试验合并a1:j1的代码就成了:
Sub 宏1()
Range(“A1:j1”).Merge
End Sub
那么长一段代码,最后就整出这么三行,简单吧?就这么简单!
可是,没对,还没对!还有居中呢?居中嘛,再加不就完了?仿合并的:
Sub 宏1()
Range(“A1:j1”).Merge
Range(“A1:j1”).HorizontalAlignment = xlCenter ‘这是水平居中的
Range(“A1:j1”).VerticalAlignment = xlCenter ‘这是垂直居中的
End Sub
如果还要其它功能就再加就是了,看你的需要而定嘛,哈哈
大家去试试通过录制宏,实现整张表格所有内容清除、整张表格内容水平居中和垂直居中、行高25、A1:j1合并、D2:E2合并的功能,我们这里仅给出最终代码:
Sub 宏1()
Cells.clear ‘整张表格所有内容清除
Cells.HorizontalAlignment = xlCenter '整张表格所有内容水平居中
Cells.VerticalAlignment = xlCenter '整张表格所有内容垂直居中
Cells.RowHeight = 25 '整张表格行高为25
Range(“A1:j1”).Merge ‘合并A1:j1
Range(“D2:E2”).Merge ‘合并D2:E2
End Sub
注意:录的宏中其它东东都不是无用的,有兴趣的可以选中鸟文后按f1从帮助中看他们是做何用的。对于所有的这些东西,你能够把他们都记住、能独立写出来固然非常好,但是如果象我一样实在记不住,那也没什么大不了的,只要大至有个印象,到要用的时候任意录制个宏,从宏中能把他们选出来就行了(我就是这样干的,嘿嘿)。
下面我们来给A3:J4设置边框:同样录个宏,得到的代码如下:
Sub 宏2()
'
' 宏2 宏
'
'
Range("A3:J4").Select
Selection.Borders(xlDiagonalDown).LineStyle = xlNone
Selection.Borders(xlDiagonalUp).LineStyle = xlNone
With Selection.Borders(xlEdgeLeft)
.LineStyle = xlContinuous
.ColorIndex = 0
.TintAndShade = 0
.Weight = xlThin
End With
With Selection.Borders(xlEdgeTop)
.LineStyle = xlContinuous
.ColorIndex = 0
.TintAndShade = 0
.Weight = xlThin
End With
With Selection.Borders(xlEdgeBottom)
.LineStyle = xlContinuous
.ColorIndex = 0
.TintAndShade = 0
.Weight = xlThin
End With
With Selection.Borders(xlEdgeRight)
.LineStyle = xlContinuous
.ColorIndex = 0
.TintAndShade = 0
.Weight = xlThin
End With
With Selection.Borders(xlInsideVertical)
.LineStyle = xlContinuous
.ColorIndex = 0
.TintAndShade = 0
.Weight = xlThin
End With
With Selection.Borders(xlInsideHorizontal)
.LineStyle = xlContinuous
.ColorIndex = 0
.TintAndShade = 0
.Weight = xlThin
End With
End Sub
越整越多了,这么长,真晕!别急,有前面的经验,慢慢来:还是先将A3:J4的所有东西都删了,然后一边单步执行代码,一边仔细观察A3:J4的变化。可以看到:
Range("A3:J4").Select与前面的相同,就是选择该区域Selection.Borders(xlDiagonalDown).LineStyle = xlNone 没什么明显变化(查帮助方知是去除斜线),下一句Selection.Borders(xlDiagonalUp).LineStyle = xlNone也一样(斜线有两个方向嘛)后面的六个With语句分别是加左、上、下、右、中间的竖、中间的橫边框,而且加框线就.LineStyle = xlContinuous,另几个是线型颜色什么的。对这个进行精减要稍动点脑筋,在结合帮助的情况下我最终将他整成了(只有一句,下面把他与前面的内容合在一起了):
Sub 宏1()
Cells.clear ‘整张表格所有内容清除
Cells.HorizontalAlignment = xlCenter '整张表格所有内容水平居中
Cells.VerticalAlignment = xlCenter '整张表格所有内容垂直居中
Cells.RowHeight = 25 '整张表格行高为25
Range(“A1:j1”).Merge ‘合并A1:j1
Range(“D2:E2”).Merge ‘合并D2:E2
Range("A3:J4").Borders.LineStyle = xlContinuous ‘设置单元格区域A3:J4的边框为实线
End Sub
下面我们来填数据:
要填数据,得先把数从sheet1中取出来。虽然可以取一个填一个,但这样速度很慢,为速度计我们采取两个措施:
1、先将sheet1中的数据取出来放到一数组中。要用数时就从这个数组中来取
2、学生数据不是直接写入单元格,而是先写入一目的数组中,然后再一次性写入单元格区域
为此我们先定义一个动态数组(dim arr1(),必须是动态数组,要是固定数组的话就不能一次性将数据全放进去了),然后用数组名=单元格区域.value的形式一次性将数据放进数组中。
Sub 宏1()
Dim arr1(), arr2(1 To 4, 1 To 10)
Dim i1%, i2%, l&
arr1 = Sheets("sheet1").UsedRange.Value '将sheet1的所有数据存入数组arr1,这样取来的第一行表头也在里面了
Sheets("sheet2").Select ‘选中sheet2
Cells.Clear '清除当前表格的所有内容(包括格式)
Cells.HorizontalAlignment = xlCenter '整张表格所有内容水平居中
Cells.VerticalAlignment = xlCenter '整张表格所有内容垂直居中
Cells.RowHeight = 25 '整张表格行高为25
'以下生成目的数组。暂时还没有学生信息
arr2(1, 1) = "*学校*学年度下期成绩通知单"
arr2(2, 1) = "姓名"
arr2(2, 3) = "学号"
arr2(2, 8) = "班级"
For i1 = 1 To 10 ‘这个循环用于写科目名称到目的数组的第三行中去
arr2(3, i1) = arr1(1, i1 + 3) '为什么要i1+3呢?
Next
‘以下填入第一个学生的信息到目的数组中去:
arr2(2, 2) = arr1(2, 3) ‘写姓名
arr2(2, 4) = arr1(2, 2) ‘写学号
arr2(2, 9) = arr1(2, 1) ‘写班级
For i1 = 1 To 10 ‘这个循环用于写各科成绩到目的数组的第三行中去
arr2(4, i1) = arr1(2, i1 + 3) '为什么要i1+3呢?
Next
Range("A1:j4") = arr2 '将数据写入表格,这样第一个学生的信息就填好了
'下面来设置格式
Range("A1:j1").Merge '这是合并
Range("d2:e2").Merge '这是合并
Range("A3:J4").Borders.LineStyle = xlContinuous '这是设置边框
End Sub
这样第一个学生的信息就填完整了。单步执行一下,看情况是否如我们所料。
如果是只到这里,那么V就太麻烦了,小小的一个事情整得这么复杂,还不如手工来得爽。但是我们的目的不仅仅是做出第一个学生的,而是要将很多很多的学生都做出来。有了第一个学生的,其他的就变得非常简单:只需我们在外加个循环就成了。现在我们来看如何加循环:
循环应该包括两层意思:循环取数和循环写数。
循环取数,我们已经将数据取到数组arr1中了,在数组arr1中,从第二行起每个学生占一行,固只须从第二行起一行一行地读下去就可以了。可见这是个初值为2、终值为数组arr1最大下标、步长为1的循环。
循环写数,要稍麻烦点,由示例要求得知,在sheet2中是从第一行起,第一个学生占第1至4行,然后空1行;第二个学生占第6至9行,然后空1行……也就是说从第一个学生起每个学生的起始行依次是1、6、11……看出规律了吗?这是个初值为1、步长为5、循环次数与取数相同,是不?
那么如何将这两个整合在一起呢?这里直接给出结果,大家自己体会吧:
Sub 宏1()
Dim arr1(), arr2(1 To 4, 1 To 10)
Dim str1$, i1%, i2%, l&
arr1 = Sheets("sheet1").UsedRange.Value
Sheets("sheet2").Select
Cells.Clear '清除当前表格的所有内容(包括格式)
Cells.HorizontalAlignment = xlCenter '整张表格所有内容水平居中
Cells.VerticalAlignment = xlCenter '整张表格所有内容垂直居中
Cells.RowHeight = 25 '整张表格行高为25
'以下生成目的数组。暂时还没有学生信息
arr2(1, 1) = "*学校*学年度下期成绩通知单"
arr2(2, 1) = "姓名"
arr2(2, 3) = "学号"
arr2(2, 8) = "班级"
For i1 = 1 To 10
arr2(3, i1) = arr1(1, i1 + 3)
Next
l = 1 ‘初值为1,因为第一个学生是从第一行开始填
For i1 = 2 To UBound(arr1) '循环初值从2开始,循环到arr1的最大下标,步长为1.可是这个循环为什么要加在此处呢?能否前后移动呢?
arr2(2, 2) = arr1(i1, 3)
arr2(2, 4) = arr1(i1, 2)
arr2(2, 9) = arr1(i1, 1)
For i2 = 1 To 10
arr2(4, i2) = arr1(i1, i2 + 3)
Next
Range("A" & l & ":j" & l + 3) = arr2 '将数据写入表格,这样第一个学生的信息就填好了,下面来设置格式
Range("A" & l & ":j" & l).Merge '这是合并
Range("d" & l + 1 & ":e" & l + 1).Merge '这是合并
Range("A" & l + 2 & ":j" & l + 3).Borders.LineStyle = xlContinuous '这是设置边框
‘上面为什么有寻么多的&和+呢?结果是什么?可以在上面加一行tmp="A" & l + 2 & ":j" & l + 3并单执行,就可看到"A" & l + 2 & ":j" & l + 3的作用是啥子了,其它雷同
l = l + 5 ‘下一个学生开始的位置
Next
End Sub
这样这个工程就差不多了。收拾下思绪,对这个问题我们再提点东东:
为速度计,其一我们还必须在程序开始位置再加一句:Application.ScreenUpdating = False,作用是关闭屏幕更新,从而达到提速的目的;其二,这个代码数组用得还不够彻底,向表中写数据时,一个学生写一次,在数据量大的情况下速度肯定快不起来,还可以考虑将所有学生的数据全写到一个数组中后再一次性写进表格;其三,每个学生都进行两次合并单元格、一次设置边框,同样可以优化思路,以达到提速的目的。
说这么几点,实际就是提速提速再提速。
不过,这不是这部份内容的主题,这部份内容的主题是录制宏,在帮助的帮助下运用一些手段从录的宏中准确地找出我们所需要的东西并恰当地运用到我们的代码中去,因此只要这个目的达到了就成了。