从链表中删除用户选择的节点,用 c 中另一个列表中的节点替换它们

Posted

技术标签:

【中文标题】从链表中删除用户选择的节点,用 c 中另一个列表中的节点替换它们【英文标题】:removing user chosen nodes from a linked list, replacing them with nodes from another list in c 【发布时间】:2022-01-19 23:06:39 【问题描述】:

我正在尝试编写一个扑克程序。现在几乎一切正常,一个问题是程序询问用户他们想要保留哪些卡片,以及他们想要丢弃哪些卡片。如果它按预期工作,用户将输入他们想要保留的卡片。没有被选中的卡片将被移除,并用牌组中的卡片替换。玩家的手牌和牌组是两个独立的链表。

这部分程序的行为有点奇怪。有时它工作正常。其他时候,它会丢弃本应保留的牌,或保留本应丢弃的牌。有时它也会改变某些牌的花色(或者可能是复制某些牌,我不确定)。

这是创建牌组的函数:

card *
createCard(int n)

    int i = 0;
    card *head = (card *) malloc(sizeof(card));
    card *tmp = NULL;
    card *p = NULL;

    p = head;

    for (i = 0; i < n - 1; i++) 
        tmp = (card *) malloc(sizeof(card));
        p->next = tmp;
        p = p->next;
        p->next = NULL;
    

    tmp = head;
    for (i = 1; i <= 13; i++) 

        for (int j = 3; j <= 6; j++) 
            tmp->face = i;
            tmp->suit = j;
            tmp = tmp->next;
        

    

    return (head);

这是创建玩家手牌的函数(通过从套牌列表的前五个节点创建一个新的链表):

void
createHand(card ** deck, card ** hand)

    card *tmp = NULL;
    card *card = *deck;
    int i;

//while (card != NULL)
    for (i = 0; i < 5; i++) 
        (*deck) = (*deck)->next;

        tmp = card->next;
        card->next = *hand;
        *hand = card;
        card = tmp;

    
    (*hand)->next->next->next->next->next = NULL;
    return;


这是不起作用的代码部分(注意:playerHand 是玩家的手,首先是牌组):

i = 1;

// this array keeps track of which cards the user wants to keep
int a[] =  0, 0, 0, 0, 0 ;

while (i <= 5) 
    printf("Pick cards (between 1-5) to hold (-1 to stop): ");
    scanf("%d", &keep);

    // breaks from loop if the user enters -1
    if (keep == -1)
        break;

    if (keep == 0 || keep > 5 || keep <= -2) 
        printf("Invalid index. Pick cards (between 1-5) to hold (-1 to stop): ");
        scanf("%d", &keep);
        if (keep == -1)
            break;
    
    if (keep == -1) 
        break;
    
    if (keep != -1) 
        // when player wants to keep a card, the corresponding index of that
        // card is set to one in the array
        a[keep - 1] = 1;
    
    i++;


card *tmp;

tmp = first;
card *tmp2;

tmp2 = playerHand;
for (i = 0; i < 5; i++) 
    // if the corresponding index in the array is 0, that card is replaced
    if (a[i] == 0) 
        tmp2->face = tmp->face;
        tmp2->suit = tmp->suit;
    
    first = first->next;
    free(tmp);
    tmp = first;
    tmp2 = tmp2->next;

删除这部分代码后,卡片并没有改变,所以错误一定在哪里,我只是不确定在哪里。

这是玩家选择要保留的卡片时的输出结果。在这种情况下,玩家选择保留第一张和第三张牌并丢弃其他三张:

选择卡(1-5 之间)持有(-1 停止):1

选择卡(1-5 之间)持有(-1 停止):3

选择卡片(1-5 之间)持有(-1 停止):-1

【问题讨论】:

您是否尝试过在调试器中逐行运行代码,同时监控所有变量的值,以确定您的程序在哪个点停止按预期运行?如果您没有尝试过,那么您可能想阅读以下内容:What is a debugger and how can it help me diagnose problems? 您可能还想阅读以下内容:How to debug small programs?。 请注意,在调试生成随机值的程序时,使用固定值播种随机数生成器通常很有帮助,而不是每次运行程序时都使用不同的值。这样,您可以更轻松地重现特定错误,因为无论何时在调试器中重新启动程序,程序的行为都是相同的。 如果可能,请提供问题的minimal reproducible example(包括函数main 和所有#include 指令)。例如,也许你可以为玩家的手牌和牌组提供一个简单的硬编码链表,并在上面演示问题。 你有 UB(未定义的行为)。在第三个代码块中,在 for 循环中,您执行以下操作:tmp2-&gt;face = tmp-&gt;face;。但是,tmp统一。它有一个“随机”值,所以它可以指向任何东西。通常,这会导致段错误。但是,对于 UB,如果 tmp 指向存在的内存,您可以获得随机结果,基于 whatever 只是“碰巧”在那里。你会希望tmp 指向一张从牌组中拉出/出列的新牌。所以:在if 下,你可以这样做:tmp = get_card_from_deck()。但是,这会“泄露”tmp 指向的卡片...... ... 最好让tmp2 出队/释放tmp,其中tmp2 在玩家手牌列表中。我会编写执行低级任务的函数(例如):void list_append(list *lst,card *crd);void list_remove(list *lst,card *crd);void list_discard_and replace(list *lst,card *old,card *new); 您可能需要一些列表:list discard_list, deck_list, player_hands[NPLAYERS]; 并且,将卡片移入/移出这些列表 【参考方案1】:

也许代码应该是这样的?玩家如何选择替换卡?

你能展示保存卡片以供替换的代码是什么样的吗?

card *tmp;

tmp = first;
card *tmp2;

tmp2 = playerHand;
for (i = 0; i < 5; i++) 
    if (a[i] == 0) //if the corresponding index in the array is 0, that card is replaced
        tmp2->face = tmp->face;
        tmp2->suit = tmp->suit;

        first = first->next;
        free(tmp);
        tmp = first;
    
    tmp2 = tmp2->next;

用户选择要替换的卡片后,我们从牌组中取出前n张卡片。

您的代码中的错误是牌组的前五张牌总是被丢弃。如果手中的牌被标记为丢弃,则不是从牌堆中取出下一张牌,而是一张具有相同索引的牌。

card *tmp;

tmp = first;
card *tmp2;

tmp2 = playerHand;
for (i = 0; i < 5; i++) 
    // if the corresponding index in the array is 0, 
    // the card will be replaced by a card with the same index from the deck
    if (a[i] == 0) 
        tmp2->face = tmp->face;
        tmp2->suit = tmp->suit;
    
    first = first->next; //changes the top of the deck to the next
    free(tmp);    //always removes a card from the deck
                  //so the first 5 cards will be removed
    tmp = first;  //all these three lines must be moved to the condition if

    tmp2 = tmp2->next;

而在while循环中,需要改代码,因为如果用户连续两次输入错误的索引,程序就会crash

    if (keep == 0 || keep > 5 || keep <= -2) 
        printf("Invalid index. ");
        continue;
    

【讨论】:

以上是关于从链表中删除用户选择的节点,用 c 中另一个列表中的节点替换它们的主要内容,如果未能解决你的问题,请参考以下文章

从链表中删除节点(递归)

从链表 C++ 中删除节点

从链表中删除节点

javascript中的链表结构—从链表中删除元素

从链表中删除总和值为0的连续节点

递归地从链表中删除数据