绳索数据结构(字符串快速拼接)

来源:互联网 发布:淘宝宝贝重复铺货 编辑:程序博客网 时间:2024/04/27 18:39

原文地址:Ropes Data Structure (Fast String Concatenation)

字符串连接是一种十分常见的操作。当字符串以传统的方式(例如字符数组)存储的时候,连接操作需要花费O(n)的时间(在这里n是元字符串的长度)。

我们可以利用绳索数据结构减少拼接花费的时间。

绳索数据结构

绳索是一个二叉树,这个二叉树的节点包含除叶子节点以外,节点左边字符的个数。叶子节点包含的是字符串断开的子字符串(子字符串的长度是由用户自己确定的)。

如下图所示:


这里写图片描述

上图说明了字符串在内存中的存储方式。每个叶子节点都包含了原字符串的子字符串,其他所有的节点包含了这个节点左边节点的字符数目。将字符的数目保存到左边的背后思想是查找第i个位置的字符所需要的成本最小化。

优点

  1. 绳索可以很明显地降低拼接的成本。
  2. 与数组不一样,绳索不需要大块连续的内存。
  3. 绳索不需要额外O(n)的空间做插入、删除、查询等操作。
  4. 如果要恢复最后一次拼接,那么可以在O(1)内移除树的根节点。

缺点

  1. 源码的复杂度增加了。
  2. 很容易出现bug。
  3. 存储父节点需要额外的空间。
  4. 访问第i个字符的时间增加了。

现在我们看一种情况,它可以解释为什么绳索法适用于大型字符串数组。

已知两个字符串a[]b[],在c[]中拼接它们。

例子:

Input  : a[] = "This is ", b[] = "an apple"Output : "This is an apple"Input  : a[] = "This is ", b[] = "geeksforgeeks"Output : "This is geeksforgeeks"

方法一(普通法)

我们用字符串c[] 存储拼接后的结果。我们首先遍历a[]然后将a[]中的所有字符全部拷贝到c[] 中,然后再把b[]全部拷到c[] 中。

// Simple C++ program to concatenate two strings#include <iostream>using namespace std;// Function that concatenates strings a[0..n1-1] // and b[0..n2-1] and stores the result in c[]void concatenate(char a[], char b[], char c[],                              int n1, int n2){    // Copy characters of A[] to C[]    int i;    for (i=0; i<n1; i++)        c[i] = a[i];    // Copy characters of B[]    for (int j=0; j<n2; j++)        c[i++] = b[j];    c[i] = '\0';}// Driver codeint main(){    char a[] =  "Hi This is geeksforgeeks. ";    int n1 = sizeof(a)/sizeof(a[0]);    char b[] =  "You are welcome here.";    int n2 = sizeof(b)/sizeof(b[0]);    // Concatenate a[] and b[] and store result    // in c[]    char c[n1 + n2 - 1];    concatenate(a, b, c, n1, n2);    for (int i=0; i<n1+n2-1; i++)        cout << c[i];    return 0;}

输出:

This is geeksforgeeks

时间复杂度:O(n)

下面我们用绳索来解决这个问题。

方法二(绳索法)

绳索法可以在常量时间内将两个字符串拼接在一起。

  1. 创建一个根节点(这个节点存储要拼接的新字符串的根节点)。
  2. 标记这个节点的左孩子,这个字符串的根节点出现在首位。
  3. 标记这个节点的右孩子,这个字符串的根节点出现在第二位。

就是这样的,因为这个方法只要求建立新节点,所以时间复杂度是O(1)

考虑下图:


这里写图片描述

// C++ program to concatenate two strings using// rope data structure.#include <bits/stdc++.h>using namespace std;// Maximum no. of characters to be put in leaf nodesconst int LEAF_LEN = 2;// Rope structureclass Rope{public:    Rope *left, *right, *parent;    char *str;    int lCount;};// Function that creates a Rope structure.// node --> Reference to pointer of current root node//   l  --> Left index of current substring (initially 0)//   r  --> Left index of current substring (initially n-1)//   par --> Parent of current node (Initially NULL)void createRopeStructure(Rope *&node, Rope *par,                         char a[], int l, int r){    Rope *tmp = new Rope();    tmp->left = tmp->right = NULL;    // We put half nodes in left subtree    tmp->lCount = (r-l)/2;    tmp->parent = par;    // If string length is more    if ((r-l) > LEAF_LEN)    {        tmp->str = NULL;        node = tmp;        int m = (l + r)/2;        createRopeStructure(node->left, node, a, l, m);        createRopeStructure(node->right, node, a, m+1, r);    }    else    {        node = tmp;        int j = 0;        tmp->str = new char[LEAF_LEN];        for (int i=l; i<=r; i++)            tmp->str[j++] = a[i];    }}// Function that prints the string (leaf nodes)void printstring(Rope *r){    if (r==NULL)        return;    if (r->left==NULL && r->right==NULL)        cout << r->str;    printstring(r->left);    printstring(r->right);}// Function that efficiently concatenates two strings// with roots root1 and root2 respectively. n1 is size of// string represented by root1.// root3 is going to store root of concatenated Rope.void concatenate(Rope *&root3, Rope *root1, Rope *root2, int n1){    // Create a new Rope node, and make root1     // and root2 as children of tmp.    Rope *tmp = new Rope();    tmp->parent = NULL;    tmp->left = root1;    tmp->right = root2;    root1->parent = root2->parent = tmp;    tmp->lCount = n1;    // Make string of tmp empty and update     // reference r    tmp->str = NULL;    root3 = tmp;}// Driver codeint main(){    // Create a Rope tree for first string    Rope *root1 = NULL;    char a[] =  "Hi This is geeksforgeeks. ";    int n1 = sizeof(a)/sizeof(a[0]);    createRopeStructure(root1, NULL, a, 0, n1-1);    // Create a Rope tree for second string    Rope *root2 = NULL;    char b[] =  "You are welcome here.";    int n2 = sizeof(b)/sizeof(b[0]);    createRopeStructure(root2, NULL, b, 0, n2-1);    // Concatenate the two strings in root3.    Rope *root3 = NULL;    concatenate(root3, root1, root2, n1);    // Print the new concatenated string    printstring(root3);    cout << endl;    return 0;}

输出:

Hi This is geeksforgeeks. You are welcome here.
原创粉丝点击