如何从几乎排序的链表中分离放错位置的元素?

时间:2018-08-19 22:06:01

标签: c++ list sorting linked-list insertion-sort

我有一个几乎排序的链表,其中至少包含两个唯一的元素,而没有1元素。一些示例:

28 (144) 44 52 60
60 68 76 84 (65) 100

结构如下:

struct node {node * next; int val;}

这是我的分离功能(并非始终有效):

node *detach(node *&l)
{
    if(l->val>l->next->val)
    {
        node *removed=l;
        l=l->next;

        return removed;
    }

    node *first=l->next->next;
    node *prev=l;

    while(first!=NULL)
    {
        if(prev->next->val>first->val)
        {
            node *removed=prev->next;
            prev->next=removed->next;

            return removed;
        }

        prev=prev->next;
        first=first->next;
    }

    return NULL;
}

我应该对其进行哪些更改以使其正常工作?

4 个答案:

答案 0 :(得分:5)

这并不能直接回答您的问题,因为它是目前制定的:

  

我应该如何更改(detach中的

这更多地是对“如何更改它以使其变得更好”的回答。但是,根据您的目标,您可能会发现它有用。

C ++的最佳做法是使用标准容器和算法,而不是推出自己的容器或使用原始循环,因为除其他外,它们经过了很好的测试,可以为读者明确表达您的意图(请参阅{{3 }}了解更多信息。

假设您具有C ++ 11,则可以使用a talk by Sean Parent作为单链列表实现和std::forward_list算法来查找最后正确排序的元素std::adjacent_find不能与std::forward_list一起使用,因为它会返回顺序错误的第一个元素,而且您不能通过单链接返回到前一个元素列表):

std::forward_list<int> list = {60, 68, 76, 84, 65, 100};
auto last_sorted = std::adjacent_find(list.cbegin(), list.cend(), std::greater_equal<int>{});
// use last_sorted here
list.erase_after(last_sorted); // delete the not-in-place-element after you are done

或者,您可以使用在C ++ 11之前可用的双链接std::list。区别在于std::list::erase()接受要删除的元素的迭代器,因此std::is_sorted_untilstd::less<int>在这里更合适:

std::list<int> list = {60, 68, 76, 84, 65, 100};
auto last_sorted = std::is_sorted_until(list.cbegin(), list.cend(), std::less<int>{});
// use last_sorted here
list.erase(last_sorted); // delete the not-in-place-element after you are done

答案 1 :(得分:4)

以下是您代码的经过调试(但并未真正改进)的版本:

struct node {node * next; int val;};

node *detach(node *l)
{
    if(l->val>l->next->val)
    {
        node *removed=l;
        l=l->next;

        return removed;
    }

    node *first=l->next->next;
    node *prev=l;

    while(first!=NULL)
    {
        if(prev->next->val>first->val)
        {
          if(prev->val>first->val)
          {
              node *removed=first;
              prev->next->next=removed->next;

              return removed;
          }
          else
          {
              node *removed=prev->next;
              prev->next=removed->next;

              return removed;
          }
        }

        prev=prev->next;
        first=first->next;
    }

    return NULL;
}

测试片段的有效代码段为here

至于花一些时间来提出更好的解决方案,您需要澄清一点要求:不清楚这是一个分配,您是否必须使用数据严格性节点,或者这是您的选择并且相同关于分离方法-如果应该这样或您的想法。另外,您还必须回答paxdiablo的“哲学问题”:

  

列表{10,25,20,30}中的20或25是否乱序?

答案 2 :(得分:4)

这是一个简单得多的解决方案。仅有一个while循环和一个if语句。

node *detach(node *&l)
{
    node **p=&l;

    while ( (*p) && (*p)->next)
    {
        if ( (*p)->val > (*p)->next->val)
        {
            node *q=(*p)->next;

            (*p)->next=(*p)->next->next;

            return q;
        }

        p= &(*p)->next;
    }
    return NULL;
}

现在,有了这种解决方法,我想我将对其进行一些解释。

让我们从遍历链接列表的基本循环开始:

node *p;

for (p=head; p; p=p->next)
{
    ;
}

就这么简单。您携带一个指针,并将其前进到列表中的每个元素。这是每本教科书中的第一个例子。

现在,让我们退后一步。假设不是携带指向每个节点的指针,而是携带指向 那个 指针的指针?

这是什么意思:指向链接列表中每个元素的指针来自两个位置之一:它是上一个节点的head指针或next指针。 / p>

因此,让我们开始冒险,将指针指向head

node **p=&head;

这是一个开始。下一步是使该指针前进,以指向链接列表中所有剩余元素的next。因此,我们最终得到如下所示的内容:

for (node **p=&head; *p; p=&(*p)->next)
{
}

在此for循环的主体中,表达式“ *p”在逻辑上等效于使用普通指针的第一个简单循环中的普通p

花点时间将您的大脑包裹在此循环中,在您完全了解其工作原理之前,不要继续进行下去。

。 。

现在,回到我最初的答案,您应该能够弄清楚它是如何工作的。碰巧的是,当我写我的答案时,我感觉就像在使用while循环,但是它可能只是这个精确的for循环。

答案 3 :(得分:0)

使用stl,您可以这样做:

int detach(std::list<int>& l)
{
    if (l.size() < 2) {
        throw std::runtime_error("too short list");
    }
    auto it = std::is_sorted_until(l.begin(), l.end());
    if (it == l.end()) {
        throw std::runtime_error("already sorted list");
    }
    if (!std::is_sorted(it, l.end())) {
        throw std::runtime_error("not 'partially' sorted list");
    }
    if (std::prev(it) == l.begin() || *it < *std::prev(it, 2)) {
//  if (std::next(it) != l.end() && !(*std::next(it) < *std::prev(it))) {
        auto res = *it;
        l.erase(it);
        return res;
    } else {
        auto res = *std::prev(it);
        l.erase(std::prev(it));
        return res;
    }
}

Demo Demo

将(针对您的结构)翻译为:

bool is_sorted(const node* n) {
    const node* cur = n;
    const node* next = cur->next;

    while (next != nullptr && cur->val < next->val) {
        cur = next;
        next = next->next;
    }   
    return next == nullptr; 
}

node* extract(node*& root, node* prev, node* n)
{
    if (prev == nullptr)
    {
        if (root == nullptr) {
            return nullptr;   
        }
        root = n->next;
        n->next = nullptr;
        return n;
    }
    prev->next = prev->next->next;
    n->next = nullptr;
    return n;
}

node* detach(node*& root)
{
    if (root == nullptr || root->next == nullptr) {
        throw std::runtime_error("too short list");
    }
    node* prev = nullptr;
    node* cur = root;
    node* next = cur->next;

    while (next != nullptr && cur->val < next->val) {
        prev = cur;
        cur = next;
        next = next->next;
    }
    if (next == nullptr) {
        throw std::runtime_error("already sorted list");
    }
    if (!is_sorted(it, l.end())) {
        throw std::runtime_error("not 'partially' sorted list");
    }
    if (next->next == nullptr || next->next->val < cur->val) {
        return extract(root, prev, cur);
    } else {
        return extract(root, cur, next);
    }
}

Demo