【问题标题】:Sieve of Eratosthenes C++ using single linked list使用单链表筛选 Eratosthenes C++
【发布时间】:2020-11-04 23:18:18
【问题描述】:

您好,我对 C++ 中的埃拉托色尼筛法有疑问。我必须使用单链表来做到这一点。我的程序正在运行并显示列表的第一个声明,但我不知道如何正确删除非素数。我的功能对我不起作用。我应该如何更改我的删除功能?

#include <iostream>
#include <cmath>

using namespace std;

struct List
{
    int number;
    List* next;
};
List* head = new List;
void l_add(int n)
{
    List* temp = head;
    for (int i = 2; i <= n; i++)
    {
        temp->next = new List();
        temp->number = i;
        temp = temp->next;
    }
}
void l_print()
{
    List* temp = head;
    while (temp->next != 0)
    {
        cout << temp->number << " ";
        temp = temp->next;
    }
    cout << endl;
}
void l_delete(int n)
{
    List* temp = head;
    for (int i = 2; i < sqrt(n); i++)
    {
        if (temp->number % i == 0)
        {
            head = temp->next;
            delete temp;
            temp = head;
        }
        while (temp->next != 0)
        {
            if (temp->next->number % i == 0)
            {
                temp->next = temp->next->next;
                delete temp->next;
            }
            temp = temp->next;
        }
    }
}
int main()
{
    int n;
    cout << "Enter up to which number to find prime numbers using Sieve of Eratosthenes: " << endl;
    cin >> n;
    l_add(n);
    l_print();
    l_delete(n);
    l_print();
    return 0;
}

【问题讨论】:

  • 请注意,遍历链表会影响性能,而哨兵节点会让您感到悲痛,因为我认为您没有任何方法可以知道它是哨兵节点。

标签: c++ singly-linked-list sieve-of-eratosthenes


【解决方案1】:

这将是 l_delete 方法的工作版本:

void l_delete(int n)
{
    List* temp = head;
    for (int i = 2; i < sqrt(n); i++)
    {
        while (temp->next != 0)
        {
            if (temp->next->number % i == 0 && temp->next->number != i)
            {
                List* temp2 = temp->next->next;
                delete temp->next;
                temp->next = temp2;
            }
            if(temp->next == 0) break;
            temp = temp->next;
        }
        temp = head;
        if (temp->number % i == 0 && temp->number != i)
        {
            head = temp->next;
            delete temp;
            temp = head;
        }
    }
}

您的删除方法存在几个问题。 算法逻辑的问题:你的算法头应该最后检查,否则如果它被删除,新的头不会检查素数,你会立即检查新的下一个,这是旧的->下一个->下一个。此外,您没有检查数字是否等于除数,在这种情况下不应删除它。

编程逻辑问题: 当您在while循环中删除下一个节点时,与删除head时相同,您需要另一个临时变量来存储temp->next->next,然后在删除后将其分配给temp->next。

但这里最大的问题是,这根本不是埃拉托色尼筛子,你是 只需检查所有数字与所有其他小于 sqrt(n) 的数字的可分性。它 与 Eratosthenes 筛相比是次优的。如果你 Google Eratosthenes sieve,你会发现很多详细的教程和解释。

【讨论】:

    【解决方案2】:

    我喜欢布兰的投球方式,我将添加一些技巧。

    评论:全局变量很烂。当项目变得更大时,它们使事情更难追踪。我会将head 移动到main 并将其作为参数传递。我也放弃了哨兵节点,因为我发现它们大部分时间比它们更值得的麻烦。

    int main()
    {
        int n;
        cout << "Enter up to which number to find prime numbers using Sieve of Eratosthenes: " << endl;
        if (cin >> n)
        {
            // should be a bit more logic here to automatically handle ns of 1 or 2
            List* head = nullptr; // not using a sentinel node
            l_add(head, n); // passing in head rather than global variable free-for-all
            l_delete(head);
            l_print(head);
            return 0;
        }
        else
        {
            cout << "invalid input." << endl;
            return 1;
        }
    }
    

    添加到链表时,除了2之外不需要任何偶数,所以不要添加它们。迭代列表所花费的时间更少。之后就是确保节点按正确顺序进入的问题。

    void l_add(List*& head, // passing in head. Easier to track
               int n)
    {
        List** temp = &head; // head is a next pointer with a different name
                             // hiding it behind another pointer allows us to treat 
                             // it like a next
                             // temp is now a pointer to next pointers. We can add directly to the 
                             // last nodes's next pointer and also use it to access the current 
                             // pointer if we need to
        (*temp) = new List {2, nullptr}; // 2 is only even prime
         temp = &(*temp)->next;
        for (int i = 3; i <= n; i+=2) // start at 3 and only add odd numbers
        {
            (*temp) = new List {i, nullptr};
            temp = &(*temp)->next; // Advance to next node
        }
    }
    

    当我们遍历列表以消除倍数时,我们需要两个循环。一个来跟踪我们正在消除的多个节点,另一个循环来进行搜索和消除。请注意,我再次使用了指针对指针的技巧。这个技巧消除了大约一半的遍历和删除节点所需的代码。

    void l_delete(List * head)
    {
        List* last = head->next; // track the last known prime node. skip node 2. 
                                 // note this will blow up if there is no node 2.
        while (last) // for every node still in the list
        {
            List** current = &last->next; // similar to trick above.
                                          // if we have a pointer to the next to be 
                                          // updated, we don't need to track the previous node   
            while ((*current)) // look at every node after the last prime
            {
                if ((*current)->number % last->number == 0) // if it's a multiple, remove it.
                {
                    List * to_del = (*current); //save node to delete
                    (*current) = (*current)->next; // link previous node to next node. 
                                                   // effectively automatically advances the node
                    delete to_del;
                }
                else // proceed to next node
                {
                    current = &(*current)->next;
                }
            }
            last = last->next; // advance to next prime number
        }
    }
    

    其中可能有足够的空间进行优化,但我的目标是提高可读性,因为如果我丢掉 10 行神秘的胡言乱语,没有人会学到 nuthin'。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多