数据结构《17》---- 自动补齐之《二》----Ternary Search Tree

本文介绍了如何使用 Ternary Search Tree 来优化 Trie 树在自动补齐功能上的内存占用问题。通过节点定义和数据结构的对比,展示了 Ternary Tree 在内存效率上的优势。在构建包含7000+单词的词典时,Ternary Tree 相比 Trie 树显著减少了内存消耗。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、 序言


上一篇文章中,给出了 trie 树的一个实现。可以看到,trie 树有一个巨大的弊病,内存占用过大。

本文给出另一种数据结构来解决上述问题---- Ternary Search Tree (三叉树)


二、数据结构定义


Trie 树中每个节点包含了 26 个指针,但有很大一部分的指针是 NULL 指针,因此浪费了大量的资源。

一种改进措施就是,以一棵树来代替上述的指针数组。

节点定义如下:

struct Node
{
  char data;
  Node *left, *center, *right;
  bool isStr;
  Node(char x, bool flag)
    : data(x), left(NULL), right(NULL), center(NULL), isStr(flag) {}
};

一个节点代表了一个字母,左孩子的字母小于当前节点,右孩子的字母大于当前节点。

同时每个节点包含一个标记:指出当前节点是否是单词的结尾。

如下图: 

这个图很容易理解错。我详细讲解以下。

首先,根节点是 A, 以 A 为开头的单词都在 中子树中;

左子树表示那些首字母 < A 的单词集合;

中子树表示那些首字母 = A 的单词集合;

右子树表示那些首字母 > A 的单词集合;

黄色表示单词的结尾;

下图中包含以下单词: AB ABCD ABBA BCD


三、与 Trie 树的比较


当建立一个 7000+ 的词典时,

1. Trie 树共消耗了大约 22383 * 27 * 4 BYTE = 2.4 M

2. Ternary Tree 共消耗了 22468 * 14 BYTE = 0.31M


可以看出,在内存占用方面 Ternary Tree 较 Trie 树有着巨大的优势。


四、代码


// Last Update:2014-04-29 22:47:20
/**
 * @file ternary-tree.h
 * @brief ternary tree
 * @author shoulinjun@126.com
 * @version 0.1.00
 * @date 2014-04-22
 */

#ifndef TERNARY-TREE_H
#define TERNARY-TREE_H

#include 
#include 
#include 
using std::string;
using std::cout;
using std::endl;

struct Node
{
  char data;
  Node *left, *center, *right;
  bool isStr;
  Node(char x, bool flag)
    : data(x), left(NULL), right(NULL), center(NULL), isStr(flag) {}
};

class TernaryTree
{
public:
  TernaryTree(): root(NULL){}
  ~TernaryTree() { destroy_tree(root); 
    cout << "count: " << count << endl;}
  void Add(const string &str);
  bool Search(const string &str) const;
  // 打印所以以str开头的单词
  void Dfs(const string &str); 
  void Input(const string &file);

private:
  //not copyable
  TernaryTree(const TernaryTree &);
  TernaryTree& operator=(const TernaryTree&);
  Node *root;
  static size_t count;

  void add(const string &str, size_t pos, Node* &root);
  bool search(const string &str, size_t pos, Node *root) const;
  void dfs(Node *p, string &path); 
  void destroy_tree(Node* &root);
};

size_t TernaryTree::count = 0;

void TernaryTree::Add(const string &str)
{
  add(str, 0, root);
}

bool TernaryTree::Search(const string &str) const
{
  return search(str, 0, root);
}

void TernaryTree::add(const string &str, size_t pos, Node* &root)
{
  if(root == NULL){
    root = new Node(str[pos], false);
    ++ count;
  }

  if(str[pos] < root->data){
    add(str, pos, root->left);
  }
  else if(str[pos] > root->data){
    add(str, pos, root->right);
  }
  else{
    if(pos + 1 == str.size()){
      root->isStr = true;
      return;
    }
    add(str, pos+1, root->center); 
  }
}

bool TernaryTree::search(const string &str, size_t pos, Node *root) const
{
  if(str.empty() || root == NULL) return false;

  if(str[pos] < root->data){
    return search(str, pos, root->left);
  }
  else if(str[pos] > root->data){
    return search(str, pos, root->right);
  }
  else{
    if(pos + 1 == str.size())
      return root->isStr;
    return search(str, pos+1, root->center);
  }
}

void TernaryTree::Dfs(const string &str)
{
  Node *p(root);
  int cur(0);

  while(p && cur != str.size())
  {
    if(p->data == str[cur]){
      p = p->center;
      ++ cur;
    }
    else if(p->data > str[cur]){
      p = p->left;
    }
    else{
      p = p->right;
    }
  }

  string path = str;
  // find the root node 
  dfs(p, path);
}

void TernaryTree::dfs(Node *p, string &path) 
{
  if(p == NULL) return;
  
  // center sub-tree
  path.push_back(p->data);
  if(p->isStr) cout << path << endl;
  dfs(p->center, path);
  path.resize(path.size()-1);

  // left sub-tree
  dfs(p->left, path);
  
  // right sub-tree
  dfs(p->right, path);
}

void TernaryTree::destroy_tree(Node* &root)
{
  if(root == NULL) return;
  destroy_tree(root->left);
  destroy_tree(root->center);
  destroy_tree(root->right);

  delete root;
  root = NULL;
}

void TernaryTree::Input(const string &str)
{
  std::ifstream ifile(str.c_str());

  string word;

  while(ifile >> word)
  {
    Add(word);
  }

  ifile.close();
}

#endif  /*TERNARY-TREE_H*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值