使用单个链接列表筛选Eratosthenes C ++

问题描述

嗨,我在使用C ++时遇到Eratosthenes筛问题。我必须使用单个链表进行此操作。我的程序正在运行,并且显示列表的第一个声明,但是我不知道如何正确删除非素数。我的功能对我不起作用。我应该如何更改删除功能

#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;
}

解决方法

这将是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;
        }
    }
}

您的删除方法存在一些问题。 算法逻辑问题:应该首先检查算法头,因为如果删除了算法头,则不检查新头的素性,您需要立即检查下一个新头,即旧-> next-> next。另外,您也没有检查数字是否等于分频器,在这种情况下,不应删除该数字。

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

但是这里最大的问题是,这根本不是Eratosthenes筛子,你是 只需检查所有数字是否与小于sqrt(n)的所有其他数相除即可。它 相较于Eratosthenes筛子,它是次优的。如果您使用Google Eratosthenes筛子,则会发现很多详细的教程和说明。

,

我喜欢Bran的宣传,我将添加一些技巧。

评论:全局变量很烂。当项目变大时,它们使事情难以追踪。我将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行神秘的胡言乱语,没人会学到什么。