백엔드 개발 파이썬 튜토리얼 Python Trie树实现字典排序

Python Trie树实现字典排序

Jun 06, 2016 am 11:29 AM
python

一般语言都提供了按字典排序的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/env python
#coding: utf8

if __name__ == '__main__':
 arr = [c for c in 'python']
 arr.sort()
 print arr

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

代码如下:


#!/usr/bin/env python
#coding: utf8

class Bitmap(object):
 def __init__(self, max):
  self.size  = self.calcElemIndex(max, True)
  self.array = [0 for i in range(self.size)]

 def calcElemIndex(self, num, up=False):
  '''up为True则为向上取整, 否则为向下取整'''
  if up:
   return int((num + 31 - 1) / 31) #向上取整
  return num / 31

 def calcBitIndex(self, num):
  return num % 31

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

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

 def test(self, i):
  elemIndex = self.calcElemIndex(i)
  byteIndex = self.calcBitIndex(i)
  if self.array[elemIndex] & (1    return True
  return False

if __name__ == '__main__':
 MAX = ord('z')
 suffle_array = [c for c in 'python']
 result       = []
 bitmap = Bitmap(MAX)
 for c in suffle_array:
  bitmap.set(ord(c))

 for i in range(MAX + 1):
  if bitmap.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/env python
#coding: utf8

class Node(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/env python
#coding: utf8

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

class Trie(object):
 def __init__(self):
  self.root  = Node() # Trie树root节点引用

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

 def preOrder(self, node):
  '''先序输出'''
  results = []
  if node.word:
   results.append(node.word)
  for child in node.childs:
   results.extend(self.preOrder(child))
  return results

 def find(self, node, c):
  '''查找字符插入的位置'''
  childs = node.childs
  _len   = len(childs)
  if _len == 0:
   return -1
  for i in range(_len):
   if childs[i].c == c:
    return i
  return -1

 def setWords(self, words):
  for word in words:
   self.add(word)

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

代码如下:


#!/usr/bin/env python
#coding: utf8

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

class Trie(object):
 def __init__(self):
  self.root  = Node() # Trie树root节点引用

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

 def preOrder(self, node):
  '''先序输出'''
  results = []
  if node.word:
   results.append(node.word)
  for child in node.childs:
   results.extend(self.preOrder(child))
  return results

 def find(self, node, c):
  '''查找字符插入的位置'''
  childs = node.childs
  _len   = len(childs)
  if _len == 0:
   return -1
  for i in range(_len):
   if childs[i].c == c:
    return i
  return -1

 def setWords(self, words):
  for word in words:
   self.add(word)

class Node(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

结束语

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

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

템플릿의 장점과 단점은 무엇입니까? 템플릿의 장점과 단점은 무엇입니까? May 08, 2024 pm 03:51 PM

템플릿의 장점과 단점은 무엇입니까?

Google AI, 개발자를 위한 Gemini 1.5 Pro 및 Gemma 2 발표 Google AI, 개발자를 위한 Gemini 1.5 Pro 및 Gemma 2 발표 Jul 01, 2024 am 07:22 AM

Google AI, 개발자를 위한 Gemini 1.5 Pro 및 Gemma 2 발표

DeepSeek Xiaomi를 다운로드하는 방법 DeepSeek Xiaomi를 다운로드하는 방법 Feb 19, 2025 pm 05:27 PM

DeepSeek Xiaomi를 다운로드하는 방법

여러 .NET 오픈 소스 AI 및 LLM 관련 프로젝트 프레임워크 공유 여러 .NET 오픈 소스 AI 및 LLM 관련 프로젝트 프레임워크 공유 May 06, 2024 pm 04:43 PM

여러 .NET 오픈 소스 AI 및 LLM 관련 프로젝트 프레임워크 공유

당신은 그에게 Deepseek에게 어떻게 물어 봐요 당신은 그에게 Deepseek에게 어떻게 물어 봐요 Feb 19, 2025 pm 04:42 PM

당신은 그에게 Deepseek에게 어떻게 물어 봐요

평가 기능을 저장하는 방법 평가 기능을 저장하는 방법 May 07, 2024 am 01:09 AM

평가 기능을 저장하는 방법

NET40은 어떤 소프트웨어인가요? NET40은 어떤 소프트웨어인가요? May 10, 2024 am 01:12 AM

NET40은 어떤 소프트웨어인가요?

DeepSeek을 검색하는 방법 DeepSeek을 검색하는 방법 Feb 19, 2025 pm 05:18 PM

DeepSeek을 검색하는 방법

See all articles