Big O使用堆栈是O(1)吗?

huangapple go评论72阅读模式
英文:

Big O using a stack is it O(1)?

问题

我正在尝试解决一些算法问题。我有一个问题:

编写一个方法reverseWords(),它以字符数组的形式接受一条消息,并原地颠倒单词的顺序。

例如:

char[] message = { 'c', 'a', 'k', 'e', ' ',
            'p', 'o', 'u', 'n', 'd', ' ',
            's', 't', 'e', 'a', 'l' };

结果应该是:

steal pound cake

建议的实现是这样的:

public static void reverseWords(char[] message) {

    // 首先,我们颠倒整个消息数组中的所有字符
    // 这样可以得到正确的单词顺序
    // 但每个单词都是反向的
    reverseCharacters(message, 0, message.length - 1);

    // 现在我们将使单词再次变正向
    // 通过颠倒每个单词的字符

    // 我们保存当前单词的*起始*索引
    // 当查找当前单词的*结束*时
    int currentWordStartIndex = 0;
    for (int i = 0; i <= message.length; i++) {

        // 找到了当前单词的结束!
        if (i == message.length || message[i] == ' ') {

            // 如果我们还没有用尽数组,下一个单词的起始位置是当前位置的后面一个字符
            reverseCharacters(message, currentWordStartIndex, i - 1);
            currentWordStartIndex = i + 1;
        }
    }
}

private static void reverseCharacters(char[] message, int leftIndex, int rightIndex) {

    // 从两边向中间走
    while (leftIndex < rightIndex) {

        // 交换左字符和右字符
        char temp = message[leftIndex];
        message[leftIndex] = message[rightIndex];
        message[rightIndex] = temp;
        leftIndex++;
        rightIndex--;
    }
}

正如您所看到的,它会两次遍历数组,N + N,所以它是O(n)。

但我认为使用堆栈可能是更好的方法,类似于这样:

public static void reverseWords(char[] message) {
        Stack<String> stack = new Stack<>();
        String word = "";

        int i = 0;
        while (i < message.length) {
            if (message[i] != ' ') {
                word = word + message[i];
            }
            else {
                stack.push(word);
                word = "";
            }
            i++;
        }

        stack.push(word);

        int j = 0;
        while (!stack.isEmpty()) {
            String wordStack = "";
            wordStack = stack.pop();
            for (i = 0; i < wordStack.length(); i++) {
                message[j] = wordStack.charAt(i);
                j++;
            }
            if (stack.size() > 0) {
                message[j] = ' ';
                j++;
            }
        }
}

在这种情况下,它只遍历数组一次,并向堆栈插入和移除项目,应该是O(1)。我是说,它也是O(n),但不同之处在于您只遍历数组一次。

您认为呢?我是对的吗?

谢谢。

英文:

I trying to solve some algorithms problems. I have this one:

Write a method reverseWords() that takes a message as an array of characters and reverses the order of the words in place.

For example:

char[] message = { &#39;c&#39;, &#39;a&#39;, &#39;k&#39;, &#39;e&#39;, &#39; &#39;,
            &#39;p&#39;, &#39;o&#39;, &#39;u&#39;, &#39;n&#39;, &#39;d&#39;, &#39; &#39;,
            &#39;s&#39;, &#39;t&#39;, &#39;e&#39;, &#39;a&#39;, &#39;l&#39; };

As a result we should get:

> steal pound cake

The suggested implementation was this one:

public static void reverseWords(char[] message) {

    // first we reverse all the characters in the entire message array
    // this gives us the right word order
    // but with each word backward
    reverseCharacters(message, 0, message.length - 1);

    // now we&#39;ll make the words forward again
    // by reversing each word&#39;s characters

    // we hold the index of the *start* of the current word
    // as we look for the *end* of the current word
    int currentWordStartIndex = 0;
    for (int i = 0; i &lt;= message.length; i++) {

        // found the end of the current word!
        if (i == message.length || message[i] == &#39; &#39;) {

            // if we haven&#39;t exhausted the array, our
            // next word&#39;s start is one character ahead
            reverseCharacters(message, currentWordStartIndex, i - 1);
            currentWordStartIndex = i + 1;
        }
    }
}

private static void reverseCharacters(char[] message, int leftIndex, int rightIndex) {

    // walk towards the middle, from both sides
    while (leftIndex &lt; rightIndex) {

        // swap the left char and right char
        char temp = message[leftIndex];
        message[leftIndex] = message[rightIndex];
        message[rightIndex] = temp;
        leftIndex++;
        rightIndex--;
    }
}

As you can see it goes over the array twice, N + N so it would be O(n)

but I think of using a stack would be a better approach, something like this:

public static void reverseWords(char[] message) {
        Stack&lt;String&gt; stack = new Stack&lt;&gt;();
        String word = &quot;&quot;;

        int i = 0;
        while (i &lt; message.length) {
            if (message[i] != &#39; &#39;) {
                word = word + message[i];
            }
            else {
                stack.push(word);
                word = &quot;&quot;;
            }
            i++;
        }

        stack.push(word);

        int j = 0;
        while (!stack.isEmpty()) {
            String wordStack = &quot;&quot;;
            wordStack = stack.pop();
            for (i = 0; i &lt; wordStack.length(); i++) {
                message[j] = wordStack.charAt(i);
                j++;
            }
            if (stack.size() &gt; 0) {
                message[j] = &#39; &#39;;
                j++;
            }
        }

In this case it goes over the array only one and insert and remove items from the stack and it is supposed to be O(1). I mean it would be also O(n) but the difference is that you go over the array only once.

What do you think? Am I right or not?

Thanks

答案1

得分: 2

你不能在O(1)时间内解决这个任务。

为了颠倒消息中的单词(字符数组),显然需要至少一次完整遍历整个数组,可能还需要多次,正如我们在不同的解决方案中所看到的。

但是你绝对不能在不实际读取完整数组的情况下颠倒所有单词。根据定义,读取数组至末尾是一个O(n)操作,其中n是数组的长度。

当运行时间(或所需的步骤数量)是常数,或者至少不依赖于输入长度时,操作就是O(1)的。一个读取其整个输入的方法不能在输入长度方面是O(1)的。

请注意,“关于...”部分非常重要 - nO(n)中的含义很重要。

例如,在哈希表(或Java中的HashMap)中搜索元素通常被视为O(1)操作,因为它不依赖于地图中已存在的元素数量。
但它确实非常依赖于要搜索的元素的长度 - 需要计算此元素的哈希值,这需要遍历元素的整个长度,因此显然是关于输入长度的O(n)操作。
然而,由于元素的长度通常与整个哈希表的潜在大小相比可以忽略不计,因此这个O(n)是不相关的。并且由于在哈希映射中的搜索确实与地图的大小无关,因此整个事物被视为O(1)。

还要注意,O(1)并不意味着算法对于每个给定的输入都会很快。它只意味着运行时间是常数,而不是低。通常,O(1)算法有很高的固定成本和高的常数因子,这对于大输入是可以接受的,但对于小输入,具有低固定开销的O(n)算法可能更快。

以哈希映射的示例为例,如果地图中有5个元素,而你想检查地图是否包含给定的值,那么遍历所有5个元素要快得多,而不是使用通用算法 - 计算搜索元素的哈希值并使用它来预测它应该存储在地图中的位置。这在地图中有10个或50个元素时也是如此,但在有500万个元素时情况就不同了,在有50亿个元素时情况则截然不同。

英文:

You can't solve this task in O(1).

In order to reverse the words in the message (array of characters), you obviously need to go through the whole array at least once, and probably more than once, as we can see in the various solutions.

But you definitely cannot reverse all the words without actually reading the complete array to the end. Reading the array to the end is an O(n) operation, by definition, with n being the length of the array.

An operation is O(1) when the runtime (or the number of steps required) is constant, or at least does not depend on the length of the input. A method that reads its whole input cannot be O(1) with regard to the length of the input.

Note that the "with regard to..." part is very important - it does matter what the n means in an O(n).

For example, searching for an element in a hash table (or HashMap, in Java terms) is generally regarded to be a O(1) operation, because it does not depend on the number of elements already in the map.
It does however very much depend on the length of the element to be searched - it needs to compute the hash value of this element, which requires going through the whole length of the element, so this is obviously an O(n) operation with regard to the length of the input.
However, since the length of the element itself is usually negligible compared to the potential size of the whole hashmap, this O(n) is irrelevant. And since searching in the hash map is indeed independent of the size of the map, the whole thing is regarded as O(1).

Also note that O(1) does not mean that the algorithm will be fast for every given input. It only means that the runtime is constant, not that it is low. Usually O(1) algorithms have high fixed costs and high constant factors which is acceptable for large inputs, but for small inputs an O(n) algorithm with a low fixed overhead may well be faster.

Taking the hash map example, if there are 5 elements in the map, and you want to check if the map contains a given value, it is much, much faster to go through all 5 elements, instead of using the generic algorithm - calculating the hash of the searched element and using it to predict where it should be stored in the map. This is also true with 10 or 50 elements in the map, but with 5 million, it's a different story, and with 5 billion it's a much different story.

答案2

得分: 1

第一个解决方案应该更快,但它们都是O(n)。

英文:

The first solution is should be faster, but they both in O(n).

huangapple
  • 本文由 发表于 2020年8月14日 09:16:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/63405208.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定