[英]Implementation of BST C++ Segmentation Fault
我在 C++ 中實現了二叉搜索樹,但出於某種原因,我沒有看到分段錯誤發生的位置。 但我確實注意到,當我在 addNode 的第一個條件語句中注釋掉 root = node 時,錯誤消失了。 什么是分段錯誤,它與指針有什么關系?
#include <iostream>
#include <iomanip>
using namespace std;
class bstNode
{
public:
int value;
bstNode *left;
bstNode *right;
bstNode(){};
~bstNode(){};
bstNode(int value)
{
this->value = value;
this->left = NULL;
this->right = NULL;
}
bstNode(int value, bstNode *left, bstNode *right)
{
this->value = value;
this->left = left;
this->right = right;
}
bstNode *root;
void addNode(int value)
{
if (root == NULL)
{
bstNode *node = new bstNode(value);
root = node;
}
else
{
bstNode *focusNode = root;
bstNode *parent;
while (focusNode != NULL)
{
if (value > focusNode->value)
{
focusNode = focusNode->right;
if (focusNode == NULL)
{
focusNode->right = new bstNode(value);
}
}
else
{
focusNode = focusNode->left;
if (focusNode == NULL)
{
focusNode->left = new bstNode(value);
}
}
}
}
}
static void printBST(bstNode *node)
{
while (node != NULL)
{
printBST(node->left);
cout << node->value;
printBST(node->right);
}
}
};
int main()
{
bstNode *node = new bstNode();
node->addNode(7);
node->addNode(2);
node->addNode(18);
node->addNode(6);
node->addNode(4);
node->addNode(23);
bstNode::printBST(node->root);
return 0;
}
直接錯誤是這個
if (focusNode == NULL) {
focusNode->left = new bstNode(value);
}
這顯然是錯誤的,如果指針是 null 你就不能使用它。 你在多個地方都有這個。 修復該問題,然后在您通過該問題后更新問題。 我怎么知道的? 我在我的調試器下運行了你的代碼,它立即告訴我,你應該學習如何充分利用你的調試器。
下一個
void addNode(int value)
作為定義為 class 的方法
class bstNode {
public:
int value;
是非常糟糕的做法。 在那種方法中, value
指的是什么? 參數或成員變量。 養成像這樣給成員變量指定名稱的習慣
class bstNode {
public:
int value_;
還有輕微的虱子。 命名類的公認風格是像這樣使用前導大寫字母
class BstNode {
public:
int value_;
甚至
class BSTNode
class bstNode {
public:
int value_;
在下面的代碼中...
while(focusNode != NULL){
if(value > focusNode->value){
focusNode = focusNode->right;
if(focusNode == NULL){
focusNode->right = new bstNode(value);
}
}
else{
focusNode = focusNode->left;
if(focusNode == NULL){
focusNode->left = new bstNode(value);
}
}
}
...您正在引用保證為 NULL 的節點的子節點,因為您使用條件語句驗證了這一點。 由於節點本身不存在,因此它沒有子節點之類的屬性。 想象一下,您正試圖與一個從未存在過的人的孩子交流。
變量focusNode
存儲節點的地址。 focusNode->value
所做的是它轉到其地址focusNode
存儲並從那里檢索 value 屬性的節點。
當focusNode
是NULL
時,它不指向任何節點,因此您不能在那里 go 並檢索它的 value 屬性。
我編寫了您可以用 while 循環替換的代碼。 我已經測試過它並且有效:
while(true){
if(value > focusNode->value){
if(focusNode->right == NULL){
focusNode->right = new bstNode(value);
return;
} else focusNode = focusNode->right;
}
else{
if(focusNode->left == NULL){
focusNode->left = new bstNode(value);
return;
} else focusNode = focusNode->left;
}
}
我還修復了您的printBST function。在printBST function 中使用if而不是while ,因為 while 循環內的代碼將被執行無限次而不是一次打印 BST。
static void printBST(bstNode* node){
if(node != NULL){
printBST(node->left);
cout << node->value <<" ";
printBST(node->right);
}
}
using namespace std;
我一般建議不要這樣做。 很難確定命名空間std
中有什么,但簡短的總結是“很多,而且一直都在增加”,因此讓所有這些直接可見可能會導致問題。
bstNode(){};
~bstNode(){};
這些並沒有真正完成任何有用的事情。 構造函數的目的是初始化 object,但這些只會讓 object 未初始化,這可能會導致問題——尤其是當/如果您嘗試取消引用未初始化的指針時出現分段錯誤。
bstNode(int value){
this->value = value;
this->left = NULL;
this->right = NULL;
}
這樣更好,但我更喜歡使用成員初始化列表而不是 ctor 體內的賦值,而且我更喜歡nullptr
而不是NULL
:
bstNode(int value)
: value(value)
, left(nullptr)
, right(nullptr) {}
下一個:
bstNode(int value, bstNode* left, bstNode* right){
this->value = value;
this->left = left;
this->right = right;
}
...寫得很好(盡管它也可以使用成員初始化列表,這通常更可取),但在構建二叉搜索樹時很少有用,因為在正常使用中你只會插入新的葉節點,而不是新的內部節點節點。
void addNode(int value){
if (root == NULL){
bstNode* node = new bstNode(value);
root = node;
}
else{
bstNode* focusNode = root;
bstNode* parent;
while(focusNode != NULL){
if(value > focusNode->value){
focusNode = focusNode->right;
if(focusNode == NULL){
focusNode->right = new bstNode(value);
}
}
else{
focusNode = focusNode->left;
if(focusNode == NULL){
focusNode->left = new bstNode(value);
}
}
}
}
}
這至少是分段錯誤的一個明顯來源——您在驗證指針是 null 后立即取消引用它。
至少對於第一次嘗試,我想我會使用遞歸實現,它往往更簡單:
void addNode(int value, bstNode *&node = root) {
if (node == nullptr) {
node = new node(value);
} else if (value < node->value) {
addNode(value, node->left);
} else if (value > node->value) {
addNode(value, node->right);
} else {
// attempt at inserting duplicate value
}
}
請注意,這傳遞了對指針的引用,因此我們可以修改“當前”指針,而不必在遍歷樹時跟蹤父指針。
static void printBST(bstNode* node){
while(node != NULL){
printBST(node->left);
cout << node->value;
printBST(node->right);
}
}
由於我們遞歸地執行此操作,因此我們不需要(甚至不需要)循環。 遍歷左子樹,當前節點,右子樹遍歷整棵樹,不需要迭代。
另請注意,這不會在節點中的數字之間打印任何分隔符,因此包含12, 34
的樹和包含1, 2, 3, 4
的樹都將打印為1234
,這可能不是很有用. 幸運的是,添加分隔符非常容易。
static void printBST(bstNode* node){
if (node != nullptr){
printBST(node->left);
cout << node->value << ' ';
printBST(node->right);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.