Python Trie树实现字典排序

时间:2023-08-23 02:06:10 

一般语言都提供了按字典排序的API,比如跟微信公众平台对接时就需要用到字典排序。按字典排序有很多种算法,最容易想到的就是字符串搜索的方式,但这种方式实现起来很麻烦,性能也不太好。Trie树是一种很常用的树结构,它被广泛用于各个方面,比如字符串检索、中文分词、求字符串最长公共前缀和字典排序等等,而且在输入法中也能看到Trie树的身影。
什么是Trie树
Trie树通常又称为字典树、单词查找树或前缀树,是一种用于快速检索的多叉树结构。如图数字的字典是一个10叉树:

Python Trie树实现字典排序

同理小写英文字母或大写英文字母的字典数是一个26叉树。如上图可知,Trie树的根结点是不保存数据的,所有的数据都保存在它的孩子节点中。有字符串go, golang, php, python, perl,它这棵Trie树可如下图所示构造:

Python Trie树实现字典排序

我们来分析下上面这张图。除了根节点外,每个子节点只存储一个字符。go和golang共享go前缀,php、perl和python只共用p前缀。为了实现字典排序,每一层节点上存储的字符都是按照字典排序的方式存储(这跟遍历的方式有关)。我们先来看看对单个字符如何进行字典排序。本文只考虑小写字母,其它方式类似。'a'在'b'的前面,而'a'的ASCII码小于'b'的ASCII码,因此通过它们的ASCII相减就可以得到字典顺序。而且python内置了字典排序的API,比如:

#!/usr/bin/envpython

#coding:utf8

if__name__=='__main__':

 arr=[cforcin'python']
 arr.sort()
 printarr

而且也可以使用我之前的一篇文章介绍的bitmap来实现:Python: 实现bitmap数据结构 。实现代码如下:


#!/usr/bin/envpython
#coding:utf8

classBitmap(object):

 def__init__(self,max):
  self.size=self.calcElemIndex(max,True)
  self.array=[0foriinrange(self.size)]

 defcalcElemIndex(self,num,up=False):
  '''up为True则为向上取整,否则为向下取整'''
  ifup:
   returnint((num+31-1)/31)#向上取整
  returnnum/31

 defcalcBitIndex(self,num):
  returnnum%31

 defset(self,num):
  elemIndex=self.calcElemIndex(num)
  byteIndex=self.calcBitIndex(num)
  elem=self.array[elemIndex]
  self.array[elemIndex]=elem|(1<<byteIndex)

 defclean(self,i):
  elemIndex=self.calcElemIndex(i)
  byteIndex=self.calcBitIndex(i)
  elem=self.array[elemIndex]
  self.array[elemIndex]=elem&(~(1<<byteIndex))

 deftest(self,i):
  elemIndex=self.calcElemIndex(i)
  byteIndex=self.calcBitIndex(i)
  ifself.array[elemIndex]&(1<<byteIndex):
   returnTrue
  returnFalse

if__name__=='__main__':
 MAX=ord('z')
 suffle_array=[cforcin'python']
 result=[]
 bitmap=Bitmap(MAX)
 forcinsuffle_array:
  bitmap.set(ord(c))
 foriinrange(MAX+1):
  ifbitmap.test(i):
   result.append(chr(i))

 print'原始数组为:%s'%suffle_array
 print'排序后的数组为:%s'%result

bitmap的排序不能有重复字符。其实刚才所说的基于ASCII码相减的方式进行字典排序,已经有很多成熟算法了,比如插入排序、希尔排序、冒泡排序和堆排序等等。本文为了图简单,将使用Python自带的sorted方法来进行单字符的字典排序。如果读者自行实现单字符数组的排序也可以,而且这样将可以自定义字符串的排序方式。

实现思路

整个实现包括2个类:Trie类和Node类。Node类表示Trie树中的节点,由Trie类组织成一棵Trie树。我们先来看Node类:


#!/usr/bin/envpython
#coding:utf8

classNode(object):

 def__init__(self,c=None,word=None):
  self.c=c#节点存储的单个字符
  self.word=word#节点存储的词
  self.childs=[]#此节点的子节点

Node包含三个成员变量。c为每个节点上存储的字符。word表示一个完整的词,在本文中指的是一个字符串。childs包含这个节点的所有子节点。既然在每个节点中存储了c,那么存储word有什么用呢?并且这个word应该存在哪个节点上呢?还是用刚才的图举例子:比如go和golang,它们共用go前缀,如果是字符串搜索倒好办,因为会提供原始字符串,只要在这棵Trie树上按照路径搜索即可。但是对于排序来说,不会提供任何输入,所以无法知道单词的边界在哪里,而Node类中的word就是起到单词边界作用。具体是存储在单词的最后一个节点上,如图所示:

Python Trie树实现字典排序

而Node类中的c成员如果这棵树不用于搜索,则可以不定义它,因为在排序中它不是必须的。

接下来我们看看Trie类的定义:

#!/usr/bin/envpython

#coding:utf8

'''Trie树实现字符串数组字典排序'''

classTrie(object):
 def__init__(self):
  self.root=Node()#Trie树root节点引用

 defadd(self,word):
  '''添加字符串'''
  node=self.root
  forcinword:
   pos=self.find(node,c)
   ifpos<0:
    node.childs.append(Node(c))
    #为了图简单,这里直接使用Python内置的sorted来排序
    #pos有问题,因为sort之后的pos会变掉,所以需要再次find来获取真实的pos
    #自定义单字符数组的排序方式可以实现任意规则的字符串数组的排序
    node.childs=sorted(node.childs,key=lambdachild:child.c)
    pos=self.find(node,c)
   node=node.childs[pos]
  node.word=word

 defpreOrder(self,node):
  '''先序输出'''
  results=[]
  ifnode.word:
   results.append(node.word)
  forchildinnode.childs:
   results.extend(self.preOrder(child))
  returnresults

 deffind(self,node,c):
  '''查找字符插入的位置'''
  childs=node.childs
  _len=len(childs)
  if_len==0:
   return-1
  foriinrange(_len):
   ifchilds[i].c==c:
    returni
  return-1

 defsetWords(self,words):
  forwordinwords:
   self.add(word)

Trie包含1个成员变量和4个方法。root用于引用根结点,它不存储具体的数据,但是它拥有子节点。setWords方法用于初始化,调用add方法来初始化Trie树,这种调用是基于每个字符串的。add方法将每个字符添加到子节点,如果存在则共用它并寻找下一个子节点,依此类推。find是用于查找是否已经建立了存储某个字符的子节点,而preOrder是先序获取存储的word。树的遍历方式有三种:先序遍历、中序遍历和后序遍历,如果各位不太明白,可自行Google去了解。接下我们测试一下:

#!/usr/bin/envpython
#coding:utf8

'''Trie树实现字符串数组字典排序'''

classTrie(object):
 def__init__(self):
  self.root=Node()#Trie树root节点引用

 defadd(self,word):
  '''添加字符串'''
  node=self.root
  forcinword:
   pos=self.find(node,c)
   ifpos<0:
    node.childs.append(Node(c))
    #为了图简单,这里直接使用Python内置的sorted来排序
    #pos有问题,因为sort之后的pos会变掉,所以需要再次find来获取真实的pos
    #自定义单字符数组的排序方式可以实现任意规则的字符串数组的排序
    node.childs=sorted(node.childs,key=lambdachild:child.c)
    pos=self.find(node,c)
   node=node.childs[pos]
  node.word=word

 defpreOrder(self,node):
  '''先序输出'''
  results=[]
  ifnode.word:
   results.append(node.word)
  forchildinnode.childs:
   results.extend(self.preOrder(child))
  returnresults

 deffind(self,node,c):
  '''查找字符插入的位置'''
  childs=node.childs
  _len=len(childs)
  if_len==0:
   return-1
  foriinrange(_len):
   ifchilds[i].c==c:
    returni
  return-1

 defsetWords(self,words):
  forwordinwords:
   self.add(word)

classNode(object):
 def__init__(self,c=None,word=None):
  self.c=c#节点存储的单个字符
  self.word=word#节点存储的词
  self.childs=[]#此节点的子节点

if__name__=='__main__':
 words=['python','function','php','food','kiss','perl','goal','go','golang','easy']
 trie=Trie()
 trie.setWords(words)
 result=trie.preOrder(trie.root)
 print'原始字符串数组:%s'%words
 print'Trie树排序后:%s'%result
 words.sort()
 print'Python的sort排序后:%s'%words

结束语

树的种类非常之多。在树结构的实现中,树的遍历是个难点,需要多加练习。上述代码写得比较仓促,没有进行任何优化,但在此基础上可以实现任何方式的字符串排序,以及字符串搜索等。

标签:Python,Trie
0
投稿

猜你喜欢

  • 设计工作者必须了解的常识

    2008-04-06 13:56:00
  • Oracle查看和修改连接数(进程/会话/并发等等)

    2024-01-21 15:59:42
  • 利用Opencv实现图片的油画特效实例

    2022-01-26 14:31:59
  • SQL中from_unixtime函数的使用方法实例

    2024-01-14 12:45:01
  • python回调函数的使用方法

    2023-05-28 02:50:55
  • golang组件swagger生成接口文档实践示例

    2023-09-19 11:29:39
  • ASP实现语音分时问候

    2007-10-02 12:12:00
  • PHP的PDO连接讲解

    2023-06-12 20:40:33
  • python中for循环把字符串或者字典添加到列表的方法

    2022-05-12 19:16:44
  • 基于Python实现一键找出磁盘里所有猫照

    2022-06-20 03:52:32
  • python+pyqt实现右下角弹出框

    2023-09-07 16:04:22
  • 详解Python手写数字识别模型的构建与使用

    2023-10-21 18:34:12
  • js使用eval解析json(js中使用json)

    2024-04-19 10:00:31
  • 必须知道的10个不常用HTML标签[译]

    2009-03-31 13:19:00
  • 实例详解mysql子查询

    2024-01-26 22:22:29
  • asp 静态页面的另一种思路

    2011-04-08 10:32:00
  • SQL中笛卡尔积的实际应用

    2024-01-27 14:18:21
  • Python实现的RSS阅读器实例

    2021-11-21 14:46:49
  • keras K.function获取某层的输出操作

    2023-03-11 15:10:21
  • Python关于抽奖系统的思考与设计思路

    2022-06-09 12:53:36
  • asp之家 网络编程 m.aspxhome.com