【问题标题】:What is the time complexity of this algorithm (Big-O)?这个算法的时间复杂度是多少(Big-O)?
【发布时间】:2023-03-27 21:20:01
【问题描述】:

我仍在尝试理解 BigO 表示法和时间复杂度,但是,我真的不确定这个算法的时间复杂度是多少(我的代码)。

// 03_BeaverConstructions.c
// Created for FIKS on 28/12/2013 by Dominik Hadl
//
// Time complexity: O(N+M)
// Space complexity: O(N)
// ------------------------------------------
// LICENSE (MIT)
// Copyright (c) 2013 Dominik Hadl
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// ------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// ------------------------------------------
// Setup
// ------------------------------------------

#define MAX_PROFILE_LENGTH 10000
#define NO_VALUE -99999

#define BLOCK_VOLUME 1
#define SLOPE_VOLUME 0.5

const char kSlopeRise = '/';
const char kSlopeLower = '\\';
const char kSlopeStay = '_';

// ------------------------------------------
// Structs
// ------------------------------------------

typedef struct
{
    int start_elevation;
    float current_volume;
} Lake;

typedef struct
{
    int location;
    int elevation;
} Peak;

// ------------------------------------------
// Declarations
// ------------------------------------------

int main(int argc, char const *argv[]);
float get_water_volume_of_profile(char const *hill_profile);

// ------------------------------------------
// Main
// ------------------------------------------

int main(int argc, char const *argv[])
{
    // Get the profile
    char hill_profile[MAX_PROFILE_LENGTH + 1];
    fgets(hill_profile, MAX_PROFILE_LENGTH + 1, stdin);

    // Calculate the volume
    float volume = get_water_volume_of_profile(hill_profile);

    // Print it!
    printf("%0.1f\n", volume);

    return 0;
}

// ------------------------------------------
// Calculation
// ------------------------------------------

float get_water_volume_of_profile(char const *hill_profile)
{
    float total_volume = 0; 
    int current_elevation = 0, number_of_peaks = 0, last_peak_index = 0;

    // Get the actual length of the hill profile    
    int profile_length = strlen(hill_profile);

    // Prepare the peaks and lakes in the hill profile
    Peak peaks[profile_length / 2];
    Lake lake = {NO_VALUE, 0};

    // First, get all the peaks
    for (int i = 0; i < profile_length; i++)
    {
        char current_char = hill_profile[i];
        char next_char = hill_profile[i + 1];

        switch (current_char)
        {
            case kSlopeRise:
                current_elevation += 1;
                break;
            case kSlopeLower:
                current_elevation -= 1;
                break;
            case kSlopeStay:
                break;
        }

        if (next_char == '\n')
        {
            peaks[number_of_peaks].location = i + 1;
            peaks[number_of_peaks].elevation = current_elevation;
            number_of_peaks++;
            break;
        }

        if (current_char == kSlopeRise &&
            (next_char == kSlopeLower || next_char == kSlopeStay))
        {
            peaks[number_of_peaks].location = i + 1;
            peaks[number_of_peaks].elevation = current_elevation;
            number_of_peaks++;
        }
    }

    // Now, go through the profile and get the water volume
    current_elevation = 0;
    for (int i = 0; i < profile_length; i++)
    {
        // Get current char and decide what to do
        char current_char = hill_profile[i];
        switch (current_char)
        {
            case kSlopeRise:
            {
                if (lake.start_elevation != NO_VALUE &&
                    lake.start_elevation > current_elevation)
                {
                    lake.current_volume += SLOPE_VOLUME;
                }

                // Increase the elevation
                current_elevation++;

                if (lake.start_elevation == current_elevation)
                {
                    total_volume += lake.current_volume;

                    lake.start_elevation = NO_VALUE;
                    lake.current_volume = 0;
                    break;
                }

                if (lake.start_elevation != NO_VALUE)
                {
                    int elevation_diff = abs(lake.start_elevation - current_elevation);
                    if (elevation_diff > 1)
                    {
                        lake.current_volume += (elevation_diff - 1) * BLOCK_VOLUME;
                    }
                }

                break;
            }
            case kSlopeLower:
            {
                current_elevation--; // Lower the elevation

                // Set elevation where water starts if not already set
                if (lake.start_elevation == NO_VALUE)
                {
                    for (int p = last_peak_index; p < number_of_peaks; p++)
                    {
                        if (peaks[p].elevation >= current_elevation + 1 &&
                            peaks[p].location > i)
                        {
                            lake.start_elevation = current_elevation + 1;
                            last_peak_index = p;
                            break;
                        }
                    }
                    if (lake.start_elevation == NO_VALUE) 
                    {
                        break;
                    }
                }

                lake.current_volume += SLOPE_VOLUME;

                int elevation_diff = abs(lake.start_elevation - current_elevation);
                if (elevation_diff > 1)
                {
                    lake.current_volume += elevation_diff * BLOCK_VOLUME;
                }

                break;
            }
            case kSlopeStay:
            {
                if (lake.start_elevation != NO_VALUE)
                {
                    int elevation_diff = abs(lake.start_elevation - current_elevation);
                    lake.current_volume += elevation_diff * BLOCK_VOLUME;
                }   
                break;
            }
        }
    }

    // Return the total water volume
    return total_volume;
}

我不确定它是否是 O(N),但我不这么认为,因为在第二个 for 循环中有一个嵌套循环。但是,它可能也不是 O(N^2)……更像是 O((N^2)/2)。

有人可以给我建议吗?

【问题讨论】:

  • O((N^2)/2) 的复杂性与O(N^2) 不同
  • @SlaterTyranus O((N^2)/2) 是一个完全合法的复杂性。它正好等于O(N^2)
  • 那是很多代码。为什么不用伪代码总结一下算法呢?
  • @Andrey 公平点,编辑更准确
  • 我很惊讶关于所谓的非法复杂性的评论立即获得了 2 票。

标签: c algorithm time big-o


【解决方案1】:

算法的复杂度为O(n + m),其中n 是输入的大小,m 是“峰值”的数量,无论它们是什么。

原因是核心算法由两个循环大致运行n 次。其中一个循环包含一个内部循环。我们需要计算内部循环的主体执行了多少次。

内部循环在遇到峰值时运行,看起来循环的 body 执行的总次数大致就是您拥有的峰值数。循环嵌套无关紧要:对于复杂度计算,主体的total迭代次数才是最重要的。

(通常嵌套循环的迭代计数是相乘而不是相加,因为它在外循环的每次迭代中都完全执行,但这里不是这种情况。从逻辑上讲,您是从第一个峰值迭代(在内部循环中)到最后;请注意(使用p)在外部循环的迭代之间跟踪break 在内部循环之外的位置,并在返回内部循环时从p 开始。)

【讨论】:

  • 我知道这个问题不是关于空间复杂度的,但是空间复杂度不也是O(n + m)吗?
  • 它会是,除了你的数组的大小静态设置为1000 或其他,所以空间复杂度实际上是O(1)。 (无论给出什么输入,该程序都将使用相同数量的内存。)如果您动态分配数组,使其与给定输入所需的大小完全相同,那么是的,看起来空间复杂度将是 @ 987654330@.
  • 好的,如果我将第一个数组静态分配(hill_profile),然后将第二个(峰值)动态分配给 strlen(hill_profile)/2 ?会是 O(1 + M) 吗?
  • @DominikHadl 是这样的
【解决方案2】:

我错过了什么吗?我宁愿你刚刚提供了伪代码/或必要的代码行,但看起来它只是连续两个 for 循环,第二个 for 循环有一个嵌套循环。那将是 O(N^2),不是吗?随着数据集的增加,时间复杂度应该与数据集的平方成正比……

如果有人想纠正我,我从来没有掌握过这个主题。

编辑--查看评论了解为什么这是错误的..!

【讨论】:

  • 两个循环长度不同。
  • 听起来很接近事实,尽管有必要确定for (int p = last_peak_index; p &lt; number_of_peaks; p++)的输入集大小是否真的是线性的。
  • 这可能不是真的。内部 for 循环基于p,很难判断p 是否以任何有意义的方式基于i。比如p循环独立于i,那么复杂度就只有O(n),否则会更高。
  • 看起来峰值的数量取决于实际输入,而不仅仅是输入的大小。
  • @Jordan 啊,我想知道在阅读了上面的第一条评论之后,你会如何表达这一点^ ..这确实有道理,那么它可能只是 O(n) 吗? (如果你不得不猜测)
猜你喜欢
  • 2016-06-30
  • 2012-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-01
  • 2021-04-30
  • 2015-06-12
  • 1970-01-01
相关资源
最近更新 更多