0

この質問に答える次のプログラムを書きました

文字列内の最初の繰り返されない文字を見つける効率的な関数を作成します。たとえば、「total」の最初の繰り返しのない文字は「o」であり、「teeter」の最初の繰り返しのない文字は「r」です。アルゴリズムの効率について話し合います。

これは私がしたことです:

#include <stdio.h>
#include <iostream>
#include <vector>
using namespace std;

class Node
{
public:
    Node::Node(char ch)
    {
        c = ch;
        next = NULL;
    }
    char c;
    Node *next;
};

Node* addNode(Node *tail, char ch)
{
    if(tail == NULL)
        return new Node(ch);
    else
    {
        Node *newN = new Node(ch);
        tail->next = newN;
        return newN;
    }
}

void deleteNode(char ch, Node** head, Node**tail)
{
    Node *prev = NULL;
    Node *cur = *head;

    while(cur!=NULL)
    {
        if(cur->c == ch)
        {
            // found cut it
            if(prev == NULL)
            {
                // head cut off
                if(*tail == *head)
                {
                    // worst possible, just one element
                    delete *head;
                    *head = NULL;
                    return;
                }
                else
                {
                    // Head cut off but not just first element
                    Node *tmp = *head;
                    *head = (*head)->next;
                    delete tmp;
                    return;
                }
            }
            else
            {
                // delete normal node
                if(*tail == cur)
                {
                    // delete tail
                    Node *tmp = *tail;
                    *tail = prev;
                    delete tmp;
                    return;
                }
                else
                {
                    // Normal node not tail
                    prev->next = cur->next;
                    delete cur;
                    return;
                }
            }
        }
        // no match keep searching
        prev = cur;
        cur = cur->next;
    }
}

int  main()
{
    char str[] = "total";
    char htable[26];

    memset(htable, 0, sizeof(char)*26);

    Node *head = NULL;
    Node *tail = head;

    for(unsigned int i=0;;i++)
    {
        if(str[i] == '\0')
            break;

        // check first match
        char m = htable[str[i]-'a'];
        switch(m)
        {
        case 0:
            {
                // first time, add it to linked list
                htable[str[i]-'a']++;
                tail = addNode(tail, str[i]);
                if(head == NULL)
                    head = tail;
            }break;
        case 1:
            {
                // bam, cut it out
                htable[str[i]-'a']++;
                deleteNode(str[i], &head, &tail);
            }break;
        }

    }

    if(head != NULL)
        printf("First char without repetition: %c", head->c);
    else
        printf("No char matched");

    return 0;
}

そしてそれは機能します(ただし、リンクされたリストのプログラムの最後でメモリを解放しませんでした)。基本的に、文字がまだ見つからない場合は 0、一度見つかった場合は 1 (そして末尾の位置でリンクされたリストに追加されます)、少なくとも 2 回出現する場合は 2 のハッシュテーブルを保持します。 (そして、リンクされたリストによって削除する必要があります)。

big-O 表記を使用したこのプログラムの計算の複雑さは?

このアルゴリズムは要素ごとに 1 回渡すだけなので、O(n) だと思いますが、リンクされたリスト内の値を削除するには (最悪の場合)、追加の O(k^2) が必要になります。ここで、k は要素の長さです。アルファベット使用。O(n+k^2) のようなものは私の選択です。文字列が非常に長く、アルファベットが制限されている場合、アルゴリズムは非常に効率的になります。

4

2 に答える 2

1

そうですね、一見これはO(N)複雑に見えますが、ダイナミック アロケーションを使用することで望ましくない非効率性がもたらされました。

ただし、呼び出しdeleteNodeてリストを検索する必要があるため、O(N)複雑さはなくなりました。

次の文字列があるとどうなるか考えてみてください。

abcdefghijklmnopqrstuvwxyzzyxwvutsrqponmlkjihgfedcb

O(N*(N-1)/2)これは、すべてのdeleteNode呼び出しが残りのリストの最後までスキャンする必要があるため、おおまかに複雑です。

実際に行う必要があるのは、文字列を 1 回スキャンして各文字のインデックスを格納し (まだ見つからない場合)、その配列をスキャンして最小のインデックスを見つけることだけです。必要に応じて、インデックス を使用して-1、文字に遭遇していないことを示すことができます。それはの複雑さですO(2N)

于 2013-02-11T23:34:12.263 に答える
1

アルゴリズムの説明から:「すべての文字を読み取るまで、文字が繰り返されるかどうかわからないため、少なくとも O(n) である必要があります。」も参照してください:文字列.

あなたのアルゴリズムでは、リンクされたリストから要素を削除するための O(k^2) の複雑さがわかりません。リンクされたリストからの要素の削除は、O(n) + O(1) = O(n) です。ここで、O(n) は検索、O(1) はノードを見つけた後の消去です。

リンクされたリストには最大 k 個の要素しか含まれないため、削除には O(k) かかります。これは for ループ内にあるため => O(n*k) です。

k は定数 => O(n) の複雑さなので、はるかに簡単な方法で実装できます。繰り返しますが、https://stackoverflow.com/a/2285561/592636 (2 つの配列を使用) を参照してください。

于 2013-02-11T23:46:06.710 に答える