Shorter/more 编写按字典顺序添加新元素的 link 函数的有效方法

Shorter/more efficient way of writing a link function that adds new elements in lexicographical order

尝试编程原则和练习文本的练习,我花了一段时间调试,直到我最终设法为该函数编写了一个工作代码。练习是这样开始的: 我为 Link 创建了模板 class:

template<typename T>
struct Link
{
    T val;
    Link* prev;
    Link* succ;

    Link(const T& value, Link* p = nullptr, Link* s = nullptr)
        :val{value},prev {p}, succ{ s } {}

    Link* add_ordered_(Link*);
};

然后困难的部分是实现 add_ordered_(Link*) 函数,这样每次我使用 (new) 添加新元素时,Link 都遵循字典顺序。所以这是我的远景:

template<typename T >
Link<T>* Link<T>::add_ordered_(Link<T>*n)
{
    if (n == nullptr)return this;
    if (this == nullptr)return n;

    if (n->val < this->val)
    {
        Link* temp = this->prev;
        if (temp == nullptr)
        {
            if (this->prev)
            {
                this->prev->succ = n;
            }
            n->prev = this->prev;
            this->prev = n;
            n->succ = this;
            return this;
        }
        while (n->val < temp->val && temp)
        {
            if (temp->prev == nullptr)
            {
                if (temp->prev)
                {
                    temp->prev->succ = n;
                }
                n->prev = temp->prev;
                temp->prev = n;
                n->succ = temp;
                return this;
            }
            temp = temp->prev;
            if (n->val > temp->val)
            {
                n->prev = temp;
                if (temp->succ) temp->succ->prev = n;
                n->succ = temp->succ;
                temp->succ = n;
                return this;
            }
            if (temp->prev == nullptr)
            {
                if (temp->prev)
                {
                    temp->prev->succ = n;
                }
                n->prev = temp->prev;
                temp->prev = n;
                n->succ = temp;
                return this;
            }
        }

        if (this->prev)
        {
            this->prev->succ = n;
        }
        n->prev = this->prev;
        this->prev = n;
        n->succ = this;
        return this;
    }
    else
    {
        n->prev = this;
        if (this->succ)this->succ->prev = n;
        n->succ = this->succ;
        this->succ = n;
        return n;
    }
}

我使用了一个print_link函数来打印出元素以便于调试:

template<typename T>
void print_link(Link<T>* x)
{
    while (x)
    {
        cout << x->val << endl;
        x = x->prev;
    }
}

这样测试的:

int main()
{
     Link<int>*numbers = new Link<int>{ 5 };
     numbers = numbers->add_ordered_(new Link<int> {7} );
     numbers = numbers->add_ordered_( new Link<int>{3} );
     numbers = numbers->add_ordered_(new Link<int>{ 25 });
     numbers = numbers->add_ordered_(new Link<int>{ 19 });
     numbers = numbers->add_ordered_(new Link<int>{ 22 });
     numbers = numbers->add_ordered_(new Link<int>{ 16 });
     numbers = numbers->add_ordered_(new Link<int>{ 44 });
     numbers = numbers->add_ordered_(new Link<int>{ 11 });
     numbers = numbers->add_ordered_(new Link<int>{ 37 });
     print_link(numbers);
     cout << endl;  
     delete[] numbers;
}

输出为:

44
37
25
22
19
16
11
7
5
3

我写的add_ordered_函数好像太长了,丑陋且效率低下。如果有人向我展示一种更有效的写作方式,我会很高兴。

拆分你的方法:

std::pair<Link*, Link*> find_neighbor(const Link& link) {
    // assert(prev == nullptr);
    Link* left = nullptr; // == prev
    Link* right = this;

    while (right && right->val < link.val) {
        left = right;
        right = right->succ;
    }
    return {left, right};
}

void insert_between(Link* left, Link* right)
{
    prev = left;
    succ = right;
    if (left) {
        left->succ = this;   
    }
    if (right) {
        right->prev = this;   
    }
}

Link* add_ordered_(Link* link)
{
    // assert(prev == nullptr); // Assume we call it only with first element
    if (link == nullptr) {
        return this;   
    }
    auto p = find_neighbor(*link);
    link->insert_between(p.first, p.second);
    if (prev) {
        return prev; // == link
    }
    return this;
}

Demo

将事情分解成简单的小部分通常是个好主意。

让我们为您的 class、add_afteradd_before 添加 2 个函数。

template<typename T>
Link<T>* Link<T>::add_after(Link<T>*n)
{
    if (succ) {
        n->succ = succ;
    }

    succ = n;
    n->prev = this;
}

template<typename T>
Link<T>* Link<T>::add_before(Link<T>*n)
{
    if (prev) {
        n->prev = prev;
    }

    prev = n;
    n->succ = this;
}

现在这两个得心应手的家伙准备好了,我们可以继续 re-write add_odered_ 我们只需要专注于找到正确的插入位置。

template<typename T >
Link<T>* Link<T>::add_ordered_(Link<T>*n)
{
    Link* insert = this;
    if (n->val > insert->val) {
        insert->add_before(n);
        return n;
    }

    while (n->val < insert->val && insert->succ)
        insert = insert->succ;

    if (n->val > insert->val)
        insert->add_before(n);
    else
        insert->add_after(n);

    return this;
}