Python 常用排序算法(二)

续写上一篇常用排序算法,本篇将有 二叉树排序,桶排序,位图排序。

二叉树排序:
     
 另一个使用树进行排序的方法,和堆排序不同的是,这种方法需要这正的构建二叉树,而不是使用数组的二叉树形式。它的核心在与构建二叉树时的顺序以及输入二叉树时的顺序。         
具体方法是,依次读取待排序数组的元素,并将其添加为一个二叉树的节点;添加的时候,按值的大小放在节点的左右,如果左右节点已经被占用,则递归到子节点进行添加。二叉树输出的时候,采取前序遍历或者后序遍历的方式输出。
以下是二叉树和它的七种遍历方式,很简单,只是整理出来,方便理解。
class Node(object):
"""节点类"""
def __init__(self, elem=-1, lchild=None, rchild=None):
self.elem = elem
self.lchild = lchild
self.rchild = rchild
class Tree(object):
"""树类"""
def __init__(self):
self.root = Node()
self.myQueue = []
def add(self, elem):
"""为树添加节点"""
node = Node(elem)
if self.root.elem == -1: # 如果树是空的,则对根节点赋值
self.root = node
self.myQueue.append(self.root)
else:
treeNode = self.myQueue[0] # 此结点的子树还没有齐。
if treeNode.lchild == None:
treeNode.lchild = node
self.myQueue.append(treeNode.lchild)
else:
treeNode.rchild = node
self.myQueue.append(treeNode.rchild)
self.myQueue.pop(0) # 如果该结点存在右子树,将此结点丢弃


def front_digui(self, root):
"""利用递归实现树的先序遍历"""
if root == None:
return
print (root.elem,)
self.front_digui(root.lchild)
self.front_digui(root.rchild)


def middle_digui(self, root):
"""利用递归实现树的中序遍历"""
if root == None:
return
self.middle_digui(root.lchild)
print (root.elem,)
self.middle_digui(root.rchild)


def later_digui(self, root):
"""利用递归实现树的后序遍历"""
if root == None:
return
self.later_digui(root.lchild)
self.later_digui(root.rchild)
print (root.elem,)


def front_stack(self, root):
"""利用堆栈实现树的先序遍历"""
if root == None:
return
myStack = []
node = root
while node or myStack:
while node: #从根节点开始,一直找它的左子树
print (node.elem,)
myStack.append(node)
node = node.lchild
node = myStack.pop() #while结束表示当前节点node为空,即前一个节点没有左子树了
node = node.rchild #开始查看它的右子树


def middle_stack(self, root):
"""利用堆栈实现树的中序遍历"""
if root == None:
return
myStack = []
node = root
while node or myStack:
while node: #从根节点开始,一直找它的左子树
myStack.append(node)
node = node.lchild
node = myStack.pop() #while结束表示当前节点node为空,即前一个节点没有左子树了
print( node.elem,)
node = node.rchild #开始查看它的右子树


def later_stack(self, root):
"""利用堆栈实现树的后序遍历"""
if root == None:
return
myStack1 = []
myStack2 = []
node = root
myStack1.append(node)
while myStack1: #这个while循环的功能是找出后序遍历的逆序,存在myStack2里面
node = myStack1.pop()
if node.lchild:
myStack1.append(node.lchild)
if node.rchild:
myStack1.append(node.rchild)
myStack2.append(node)
while myStack2: #将myStack2中的元素出栈,即为后序遍历次序
print (myStack2.pop().elem,)


def level_queue(self, root):
"""利用队列实现树的层次遍历"""
if root == None:
return
myQueue = []
node = root
myQueue.append(node)
while myQueue:
node = myQueue.pop(0)
print (node.elem,)
if node.lchild != None:
myQueue.append(node.lchild)
if node.rchild != None:
myQueue.append(node.rchild)


if __name__ == '__main__':
"""主函数"""
elems = range(10) #生成十个数据作为树节点
tree = Tree() #新建一个树对象
for elem in elems:
tree.add(elem) #逐个添加树的节点

print('\n递归实现先序遍历:', tree.front_digui(tree.root))
print('\n递归实现中序遍历:', tree.middle_digui(tree.root))
print('\n递归实现后序遍历:', tree.later_digui(tree.root))
print('\n堆栈实现先序遍历:', tree.front_stack(tree.root))
print('\n堆栈实现中序遍历:', tree.middle_stack(tree.root))
print('\n堆栈实现后序遍历:', tree.later_stack(tree.root))
print('
\n队列实现层次遍历:', tree.level_queue(tree.root))

我明明写了七种Print 打印出来的结果,不知道为什么在我保存后我自己只看到4种,如有大神知道如何解决,请告知。谢谢
 

桶排序

         桶排序是一种计数排序方法,用标记过号码的桶,去装待排序数组中的数据,数组元素的值对应着桶的编号,最后按桶的标号取出。

 

    具体的方式是,获取待排序数组的最大值,以这个最大值建立数组,并将所有元素置为0,遍历待排序数组,如果元素的值和桶的编号相等,则桶的值自动加一。遍历完毕后,按照桶的编号倒序输入。

def bucketSort(nums):
# 选择一个最大的数
max_num = max(nums)
# 创建一个元素全是0的列表, 当做桶
bucket = [0]*(max_num+1)
# 把所有元素放入桶中, 即把对应元素个数加一
for i in nums:
bucket[i] += 1
# 存储排序好的元素
sort_nums = []
# 取出桶中的元素
for j in range(len(bucket)):
if bucket[j] != 0:
for y in range(bucket[j]):
sort_nums.append(j)

return sort_nums
if __name__ == "__main__":
nums = [5,6,3,2,1,65,2,0,8,1]
nums_1 = [5,6,3,2,1,65,2,0,8,1,-1]
print (bucketSort(nums))
print(bucketSort(nums_1)
上面的例子只可以排列正数,如需要排列负数需要一些修改,请自行实验。这种计数排序的方法并不是用于数序很大的情况,而且数据越紧凑排序效果越好。当然这样的算法还有可以提高的地方,那就是除了找到待排序数组的最大值以外,还可以找到它的最小值,以缩短申请的空间。
但是提高的效果有限。这样的算法对环境要求很高,但是如果满足这样的环境,它的排序效果,非常高效

   当给定的待排序数组没有重复数据,而且数据量非常大,即属于桶排序情况的一种极端特殊的情况,使用桶排序的话,我们需要很大的空间消耗,并且桶中的计数,对于结果意义不大。


  我们可以将其改造和优化,优化的部分就是如果减少空间的消耗,而不关心桶的计数——数量只有0或者1,而不是>1。0或者1的特点,给了我们启发——我们可以使用计算机的位进行存储。这种方法就是位图排序。


 位图排序:

    在桶排序的大思路下,不在使用桶记录数据,而是使用bit位。如果申请一个int的二维数组,每个位置上只放1和0,那么太浪费空间了——在32位的操作系统上,一个Int完全可以存储32位的标记,算法的核心就是如何找到这个位置。为了更好的使用位图的方式,这里我采用位运算进行编写,对应的除和余可以达到同样的操作,但是性能很低

 

posted @ 2018-02-11 17:28  蛋蛋-M  阅读(346)  评论(0编辑  收藏  举报