Ruby操纵数据结构(五)

6 、实现精确的队列

这儿我们定义一个队列,它同我们先前定义的堆栈用同样的方法。如果你想你自己的数据结构不被非法访问,我们建议你完成下面练习( Listing3.14)

Listing 3.14 A Stricter Queue
class Queue

def initialize

@store = [] # 数组

end

def enqueue(x) # 入队,添加到尾部。

@store << x

end

def dequeue
# 出队,从首部移出。

@store.shift

end

def peek
# 检查队首。

@store.first

end

def length
# 队列长度。

@store.length

end

def empty?
# 有元素吗?

@store.empty?

end
end
我们应提一下线程库中的Queue 类,它在线程代码中可以很好地工作。


7 、标记队列举例:仿制交通信号灯

我们在这儿提供了一个使用队列的例子。这个代码模仿到达交通信号灯前的汽车,并在四个队列中存储到达汽车的次数。最后,它打印一些有关队列长度和等待次数的统计表。
数字是单一化的。时间以一秒来划分。没有麻烦的线程;所有车辆以合理的方法连续移动。车辆即不左转也不右转,它们从不越过黄线或者撞红灯,等等。代码在ListIng3.15 中给出。
Listing 3.15 Traffic Light Simulation with a Queue
# 程序:仿制交通信号灯 ( 队列列子)
# 信号灯有它自己的行为:北/ 南绿灯40 秒,暂停2 秒。东/ 西绿灯45 秒,
# 暂停2 秒。重复。
# 交通的行为是这种方式:北行车辆到达交通信号灯,每隔3 秒;
# 南行车辆,每隔5 秒;东行车辆,每隔4 秒;西行车辆,每隔6 秒。
# 所有的时间的大约的( 随机) 。假设没有车辆拐弯。车辆通过信号灯的频率是每秒一辆。
# 让我们运行8900 (100 个循环或多于两小时) 然后回答下面问题:
# 当变成绿灯时,每条线路有多少辆车?平均等待多少秒?最长的等待时间是多# 少秒?


# 直接常量,将方向存入数组。
NORTH, SOUTH, EAST, WEST = 0, 1, 2, 3
dirs = %w[North South East West]


# 每个方向出现车辆的或然率:
p = Array.new(4)
p[NORTH] = 1.0/3.0
p[SOUTH] = 1.0/5.0
p[EAST]
= 1.0/4.0
p[WEST]
= 1.0/6.0


# 队列:队列以数组中的元素形式存储。
waiting = Array.new(4)
waiting[NORTH] = Queue.new
waiting[SOUTH] = Queue.new
waiting[EAST]
= Queue.new
waiting[WEST]
= Queue.new


lengths = [0, 0, 0, 0]
# 变成绿灯时队列多长?
greens
= [0, 0, 0, 0]
# 要多长时间变成绿灯?
times
= [0, 0, 0, 0]
# 车辆等待多少时间?
ncars
= [0, 0, 0, 0]
# 多少车辆通过信号灯?
maxtime = [0, 0, 0, 0]
# 最长的等待时间?

# 循环...
time=0
while time < 8900

change = true
# 信号灯改变

for time in time..time+40
# / 南绿灯40 秒。

# 以随机方式,将所有到达车辆入队。
for dir in NORTH..WEST

waiting[dir].enqueue(time) if rand < p[dir]

end

# 记录队列的长度,元素数量

if change

for dir in NORTH..SOUTH


lengths[dir] += waiting[dir].length

greens[dir] += 1

end

change = false

end

# N/S can leave, one per second...

for dir in NORTH..SOUTH

if !waiting[dir].empty?

car = waiting[dir].dequeue

wait = time - car

ncars[dir] += 1

times[dir] += wait

maxtime[dir] = [maxtime[dir],wait].max

end

end

end

for time in time..time+2
# Yellow/red

# Nothing happens...

end

change = true
# Light changed

for time in time..time+45
# East/west green

# Enqueue all arrivals

for dir in NORTH..WEST

waiting[dir].enqueue(time) if rand < p[dir]

end

# Record queue lengths, counts

if change

for dir in EAST..WEST

lengths[dir] += waiting[dir].length

greens[dir] += 1

end

change = false

end


# N/S can leave, one per second...

for dir in EAST..WEST

if !waiting[dir].empty?

car = waiting[dir].dequeue

wait = time - car

ncars[dir] += 1

times[dir] += wait

maxtime[dir] = [maxtime[dir],wait].max

end

end

end

for time in time..time+2
# Yellow/red

# Nothing happens...

end
end
# Display results...
puts "Average queue lengths:"
for dir in NORTH..WEST

printf "
%-5s %6.1fn", dirs[dir],

lengths[dir]/greens[dir].to_f
end
puts "Max wait times:"
for dir in NORTH..WEST

printf "
%-5s %4dn", dirs[dir],

maxtime[dir]
end
puts "Average wait times:"
for dir in NORTH..WEST

printf "
%-5s %6.1fn", dirs[dir],

times[dir]/ncars[dir].to_f
end
这儿是例子的输出( 由于rand 生成的随机数不同,结果可能有变化)
Average queue lengths:

North
15.6
South
9.5
East
10.8
West
7.3
Max wait times:

North
51

South
47

East
42

West
42
Average wait times:

North
19.5
South
16.2
East
13.7
West
12.9
你马上可看出至少有一打的方式来改进程序。但是,它的目的只是演示队列。








四、用树结构工作I think that I shall never see
A poem as lovely as a tree….
[Alfred] Joyce Kilmer, "Trees"
计算机科学上的树是相对于直觉概念( 它通常用”root” 绘制顶部,用”leaves” 绘制底部) 。因为我们熟悉的层次数据结构与生活中我们每天遇到树,社团的组织表,字典结构等真实存在类似。
树的术语内容丰富,但容易理解。树内的任何条目是节点;第一个或最上层的节点是根。一个节点可以有属于它的很多节点,直接继承者被称为孩子。相反,一个节点也可以有双亲( 只一个) 和祖先。没有孩子的节点被称为叶。一个子树由一个节点和它的所有孩子组成。对树的访问( 例如打印它) 称为遍历树。
我们考查通常的二叉树,尽管实际上一个节点可以有很多孩子。你会看到如何创建树,组装它,访问它。同样,我们也考查一些使用树的任务。
我们应该提一下,在很多语言内,如C Pascal ,树使用真实地址指针来实现。但是,Ruby ( Java) ,我们不使用指针;对象引用工作起来是一样的甚至更好。


1 、实现二叉树

Ruby 中有很多途径可以实现二叉树。例如,我们可以使用数组来存储值。这儿,我们使用更传统的方式,我们使用像C 的代码,除了指针被替换为对象引用。
要描述二叉树必须具备什么?每个节点都需要存储一部分数据的属性。每个节点也需要一对属性,用来引用叶和节点下的右子树。
我们也需要插入树和获取树的信息的途径。会有一对方法来完成这个任务。
我们看看以非正规方式实现的第一棵树。下个例子我们将祥述Tree 类。
感官上,树由插入算法和它的访问方式定义。第一个例子Listing3.16 中,我们定义insert 方法,它以第一分枝风格插入( 也就是说,是从顶到底,从左到右) 。这会保证树的增长会相对缓慢并总是保持平衡。与insert 方法相对应,树的访问迭代器将以同样的第一分枝风格的次序来对树遍历。
Listing 3.16 Breadth-First Insertion and Traversal in a Tree
class Tree
attr_accessor :left
# 叶属性
attr_accessor :right
# 右节点属性
attr_accessor :data
# 节点数据内容属性


# 初始化,可带数据初始初始化。
def initialize(x=nil)

@left = nil

@right = nil

@data = x
end
# 插入方法,参数为节点内容。
def insert(x)

list = []
# 以队列方式临时保存节点。

if @data == nil
# 判断是根节点吗?

@data = x

elsif @left == nil
# 左节点,生成新节点

@left = Tree.new(x)

elsif @right == nil # 右节点,生成新节点。

@right = Tree.new(x)


else
# 有孩子节点。

list << @left

list << @right
# 临时保存左,右孩子。

# 此循环为宽度优先循环,它先对某一层中的左,右节点进行判断,

# 若不为nil ,则入队list 中,直到找到为nil 的节点为止。

# 同层次中先以左为先。

loop do

node = list.shift
# 先遍历左子树。

if node.left == nil
# 生成新节点

node.insert(x)

break

else

list << node.left
# 继续入队,以下次判断。

end

if node.right == nil

node.insert(x)

break

else

list << node.right

end

end

end
end
# 带块的遍历。用块来决定对取出@data 如何操作。
def traverse()

list = []
# 以队列方式临时保存节点。

yield @data
调用块对内容操作。根

list << @left if @left != nil

list << @right if @right != nil

loop do

break if list.empty? # 该节点为空吗?

node = list.shift

yield node.data
调用块对内容操作。叶

list << node.left if node.left != nil
# 获取树的下一层

list << node.right if node.right != nil

end
end
end
items = [1, 2, 3, 4, 5, 6, 7]
tree = Tree.new
items.each { |x| tree.insert(x)}
tree.traverse { |x| print "#{ x}
"}
print "n"
# Prints "1 2 3 4 5 6 7 "
这种树,由插入和遍历算法来定义,不是很有趣。但是,这儿就不介绍了。


2 、二叉树排序

对于随机数据,使用二叉树的最好方式是排序。( 尽管,对于已排序数据,它退化为简单链接列表。) 当然,理由是每次比较什么,我们用除半来选择新节点的存放位置。
尽管现在很少使用它来排序二叉树,但它不会伤害什么。Listing3.17 中的代码构造在上个例子上。
Listing 3.17 Sorting with a Binary Tree
class Tree

# 假设从上个例子开始

def insert(x)

if @data == nil

@data = x

elsif x <= @data

if @left == nil

@left = Tree.new x

else

@left.insert x

end

else

if @right == nil

@right = Tree.new x

else

@right.insert x

end

end

end

def inorder()

@left.inorder { |y| yield y}
if @left != nil

yield @data

@right.inorder { |y| yield y}
if @right != nil

end

def preorder()

yield @data

@left.preorder { |y| yield y}
if @left != nil

@right.preorder { |y| yield y}
if @right != nil

end

def postorder()

@left.postorder { |y| yield y}
if @left != nil

@right.postorder { |y| yield y}
if @right != nil

yield @data

end
end
items = [50, 20, 80, 10, 30, 70, 90, 5, 14,

28, 41, 66, 75, 88, 96]
tree = Tree.new
items.each { |x| tree.insert(x)}
tree.inorder { |x| print x, " "}
print "n"
tree.preorder { |x| print x, " "}
print "n"
tree.postorder { |x| print x, " "}
print "n"
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值