在C++中实现最小堆时出现问题
Trouble implementing min Heaps in C++
我正在尝试用C++实现一个minheap。然而,以下代码不断引发错误,例如:
heap.cpp:24:4:错误:无法在分配中将">复杂int"转换为"int">
l=2i;
^
heap.cpp:25:4:错误:无法在分配中将">复杂int"转换为"int">
r=2i+1;
^
heap.cpp:在成员函数"int heap::main()"中:
heap.cpp:47:16:错误:没有用于调用"heap::heapify(int[11],int&)"的匹配函数
heapify(a,i);
^
heap.cpp:47:16:注意:候选者是:
heap.cpp:21:5:注意:int heap::heapify(int)
int heapify(int i)//i是父索引,a[]是堆数组
^
heap.cpp:21:5:注意:候选者需要1个参数,提供2个
make:*[heap]错误1
#include <iostream>
using namespace std;
#define HEAPSIZE 10
class Heap
{
int a[HEAPSIZE+1];
Heap()
{
for (j=1;j<(HEAPISZE+1);j++)
{
cin>>a[j];
cout<<"n";
}
}
int heapify(int i) //i is the parent index, a[] is the heap array
{
int l,r,smallest,temp;
l=2i;
r=2i+1;
if (l<11 && a[l]<a[i])
smallest=l;
else
smallest=i;
if (r<11 && a[r]<a[smallest])
smallest=r;
if (smallest != i)
{
temp = a[smallest];
a[smallest] = a[i];
a[i]=temp;
heapify(smallest);
}
}
int main()
{
int i;
for (i=1;i<=HEAPSIZE;i++)
{
heapify(a,i);
}
}
}
最终,这段代码的问题在于,它是由跳过"C++初学者"第1、2和3章的人编写的。让我们从一些基础知识开始。
#include <iostream>
using namespace std;
#define HEAPSIZE 10
在这里,我们包含了用于I/O(输入输出)的C++标头。一个良好的开端。然后,我们发布了一个指令,上面写着"将命名空间std
中的所有内容放入全局命名空间"。这为您节省了一些输入,但也意味着,仔细划分为std::
的数千个内容现在都可能与您想要在代码中使用的名称相冲突。这是一件坏事(TM)。尽量避免这样做。
然后我们继续使用C-ism,一个#define。有时你仍然需要在C++中这样做,但最好避免这样做。我们会回到这个问题上来。
下一个问题,至少在您发布的代码中,是对C++class
的误解。
C++所基于的"C"语言具有struct
的概念,用于描述数据项的集合。
struct
{
int id;
char name[64];
double wage;
};
重要的是要注意语法-尾部的";"。这是因为您可以同时描述结构和声明其类型的变量。
struct { int id; char name[64]; } earner, manager, ceo;
这声明了一个没有类型名称的结构,以及该类型的变量earner
、manager
和ceo
。分号告诉编译器我们何时完成此语句。当你需要在"}"后面加一个分号时,学习需要一段时间;通常你不会,但在结构/类定义中你会这样做。
C++为C添加了很多东西,但一个常见的误解是struct
和class
在某种程度上完全不同。
C++最初扩展了struct
的概念,允许您在结构的上下文中描述函数,允许您将成员/函数描述为private
、protected
或public
,并允许继承。
当您声明struct
时,它默认为public
。一个class
只不过是一个以"私有"开头的struct
。
struct
{
int id;
char name[64];
double wage;
};
class
{
public:
int id;
char name[64];
double wage;
};
由此产生的定义都是相同的。
您的代码没有访问说明符,因此Heap类中的所有内容都是私有的。这导致的第一个也是最有问题的问题是:没有人可以调用任何函数,因为它们是私有的,只能从其他类成员调用。这包括构造函数。
class Foo { Foo () {} };
int main()
{
Foo f;
return 0;
}
上面的代码将无法编译,因为main
不是Foo
的成员,因此不能调用任何private
。
这给我们带来了另一个问题。在您发布的代码中,main
是Foo
的成员。C++程序的入口点是main
,而不是Foo::main
、std::main
或Foo::bar::herp::main
。只是,好的旧int main(int argc, const char* argv[])
或int main()
。
在C中,使用structs,因为C没有成员函数,所以您永远不会直接使用structmembers而不在其前面加上指针或成员引用,例如foo.id
或ptr->wage
。在C++中,在成员函数中,成员变量可以像局部函数变量或参数一样被引用。这可能会导致一些混乱:
class Foo
{
int a, b;
public:
void Set(int a, int b)
{
a = a; // Erh,
b = b; // wat???
}
};
有很多方法可以解决这个问题,但最常见的方法之一是用m_
作为成员变量的前缀。
您的代码与此相冲突,显然C中的原始代码将数组传递给了heapify,并且数组位于局部变量a
中。当您将a
设置为成员时,保持变量名完全相同可以使您不会错过不再需要将其传递给对象的事实(事实上,您的heapify成员函数不再将数组作为指针,从而导致编译错误之一)。
我们遇到的下一个问题,还不是你问题的直接部分,是你的函数Heap()
。首先,它是私有的——您使用了class
,但还没有说public
。但第二,你忽略了这个功能的重要性。
在C++中,每个结构/类都有一个与定义同名的隐含函数。对于CCD_ 37,这将是CCD_。这是"默认构造函数"。每当有人创建不带任何参数的Heap
实例时,就会执行此函数。
这意味着它将在编译器创建短期临时堆时调用,或者在创建堆()的向量并分配新的临时堆时被调用。
这些功能有一个目的:准备对象占用的存储空间以供使用。你应该尽量避免其他工作,直到晚些时候。使用std::cin
在构造函数中填充成员是你能做的最糟糕的事情之一
我们现在有了一个基础,可以开始以一种可行的方式编写代码的外壳。
最后一个更改是用类枚举替换"HEAPSIZE"。这是封装的一部分。您可以将HEAPSIZE
保留为#define
,但您应该在类中公开它,这样外部代码就不必依赖它,而是可以说Heap::Size
或heapInstance.size()
等。
#include <iostream>
#include <cstdint> // for size_t etc
#include <array> // C++11 encapsulation for arrays.
struct Heap // Because we want to start 'public' not 'private'.
{
enum { Size = 10 };
private:
std::array<int, Size> m_array; // meaningful names ftw.
public:
Heap() // default constructor, do as little as possible.
: m_array() // says 'call m_array()s default ctor'
{}
// Function to load values from an istream into this heap.
void read(std::istream& in)
{
for (size_t i = 0; i < Size; ++i)
{
in >> m_array[i];
}
return in;
}
void write(std::ostream& out)
{
for (size_t i = 0; i < Size; ++i)
{
if (i > 0)
out << ','; // separator
out << m_array[i];
}
}
int heapify(size_t index)
{
// implement your code here.
}
}; // <-- important.
int main(int argc, const char* argv[])
{
Heap myHeap; // << constructed but not populated.
myHeap.load(std::cin); // read from cin
for (size_t i = 1; i < myHeap.Size; ++i)
{
myHeap.heapify(i);
}
myHead.write(std::cout);
return 0;
}
最后,我们在您的代码中遇到了一个简单而基本的问题。C++没有隐式乘法。2i
是带有后缀的数字2
。它与2 * i
不同。
int l = 2 * i;
您的代码还有一个特性,表明您正在混合基于0和基于1的实现。选择一个并坚持下去。
---编辑---
从技术上讲,这个:
myHeap.load(std::cin); // read from cin
for (size_t i = 1; i < myHeap.Size; ++i)
{
myHeap.heapify(i);
}
封装不良。我这样写是为了借鉴原始的代码布局,但我想指出,将构造和初始化分离的一个原因是,它可以确保初始化一切就绪。
因此,将heapify调用移到load函数中会更正确。毕竟,还有什么比添加新值、始终保持列表有序更好的时间来堆积呢。
for (size_t i = 0; i < Size; ++i)
{
in >> m_array[i];
heapify(i);
}
现在您已经简化了类api,用户不必知道内部机制。
Heap myHeap;
myHeap.load(std::cin);
myHeap.write(std::cout);
- 警告处理为错误这里有什么问题
- 最小硬币更换问题(自上而下方法)
- 为"adjacent"变量赋值时出现问题
- 我的神经网络不起作用 [XOR 问题]
- 在Ubuntu 16.04上安装Cilk时出现问题
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 编译包含字符串的代码时遇到问题
- Project Euler问题4的错误解决方案
- 问题:什么是QAbstractItemView::NoEditTriggers的反面
- 在编译C++代码(具有dlib和opencv)到WASM时面临问题
- 在进程中对同一管道进行读取和写入时C++管道出现问题
- 静态数据成员的问题-修复链接错误会导致编译器错误
- C++ 雷神库 - 使用资源加载器类时出现问题(不命名类型)
- 一个关于在C++中重载布尔运算符的问题
- 首要问题的答案让值班员搞错了
- setlocale的C++土耳其字符串问题
- 如何重构类层次结构以避免菱形问题
- 基于boost的程序的静态链接——zlib问题
- C++格式化输出问题
- 使用mongocxx驱动程序时包含头文件问题