您的位置:首页 > 理论基础 > 数据结构算法

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

2017-05-24 23:44 309 查看
原文地址:Ropes Data Structure (Fast String Concatenation)

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

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

绳索数据结构

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

如下图所示:



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

优点

绳索可以很明显地降低拼接的成本。

与数组不一样,绳索不需要大块连续的内存。

绳索不需要额外O(n)的空间做插入、删除、查询等操作。

如果要恢复最后一次拼接,那么可以在O(1)内移除树的根节点。

缺点

源码的复杂度增加了。

很容易出现bug。

存储父节点需要额外的空间。

访问第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 code
int 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)

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

方法二(绳索法)

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

创建一个根节点(这个节点存储要拼接的新字符串的根节点)。

标记这个节点的左孩子,这个字符串的根节点出现在首位。

标记这个节点的右孩子,这个字符串的根节点出现在第二位。

就是这样的,因为这个方法只要求建立新节点,所以时间复杂度是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 nodes
const int LEAF_LEN = 2;

// Rope structure
class 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 code
int 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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息