【问题标题】:AVL Tree implementation c++AVL 树实现 c++
【发布时间】:2017-04-24 04:57:02
【问题描述】:

所以我最近发布了这个,但我仍然不知道出了什么问题。具体来说,我似乎无法弄清楚是什么导致我的 AVL 树需要这么长时间来排序。我读入一个包含 500,000 个随机、未排序的数字的文件,通过在 for 循环中使用一个向量来一次给树提供一个数字来排序。现在,我还使用普通的 BST 进行了测试,因为有人提到必须一次创建这么多节点可能是为什么它需要这么长时间,但只用了 5 秒就完成了,只跳过了 12,164 个节点。重复。我的 AVL 树需要 3 个小时以上才能对列表的一半进行排序,所以一定是出了问题。谁能弄清楚它是什么?据我所知,重新平衡和插入逻辑是正确的,因为每当我在上面运行一堆测试用例时,它们都很好。我似乎无法找出问题所在。这是我的完整代码,供任何想要查看的人使用。 Main 现在有点乱,因为我包含了所有用于测试目的的东西(比如跟踪循环),但其中大部分将在最终版本中消失。

编辑:

这个问题已经回答了。

#include <iostream>
#include<iomanip>
#include <time.h>
#include <vector>
#include <fstream>
using namespace std;

vector<int> numbers;

struct node
{
public:
    int data, height;
    node *leftChild, *rightChild;
};

node* root = NULL;

int findMin(node *p) // finds the smallest node in the tree
{
    while (p->leftChild != NULL)
        p = p->leftChild;
    return p->data;
}
int findMax(node *p) // finds the largest node in the tree
{
    while(p->rightChild != NULL)
        p = p->rightChild;
    return p->data;
}
int max(int a, int b) // gets the max of two integers
{
    if(a > b)
        return a;
    else
        return b;
}
int height(node *p) // gets the height of the tree
{
    if(p == NULL)
        return -1;
    else
    {
        p->height = max(height(p->leftChild), height(p->rightChild)) + 1;
    }
    return p->height;
}
node* newNode(int element) // helper function to return a new node with empty subtrees
{
    node* newPtr = new node;
    newPtr->data = element;
    newPtr->leftChild = NULL;
    newPtr->rightChild = NULL;
    newPtr->height = 1;
    return newPtr;
}
node* rightRotate(node* p) // function to right rotate a tree rooted at p
{
    node* child = p->leftChild; // rotate the tree
    p->leftChild = child->rightChild;
    child->rightChild = p;

    // update the height for the nodes
    p->height = height(p);
    child->height = height(child);
    // return new root
    return child;

}
node* leftRotate(node* p) // function to left rotate a tree rooted at p
{
    node* child = p->rightChild; // perform the rotation
    p->rightChild = child->leftChild;
    child->leftChild = p;

    // update the heights for the nodes
    p->height = height(p);
    child->height = height(child);

    // return new root
    return child;
}

int getBalance(node *p)
{
    if(p == NULL)
        return 0;
    else
        return height(p->leftChild) - height(p->rightChild);
}
// recursive version of BST insert to insert the element in a sub tree rooted with root
// which returns new root of subtree
node* insert(node*& p, int element)
{
    // perform the normal BST insertion
    if(p == NULL) // if the tree is empty
        return(newNode(element));
    if(element < p->data)
    {
        p->leftChild = insert(p->leftChild, element);
    }
    else
    {
        p->rightChild = insert(p->rightChild, element);
    }

    // update the height for this node
    p->height = height(p);

    // get the balance factor to see if the tree is unbalanced
    int balance = getBalance(p);

    // the tree is unbalanced, there are 4 different types of rotation to make

    // Single Right Rotation (Left Left Case)
    if(balance > 1 && element < p->leftChild->data)
    {
        return rightRotate(p);
    }
    // Single Left Rotation (Right Right Case)
    if(balance < -1 && element > p->rightChild->data)
    {
        return leftRotate(p);
    }
    // Left Right Rotation (double left rotation)
    if(balance > 1 && element > p->leftChild->data)
    {
        p->leftChild = leftRotate(p->leftChild);
        return rightRotate(p);
    }
    // Right Left Rotation
    if(balance < -1 && element < p->rightChild->data)
    {
        p->rightChild = rightRotate(p->rightChild);
        return leftRotate(p);
    }
    // cout << "Height: " << n->height << endl;
    // return the unmodified root pointer in the case that the tree does not become unbalanced
    return p;
}
void inorder(node *p)
{
    if(p != NULL)
    {
        inorder(p->leftChild);
        cout << p->data << ", ";
        inorder(p->rightChild);
    }
}
void preorder(node *p)
{
    if(p != NULL)
    {
        cout << p->data << ", ";
        preorder(p->leftChild);
        preorder(p->rightChild);
    }
}

void print(node* root)
{
    /*cout << "Min Value: " << findMin(root) << endl;
    cout << "Max Value: " << findMax(root) << endl;
    cout << "Pre Order: ";
    preorder(root); */
    cout << endl << "Inorder: ";
    inorder(root);
    cout << endl << endl << endl << endl;

}

void read()
{
    int num;
    ifstream file_save("data.txt");
    if(file_save.is_open())
    {
        while(!file_save.eof())
        {
            file_save >> num;
            numbers.push_back(num);
        }
        file_save.close();
    }
    else
    {
        cout << "Error in opening file!!" << endl;
    }
}

int main()
{
    double duration;
    time_t begin = time(0);

    read();
    int x = 0;
    int track = 0;
    for (std::vector<int>::const_iterator i = numbers.begin(); i != numbers.begin() + 100000; ++i)
    {
        root = insert(root, numbers[x]);
        x++;
        track++;
        if( (track % 10000) == 0)
        {
            cout << track << " iterations" << endl;
            time_t now = time(0);
            cout << now - begin << " seconds" << endl;
        }

    }
    time_t end = time(0);
    duration = end - begin;
    // print(root);
    cout << "The algorithm took " << duration << " seconds to complete." << endl;
    return 0;
}

【问题讨论】:

  • 你的程序段错误相当快,不知道为什么你认为它需要很长时间才能运行。当您有一个包含 10 个数字的数据文件时,也许创建一个高度为 160 的树与它有关。还有while(eof) is wrong
  • 我不确定我是否理解你的意思,你能详细说明一下吗?
  • 我试过你的程序。当输入文件有大约 10 个数字时它会崩溃。我还在调试器中运行了它,并观察到它创建的树是无效的。现在启动您自己的调试器并开始调试。
  • 我不知道你为什么这么说,因为这些对我来说都不是真的。另外,树怎么无效?小心提供细节,因为我已经调试了好几次,我没有注意到它创建了一个无效的树。
  • this 在您看来是否像一个有效的 AVL 树?对我来说肯定不是。如果您已验证您的 AVL 树是有效的,它是在哪个数据文件上验证的?

标签: c++ algorithm sorting binary-search-tree avl-tree


【解决方案1】:

这段代码有很多问题。

  1. while(eof) is wrong
  2. 主循环需要正好 100000 个元素。
  3. 所有关键比较都是准确的(&lt;&gt;)。插入重复元素时不执行旋转。因此,相同元素的树根本不会平衡。
  4. 空树的高度被硬编码为-1,但单节点三的高度最初设置为1,因此违反了不变量height(node) = 1+max(height(node-&gt;leftChild))+height(node-&gt;rightChild))
  5. height 每次调用时都会遍历整个树,从而进行插入 O(n)

【讨论】:

  • 我的主要问题已经得到解答,但是每次我尝试使用 while(file_save >> num) 而不是 while(!file_save.eof()) 时,我都会丢失一半的数据。跨度>
  • 你可能想问一个单独的问题,虽然我猜你没有从你的循环body中删除file_save &gt;&gt; num
【解决方案2】:

所以,在我看来,花费这么长时间的原因是因为到处都有太多的递归调用。此修改后的代码具有较少的递归调用,因此需要处理的堆栈更少,从而使 CPU 陷入困境。至少,这就是我从中得到的。

void newHeight(node* p)
{
    double leftHeight = height(p->leftChild);
    double rightHeight = height(p->rightChild);
    if(leftHeight > rightHeight)
        p->height = leftHeight;
    else
        p->height = rightHeight;
}

node* rotateright(node* p) // the right rotation round p
{
    node* q = p->leftChild;
    p->leftChild = q->rightChild;
    q->rightChild = p;
    newHeight(p);
    newHeight(q);
    return q;
}

node* rotateleft(node* q) // the left rotation round q
{
    node* p = q->rightChild;
    q->rightChild = p->leftChild;
    p->leftChild = q;
    newHeight(q);
    newHeight(p);
    return p;
}

node* rebalance(node* p) // p node balance
{
    newHeight(p);
    if( getBalance(p)==2 )
    {
        if( getBalance(p->rightChild) < 0 )
            p->rightChild = rotateright(p->rightChild);
        return rotateleft(p);
    }
    if (getBalance(p)==-2 )
    {
        if( getBalance(p->leftChild) > 0  )
            p->leftChild = rotateleft(p->leftChild);
        return rotateright(p);
    }
    return p; // no balance needed
}

node* insert(node* p, int element) // k key insertion in the tree with p root
{
    if(!p) return newNode(element);
    if(element < p->data)
        p->leftChild = insert(p->leftChild, element);
    else
        p->rightChild = insert(p->rightChild, element);
    return rebalance(p);
}

【讨论】:

    猜你喜欢
    • 2012-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-26
    • 1970-01-01
    • 1970-01-01
    • 2012-08-20
    相关资源
    最近更新 更多