【问题标题】:Counting the max of elements based on stack operations c++基于堆栈操作c ++计算元素的最大值
【发布时间】:2019-10-14 04:13:52
【问题描述】:

给你一堆 N 个整数。在一个操作中,您既可以从堆栈中弹出一个元素,也可以将任何弹出的元素压入堆栈。您需要在执行完 K 次操作后最大化堆栈的顶部元素。如果在执行 K 次操作后堆栈为空,并且没有其他方法使堆栈为非空,则打印 -1。

我动态分配内存然后释放内存。我无法理解为什么我会遇到这个问题。

#include "pch.h"
#include<iostream>
using namespace std;
int main()
{
int n, x, max;
cin >> n;
cin >> x;
int* arr = new int[n];
    //Storing the values from the last, since the first element represents 
    //top of the stack, so the first element would be latest element which 
    //will be pushed into the stack
for (int i = n; i > 0; i--)
{
    cin >> arr[i];
}
max = arr[n];
    //Finding the max by iterating from the last position to the no of 
    //stack operations performed.
for (int i = n; i >= x; i--)
{
    if (arr[i] > max)
    {
        max = arr[i];
    }
}
cout << max;
delete arr;
return 0;
}

输入:

6 4
1 2 4 3 3 5

预期输出:

4

错误:

Error Message : Debug Error ! Program :- Heap Corruption detected : after 
normal block c#2368 at 0x01d21e30. CRT detected that the application wrote 
memory after end of heap buffer.

【问题讨论】:

  • 你为什么不使用std::vector?或者为了这个例子,你可以使用std::stack
  • @foreknownas_463035818 我想到了 std::stack 但我不确定如何遍历它并比较值。
  • 顺便说一句,您的两个循环都从结束迭代到第一个元素(暂时超出范围)。您可以简单地说arr 包含相反顺序的元素,并将两个循环都转换为前向循环。不会对结果产生任何影响,但代码会简单一些

标签: c++ arrays c++11 data-structures stack


【解决方案1】:

您的代码中有几个错误:三个索引错误和一个内存释放错误。首先,在 C++ 中,数组索引总是从 0 开始。所以 n 元素数组的第一个有效索引是 0,最后一个有效索引是 n-1。

1) 由于这些原因,第一个循环应该是这样的:

for (int i = n-1; i >= 0; i--) { ... }

2) 底部元素,你称之为“max”,应该像这样初始化:

max = arr[n-1];

3) 关于第二个循环的相同观察:

for (int i = n-2; i >= x; i--) { ... }

4) 数组的重新分配应使用运算符delete[] 而不是delete 执行。否则,您将有内存泄漏和未定义的行为。 Here您可以找到有关这些运算符的一些附加信息:

delete[] arr;

【讨论】:

    【解决方案2】:

    在大小为 n 的数组中,有效索引是从 0 到 n-1,而不是从 1 到 n

    警告当您使用 new 分配数组时,您必须使用 delete [] 释放它

    我也鼓励你在读取一个值时检查读取是否成功,否则当前容器未设置并且 all 下一次读取将不执行任何操作

    例如从您的代码中:

    #include <iostream>
    
    using namespace std;
    
    int main()
    {
      int n, x, max;
    
      if ((! (cin >> n)) || (n < 1))
        cerr << "invalid size" << endl;
      else if ((! (cin >> x)) || (x < 0) || (x >= n))
        cerr << "invalid min index" << endl;
      else {
        int* arr = new int[n];
        //Storing the values from the last, since the first element represents 
        //top of the stack, so the first element would be latest element which 
        //will be pushed into the stack
        for (int i = n-1; i >= 0; i--)
        {
          if (!(cin >> arr[i])) {
            cerr << "invalid value" << endl;
            return 0;
          }
        }
        max = arr[n-1];
        //Finding the max by iterating from the last position to the no of 
        //stack operations performed.
        for (int i = n-2; i >= x; i--)
        {
          if (arr[i] > max)
            {
              max = arr[i];
            }
        }
        cout << max << endl;
        delete arr; // must be delete [] arr;
      }
      return 0;
    }
    

    事实上,我检查了一个整数是否输入,我还检查了大小/最小索引是否严格为正,并检查了最小索引的有效性

    循环查找arr[n-1]与自身比较的最大值也是没用的,所以第一个考虑的索引是n-2

    从最后一个索引开始填充数组似乎很奇怪

    您使用数组,但也可以使用vector&lt;int&gt;std::vector 非常实用

    编译和执行:

    pi@raspberrypi:/tmp $ g++ -pedantic -Wall -Wextra v.cc
    pi@raspberrypi:/tmp $ ./a.out
    aze
    invalid size
    pi@raspberrypi:/tmp $ ./a.out
    -1
    invalid size
    pi@raspberrypi:/tmp $ ./a.out
    3 qsd
    invalid min index
    pi@raspberrypi:/tmp $ ./a.out
    3 4
    invalid min index
    pi@raspberrypi:/tmp $ ./a.out
    6 4
    1 2 4 3 3 5
    2
    pi@raspberrypi:/tmp $ 
    

    注意结果是 2 而不是 4 因为索引的变化,如果你想让用户索引从 1 开始

    #include<iostream>
    
    using namespace std;
    
    int main()
    {
      int n, x, max;
    
      if ((! (cin >> n)) || (n < 1))
        cerr << "invalid size" << endl;
      else if ((! (cin >> x)) || (x < 1) || (x > n))
        cerr << "invalid min index" << endl;
      else {
        x -= 1;
    
        int * arr = new int[n];
        //Storing the values from the last, since the first element represents 
        //top of the stack, so the first element would be latest element which 
        //will be pushed into the stack
        for (int i = n-1; i >= 0; i--)
        {
          if (!(cin >> arr[i])) {
            cerr << "invalid value" << endl;
            return 0;
          }
        }
        max = arr[n-1];
        //Finding the max by iterating from the last position to the no of 
        //stack operations performed.
        for (int i = n-2; i >= x; i--)
        {
          if (arr[i] > max)
            {
              max = arr[i];
            }
        }
        cout << max << endl;
        delete arr; // must be delete [] arr;
      }
      return 0;
    }
    

    编译和执行:

    pi@raspberrypi:/tmp $ g++ -pedantic -Wall -Wextra v.cc
    pi@raspberrypi:/tmp $ ./a.out
    6 4
    1 2 4 3 3 5
    4
    pi@raspberrypi:/tmp $ 
    

    valgrind下执行提示错误free arr:

    pi@raspberrypi:/tmp $ valgrind ./a.out
    ==3761== Memcheck, a memory error detector
    ==3761== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==3761== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
    ==3761== Command: ./a.out
    ==3761== 
    6 4
    1 2 4 3 3 5
    4
    ==3761== Mismatched free() / delete / delete []
    ==3761==    at 0x48491EC: operator delete(void*) (vg_replace_malloc.c:576)
    ==3761==    by 0x10BE7: main (in /tmp/a.out)
    ==3761==  Address 0x4bcb388 is 0 bytes inside a block of size 24 alloc'd
    ==3761==    at 0x48485F0: operator new[](unsigned int) (vg_replace_malloc.c:417)
    ==3761==    by 0x10A9F: main (in /tmp/a.out)
    ==3761== 
    ==3761== 
    ==3761== HEAP SUMMARY:
    ==3761==     in use at exit: 0 bytes in 0 blocks
    ==3761==   total heap usage: 4 allocs, 4 frees, 22,296 bytes allocated
    ==3761== 
    ==3761== All heap blocks were freed -- no leaks are possible
    ==3761== 
    ==3761== For counts of detected and suppressed errors, rerun with: -v
    ==3761== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 6 from 3)
    pi@raspberrypi:/tmp $ 
    

    【讨论】:

    • delete arr; 应该是delete[] arr;
    • @foreknownas_463035818 哦,是的,我错过了那个变化,谢谢,我编辑了我的答案
    • 非常感谢大家!这个 Valgrind 看起来很棒,我会试一试。
    • @DebabrataPonda 是的 valgrind 是一个很棒的工具,即使你的代码看起来是正确的也可以使用它
    【解决方案3】:

    首先,您需要直截了当地了解需求:

    给你一堆 N 个整数。在一个操作中,您既可以从堆栈中弹出一个元素,也可以将任何弹出的元素压入堆栈。您需要在执行完 K 次操作后最大化堆栈的顶部元素。如果在执行 K 次操作后堆栈为空,并且没有其他方法使堆栈为非空,则打印 -1。

    • 因此,如果 N 为 0,则打印 -1,否则...
    • 如果 K 为 0,则打印 top(),否则...
    • 如果 K 为 1,则必须 pop(),因为您没有要推送的内容,因此如果 N 为 1,则您的堆栈为空,您必须打印 -1,否则打印 top():无论您的第二个顶部元素碰巧是;否则...
    • 如果 x
    • 如果 x == N,请注意,这意味着您仍然不会从堆栈中弹出最终值,因此如果它是最大的元素,那么您就不走运了:您必须将第二大的元素向后推在栈顶 否则...
  • 如果 x > N,您可以弹出所有元素,浪费任何操作,直到您只剩下 1 个操作(如果您有另一个操作浪费弹出,则交替推回任何一个非最大值off),然后使用最后一个操作将最大值推回堆栈;这相当于找到堆栈中的最大值
  • 因此,始终在顶部“x”堆栈元素中找到最小元素的代码无法模拟您应该可用的堆栈操作。

    现在,仅仅因为您了解程序应该返回什么值并不意味着您不应该准确地为操作建模。在我看来,您应该使用堆栈、推送、弹出和顶部操作,并跟踪您弹出的值,以便在必要时将它们推回,您可能需要能够找到最大的我特意把它放回去了。

    要做到这一切,最简单的方法是使用std::stack 数据结构,并说std::multiset 按排序顺序存储弹出的元素(这样您就可以轻松找到最大的元素)。

    代码如下所示。它可以变得更高效/更短,但我会把它留给你。

    #include <iostream>
    #include <set>
    #include <stack>
    
    #define ASSERT(WHAT, MSG) \
        do { \
            if (!(WHAT)) { \
                std::cerr << "ASSERTION FAILURE AT LINE " \
                    << __LINE__ << ": " #WHAT "\n" << MSG << '\n'; \
            } \
        } while (false)
    
    #define DBG(MSG) std::cout << "DBG :" << __LINE__ << " " << MSG << '\n'
    
    // helper functions to be able to stream out containers we're using...
    std::ostream& operator<<(std::ostream& os, const std::multiset<int>& s)
    {
        os << "{ ";
        for (const auto& x : s) std::cout << x << ' ';
        return os << '}';
    }
    
    // note this makes a deep copy of the stack (because "s" is passed by value
    // not by reference), so we can pop all the elements destructively
    std::ostream& operator<<(std::ostream& os, std::stack<int> s)
    {
        os << "{ ";
        while (!s.empty())
        {
            std::cout << s.top() << ' ';
            s.pop();
        }
        return os << '}';
    }
    
    int main()
    {
        std::stack<int> s;
        std::multiset<int> popped;
    
        int n, k;
    
        ASSERT(std::cin >> n >> k, "input must have parseable int values for n and k");
    
        DBG("n " << n << ", k " << k);
        ASSERT(n >= 0, "n must not be negative");
        ASSERT(k >= 0, "k must not be negative");
    
        for (int i = 0; i < n; ++i)
        {
            int x;
            ASSERT(std::cin >> x, "input must have parseable int for value #" << i + 1);
            s.push(x);
        }
    
        DBG("initial stack " << s);
    
        if (n == 0)
            std::cout << -1 << '\n';
        else if (k == 0)
            std::cout << s.top() << '\n';
        else if (k == 1)
        {
            if (n == 1)
                std::cout << -1 << '\n';
            else
            {
                s.pop(); // have to use up our k1 operation as a pop
                std::cout << s.top() << '\n';
            }
        }
        else if (k <= n) // can't pop all the values
        {
            for (int i = 1; i < k; ++i)
            {
                DBG("popping " << s.top());
                popped.insert(s.top()); // add to popped...
                s.pop(); // ...and remove from stack
            }
            DBG("popped k-1 elements (sorted) " << popped);
            DBG("stack " << s);
            // use the last operation to put the largest popped value back
            // (if the stack's has size 2 or more, could pop another one instead -
            //  no way to know which is better, but if the values are random then
            //  the max of all the popped elements is likely to be more than any
            //  given element)
            s.push(*popped.rbegin());  // largest value seen...
            std::cout << s.top() << '\n';
        }
        else // k > n so can pop all the values...
        {
            DBG("k > n");
            while (!s.empty())
            {
                popped.insert(s.top());
                s.pop();
            }
            // waste however many spare operations we have...
            for (int i = 0; i < k - n - 1; ++i)
                if (i % 2 == 0) // on first, third, fifth iteration...
                {
                    DBG("push " << *popped.begin());
                    s.push(*popped.begin());  // push lowest value seen back to stack...
                    popped.erase(popped.begin());  // ...remove it from popped multiset
                }
                else
                {
                    DBG("pop " << s.top());
                    popped.insert(s.top());
                    s.pop();
                }
            DBG("popped elements (sorted) " << popped);
            DBG("stack " << s);
            s.push(*popped.rbegin());  // push largest value...
            std::cout << s.top() << '\n';
        }
    }
    

    【讨论】:

    • 您可能不被“允许”(您的老师/导师)使用std::stackstd::multiset,但如果您不能先​​正确使用它们,那么尝试没有意义在混合使用new[]delete[] 的麻烦中做对。
    猜你喜欢
    • 2021-02-14
    • 2019-01-12
    • 1970-01-01
    • 2020-02-22
    • 1970-01-01
    • 2013-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多