概述
数据结构实际上和算法关系很密切,很多算法的实现都配合数据结构来实现的,但是这不是这次内容,本次仅仅讲述各种数据结构的使用,至于实现,一篇文章根本不够讲。
栈的使用
栈在vb.net中用stack类封装,里面还有泛型栈stack(of T)
什么叫做栈呢?就像装乒乓球的纸筒,每个乒乓球就是栈的元素,先装进去的乒乓球位于筒的底部,后面的位于顶部。
在.net里用push表示把元素入栈,即把乒乓球放入纸筒,用pop方法把元素出栈,即把乒乓球从纸筒中拿出来,由于每次拿乒乓球都是从顶部开始拿的,所以最后进去的最先拿,即后进先出或先进后出,LIFO。
来张图客观点
入栈:
出栈:
示例代码
Sub Main()
Dim TestStack As New Stack(Of String)
Dim teststr As String = "我入栈了"
For i = 0 To teststr.Length - 1
TestStack.Push(teststr(i)) '按teststr字符顺序入栈
Next
Console.WriteLine(teststr) '打印出teststr顺序以作对比
Do While TestStack.Count > 0
Console.Write(TestStack.Pop) '出栈每个字符
Loop
Console.ReadKey()
End Sub
结果如图
可以看到出入栈的顺序和出栈顺序刚好相反。
后进先出有什么好处呢,比如说,你想要搞个撤销操作,你把你的每一步操作都入栈,那么在撤销时出栈,是不是方向相反刚好达到这个目的呢。看下面图效果吧,这个就是用栈做的。
队列的使用
队列(Queue),先进先出,如何理解?想象一下排队的场景即可理解,队列也是达到排队的效果,你排队买东西的时候,是不是先排的先买到先走?所以队列是先进先出的效果,和栈刚好相反。
应用场景:window的消息队列,window在处理各个消息发来时顺序就是按照队列来处理的,你每发送一个消息,系统会将其入队(enqueue),然后再出队(dequeue)处理,处理完就扔了不会一直存在的,所以就有消息循环,不断的发送消息,不断的处理。队列可以使程序更加的有条理。所以假如要按顺序的执行一次性的操作,用队列就可以了。
'处理消息
Private Sub DoWork()
If msgqueue.Count > 0 Then
Console.WriteLine("处理消息:{0}", msgqueue.Dequeue)
End If
End Sub
Dim msgqueue As New Queue(Of Integer) '消息队列
'发送消息
Private Sub SendMessages(ByVal msg As Integer)
Console.WriteLine("发送消息:{0}", msg)
msgqueue.Enqueue(msg)
End Sub
Sub Main()
'处理消息线程
Dim doworkthread As New Thread(Sub()
Do While True
DoWork()
Thread.Sleep(1000) '处理消息比发送消息慢,以查看效果
Loop
End Sub)
'发送10条消息的线程
Dim sendmsgthread As New Thread(Sub()
Dim count As Integer = 1
Do While count <= 10
SendMessages(Int(Rnd() * 2000))
Thread.Sleep(500)
count += 1
Loop
End Sub)
doworkthread.Start()
sendmsgthread.Start()
Console.ReadKey()
End Sub
上面的代码几乎是每两个消息发送处理一个消息,图上的到的结果,先发送了1411、1066,处理消息也是按照顺序1411、1066进行处理,当消息发送完毕后,处理线程将继续检查队列中是否有元素,不断的出队操作进行处理。当处理线程发现队列没有元素后,将处于等待状态,等待其他消息的送入。
数组与列表
数组是一个连续且固定的内存空间,你无法在原有的内存空间上进行扩容,而在vb.net 中使用的redim进行数组的扩容也是开辟新的空间进行的扩容。
代码演示真相:
Imports System.Runtime.InteropServices
Module Module1
Sub Main()
Dim nums(2) As Integer
Dim initialptr = Marshal.UnsafeAddrOfPinnedArrayElement(nums, 0)
Console.WriteLine("初始数组的首地址:{0}", initialptr.ToInt32)
ReDim nums(3)
Dim lastptr As Integer = Marshal.UnsafeAddrOfPinnedArrayElement(nums, 0)
Console.WriteLine("扩容后数组的首地址:{0}", lastptr)
Console.ReadKey()
End Sub
End Module
数组的连续性
Imports System.Runtime.InteropServices
Module Module1
Sub Main()
Dim nums(2) As Integer
For i = 0 To nums.Length - 1
Dim initialptr = Marshal.UnsafeAddrOfPinnedArrayElement(nums, i)
Console.WriteLine("初始数组的第{0}个元素地址:{1}", i, initialptr.ToInt32)
Next
Console.ReadKey()
End Sub
End Module
示例代码演示的是32位整数的数组,32位整数是属于4字节大小的,所以你内存地址偏移量为4,所以三个元素是连续在一起的。正因为连续的,所以访问查找可以通过索引查找,快速。这里和链表有大的差别‘。
频繁的扩容会导致性能的降低,所以vb.net就用列表(arraylist)或泛型列表进行替代,以降低扩容的频率,所以每个列表都会有个初始的容量,而列表的元素每当到达一定的数量时会进行自动的扩容,不需要手动的扩容,但是每次扩容都是按倍数扩容,具体是多少的倍数没留意,java好像是16的倍数扩容的。
这里以泛型列表为例教你使用泛型列表:
泛型列表是列表(arraylist)的升级版,升级在避免装箱拆箱的操作,
不多说废话了,看使用方法:
Module Module1
Sub Main()
'这里演示列表替代字符串类型的二维数组
'列
Dim columlist As List(Of String)
'行
Dim rowlist As New List(Of List(Of String))
'增加 6*5个元素
For i As Integer = 1 To 5
columlist = New List(Of String)
For j As Integer = 1 To 6
columlist.Add("行:" + i.ToString + "列:" + j.ToString) '第i行第j列的元素
Next
rowlist.Add(columlist)
Next
'访问元素
For i As Integer = 0 To 4
For j As Integer = 0 To 5
Console.WriteLine(rowlist(i)(j))
Next
Next
Console.WriteLine(rowlist(1)(3)) '访问第2第4列
rowlist(0).RemoveAt(0) '移除第一行第一列的元素
rowlist.RemoveAt(0) '移除指定位置元素,这里移除一行
rowlist.Clear() '清空列表
Console.ReadKey()
End Sub
End Module
添加后访问效果部分截图
链表的使用
链表是一个具有指针的数据结构但在内存空间中不是连续存在不同于数组,而是通过指针链接在一起的集合,链表有单向链表和双向链表以及环形链表,在vb.net里的链表是双向链表,单向链表顾名思义只有一个方向的链表,即只有一个方向指针的指向,而双向链表有两个方向的指针指向。链表不需要扩容的操作,添加都是添加的内存地址,所以链表在添加和移除上面很快速,但是在查找上面因为内存不是连续的所以查找只能依据指针逐个查找,所以在添加删除操作频繁时而查找少时使用链表更好,来配个图一目了然
单向链表:
在这里我们拥有第一个元素的指针就可以通过第一个元素指向的地址访问后面的元素。知道元素后面带的指针为空的即访问完毕。但是后面的元素没有前面元素的指针,所以后面元素无法访问前面元素。
双向链表:
如图每个元素(除了第一个和最后一个元素外)都能前后访问。这样访问元素比单向链表更为便捷,但是相对的也需要分配内存管理元素指针。
环形链表即是首尾相接。有上面的基础应当不难想象。
演示双向链表的使用
Module Module1
Sub Main()
Dim lk As New LinkedList(Of String)
Dim firstnode = lk.AddFirst("first") '添加第一个
Dim sc As New LinkedListNode(Of String)("second")
lk.AddAfter(firstnode, sc) '在第一个后面添加第二个
lk.AddLast("last")
lk.AddBefore(sc, "secondbefore") '在二前面添加元素
Console.WriteLine("second前面的元素{0},second后面的元素{1}", sc.Previous.Value, sc.Next.Value) '链表的双向访问
lk.Remove(sc.Next) '移除second后面的元素
Dim tempnode As LinkedListNode(Of String) = firstnode
'遍历链表,可通过此查找指定元素。不像数组,想访问哪个元素直接[索引]即可访问
Do While tempnode IsNot Nothing
Console.WriteLine(tempnode.Value)
tempnode = tempnode.Next '指向下一个元素
Loop
Console.ReadKey()
End Sub
End Module
本来还想写个哈希表的,可是写了那么长,想想还是算了,第一次写那么多内容一篇 博客,写了好久啊。。。