📜  绳索数据结构(快速字符串串联)

📅  最后修改于: 2021-04-17 09:59:03             🧑  作者: Mango

对字符串最常见的操作之一是附加或串联。追加到一个字符串的末尾时的字符串存储在传统的方式(即一个字符数组)将至少需要O(n)的时间(其中N是原始字符串的长度)。

我们可以减少使用绳索数据结构附加的时间。

绳索数据结构

绳索是一种二叉树结构,其中除叶节点外,每个节点都包含该节点左侧存在的字符数。叶子节点包含实际的字符串,这些字符串分成子字符串(这些子字符串的大小可以由用户确定)。

考虑下图。

Vector_Rope_example.svg

该图显示了字符串如何存储在内存中。每个叶节点都包含原始字符串的子字符串,而所有其他节点都包含该节点左侧出现的字符数。将字符数存储在左侧的想法是最大程度地降低查找第i个位置上存在的字符的成本。

好处
绳索大大减少了增加两根字符串的费用。
2.与数组不同,绳索不需要大量连续的内存分配。
3.绳索不需要O(n)额外的内存来执行诸如插入/删除/搜索之类的操作。
4.如果用户要撤消最后的串联,则可以在O(1)时间内通过删除树的根节点来撤消。

缺点
1.源代码的复杂性增加。
2.出现错误的机会更大。
3.存储父节点需要额外的内存。
4.访问第i个字符增加。

现在让我们看一下一种情况,该情况说明了为什么Ropes可以很好地替代整体字符串数组。
给定两个字符串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"

方法1(天真方法)

我们创建一个字符串c []来存储串联的字符串。我们首先遍历a []并将a []的所有字符复制到c []。然后我们将b []的所有字符复制到c []。

C++
// Simple C++ program to concatenate two strings
#include 
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


Java
//Java program to concatenate two strings
  
class GFG {
  
    // Function that concatenates strings a[0..n1-1] 
    // and b[0..n2-1] and stores the result in c[]
    static 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];
        }
  
    }
  
    // Driver code
    public static void main(String[] args) {
        char a[] = "Hi This is geeksforgeeks. ".toCharArray();
        int n1 = a.length;
  
        char b[] = "You are welcome here.".toCharArray();
        int n2 = b.length;
  
        // Concatenate a[] and b[] and store result
        // in c[]
        char c[] = new char[n1 + n2];
        concatenate(a, b, c, n1, n2);
        for (int i = 0; i < n1 + n2 - 1; i++) {
            System.out.print(c[i]);
        }
  
    }
}
// This code is contributed by PrinciRaj1992


Python3
# Python3 program to concatenate two strings
  
# Function that concatenates strings a[0..n1-1]
# and b[0..n2-1] and stores the result in c[]
def concatenate(a, b, c, n1, n2):
  
    # Copy characters of A[] to C[]
    i = -1
    for i in range(n1):
        c[i] = a[i]
  
    # Copy characters of B[]
    for j in range(n2):
        c[i] = b[j]
        i += 1
  
# Driver Code
if __name__ == "__main__":
    a = "Hi This is geeksforgeeks. "
    n1 = len(a)
  
    b = "You are welcome here."
    n2 = len(b)
  
    a = list(a)
    b = list(b)
  
    # Concatenate a[] and b[] and 
    # store result in c[]
    c = [0] * (n1 + n2 - 1)
  
    concatenate(a, b, c, n1, n2)
  
    for i in c:
        print(i, end = "")
  
# This code is conributed by
# sanjeev2552


C#
// C# program to concatenate two strings
using System; 
  
public class GFG { 
   
    // Function that concatenates strings a[0..n1-1] 
    // and b[0..n2-1] and stores the result in c[]
    static 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];
        }
   
    }
   
    // Driver code
    public static void Main() {
        char []a = "Hi This is geeksforgeeks. ".ToCharArray();
        int n1 = a.Length;
   
        char []b = "You are welcome here.".ToCharArray();
        int n2 = b.Length;
   
        // Concatenate a[] and b[] and store result
        // in c[]
        char []c = new char[n1 + n2];
        concatenate(a, b, c, n1, n2);
        for (int i = 0; i < n1 + n2 - 1; i++) {
            Console.Write(c[i]);
        }
   
    }
}
/*This code is contributed by PrinciRaj1992*/


输出:

Hi This is geeksforgeeks. You are welcome here

时间复杂度: O(n)

现在,让我们尝试使用绳索解决相同的问题。

方法2(绳索构造方法)

该绳索结构可用于在恒定时间内连接两个字符串。
1.创建一个新的根节点(存储新的串联字符串的根)
2.标记该节点的左子节点,即第一个出现的字符串的根。
3.标记此节点的右子节点,即第二个出现的字符串的根。

就是这样。由于此方法仅需要创建一个新节点,因此其复杂度为O(1)

考虑下面的图片(图片来源:https://en.wikipedia.org/wiki/Rope_(data_structure))

Vector_Rope_concat.svg

// C++ program to concatenate two strings using
// rope data structure.
#include 
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  --> Right 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->parent = par;
   
    // If string length is more
    if ((r-l) > LEAF_LEN)
    {
        tmp->str = NULL;
        tmp->lCount = (r-l)/2;
        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;
        tmp->lCount = (r-l);
        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.