在C中删除数组中的元素

时间:2017-07-31 18:19:25

标签: c arrays

所以我应该让游戏成为课堂作业。

基本上,我决定重新创建一个单挑扑克游戏,运行不同的功能,例如int deal(a, b, x, y),其中ab是英雄卡,xy是别墅的。

这个功能特别让我有点难过。实际上,我循环遍历数组deck,并将随机数分配给abxy。然后,我会将每个指定的值转换为真实的唯一卡片,然后将其返回int main()

我坚持的部分是“ as 每张卡都被选中,它会从数组中删除。似乎C中没有简单的方法可以简单地从{{{ 1}}数组。

deck

有什么想法吗?

4 个答案:

答案 0 :(得分:4)

您不必删除任何内容。

随机播放deck数组(使用Fisher-Yates shuffle或类似算法)。处理"顶部"甲板:

int top = 0;

card1 = deck[top++];
card2 = deck[top++];
card3 = deck[top++];
...

等。 top变量是套牌中下一张可用卡的索引。

您的代码的大致轮廓将类似于

#define DECKSIZE 52
#define HANDSIZE  5

int main( void )
{
  int deck[DECKSIZE] = { ... }; // initial deck;
  size_t top = 0;               // points to next available card

  shuffle( deck, DECKSIZE ); 

  int hero[HANDSIZE] = {0};   // 0 means no card has been drawn for
  int villan[HANDSIZE] = {0}; // that element.  

  if ( deal( hero, HANDSIZE, deck, DECKSIZE, &top ) &&
       deal( villan, HANDSIZE, deck, DECKSIZE, &top ) )
  {
    /** 
     * do stuff with hero and villan hands
     */
  }
  else
  {
    /**
     * Not enough cards available in deck for two hands.
     */
  }
};

您的deal功能看起来像

int deal( int *hand, size_t handsize, int *deck, size_t decksize, size_t *top )
{
  size_t i;
  for ( i = 0; i < handsize && *top < decksize; i++ )
    hand[i] = deck[(*top)++];

  return i == handsize;
}

如果我们在发牌之前0用完了牌,此功能将返回deck,在这种情况下,您需要做某事。祝你好运!

如果你想处理部分手牌(例如更换3张牌),你可以做类似的事情

 if ( deal( &hero[2], 3, deck, DECKSIZE, &top) )
   ...

此调用将使用hero[2]中的三张新卡覆盖hero[4]deck。每次拨打dealtop都会被提前指向套牌中的下一张可用卡片。

您可以编写一个discard函数,将卡片返回到卡片组。这意味着保留一个单独的bottom变量并更新:

int discard( int card, int *deck, size_t decksize, size_t *top, size_t *bottom )
{
  int result = *bottom < *top && *bottom < decksize;
  if ( result )
    deck[(*bottom)++] = card;

  return result;
}

显然,bottom应严格小于套牌大小,并严格低于丢弃时的top;否则,我们无法妥善管理我们的牌组或牌局。通过一些工作,你可以使你的数组​​&#34;循环&#34;,以便topbottom&#34;环绕&#34;有必要的。如果您耗尽了套牌,您可以重新洗牌(减去手中的牌,这将是deckbottom之间的top条目,并重置top和{{1 }} 有必要的。

在纸上玩一会儿,这应该是显而易见的。

修改

在这里解决问题:

  

此时您将卡片[5]分配给卡片,例如

这发生在bottom循环中deal函数中。我们第一次致电for时,我们会告诉它处理deal手:

hero

此时,deal( hero, HANDSIZE, deck, DECKSIZE, &top ) 为0.假设我们一次处理5张卡片,第一次拨打top会有效地执行此操作:

deal

当函数返回时,Loop iteration 0: hero[0] = deck[0] Loop iteration 1: hero[1] = deck[1] Loop iteration 2: hero[2] = deck[2] Loop iteration 3: hero[3] = deck[3] Loop iteration 4: hero[4] = deck[4] 变量已更新为5. next 时间我们调用top,我们告诉它处理deal手:

villain

同样,假设我们一次处理5张牌,循环有效地做到了这一点:

deal( villain, HANDSIZE, deck, DECKSIZE, &top ) 

第二次致电Loop iteration 0: villain[0] = deck[5]; Loop iteration 1: villain[1] = deck[6]; Loop iteration 2: villain[2] = deck[7]; Loop iteration 3: villain[3] = deck[8]; Loop iteration 4: villain[4] = deck[9]; 后,deal已更新为10.

每次使用top变量拨打deal时,它都会从top开始在deck指定的位置处理,并且每次通过循环时都会将{1}添加到top。如果两个条件之一为真,则循环将退出:

  1. top - 我们已经处理了此手所需的所有卡片
  2. i == handsize - 我们已经到达阵列的末尾,也不会发出更多的牌。
  3. 所以,假设你已经交出了很多牌,并且牌组中只剩下3张牌 - 如果你试图再买5张牌,那么圈子会在你之前退出已经处理了所有5张牌,并且我们将返回0以表示牌组中不再有牌。

      

    在哪个桌面随机洗牌?

    在第一次调用*top == decksize之前,您可以调用shuffle函数执行此操作:

    deal

    int deck[DECKSIZE] = { ... }; ... shuffle( deck, DECKSIZE ); ... if ( deal( hero, HANDSIZE, deck, DECKSIZE, &top ) && deal( villain, HANDSIZE, deck, DECKSIZE, &top ) ) { ... } 函数的简单(而非非常好)实现将是:

    shuffle

    基本上,这样做是将每个void shuffle( int *deck, size_t decksize ) { for ( size_t i = 0; i < decksize; i++ ) { int r = rand() % (decksize - i) int tmp = deck[i+r]; deck[i+r] = deck[i]; deck[i] = tmp; } } deck[i]deck[i]中随机选择的元素交换(意味着该元素可能保留在原位)。假设我们有5张牌。第一次通过循环时,deck[decksize-1]指向第一张卡片。我们随机选择i的偏移量并将其称为i

    r

    然后我们交换i --> 1 2 3 4 <-- r 5 deck[i]的内容,然后提前deck[i+r]

    i

    我们从其余元素中随机选择另一个 4 i --> 2 3 1 5

    r

    并进行另一次交换并提前 4 i --> 2 3 1 5 <-- r

    i

    泡沫,冲洗,重复 - 在循环结束时,阵列或多或少随机改组。

答案 1 :(得分:2)

不,C中没有办法从数组中删除元素。实际上,据我所知,没有办法从C ++,C#或Java中删除数组中的元素。

现在,既然如此,你有几个选择。您可以在代码中使用sentinel值将元素标记为不存在。您可以调整数组的大小以实际将其缩小一,并在删除元素时将所有元素向后重定位到一个位置。

在您的示例中,0或-1应该可以作为哨兵值使用。

答案 2 :(得分:0)

首先,让数组卡片静态。 接下来,引入另一个静态变量,该变量最初等于array = 52的长度。

现在当处理随机位置的牌时,它可以与牌组中的最后一张牌交换,用arraysize-1索引,并且阵列大小减少。

使这两个变量保持静态允许函数在调用之间维持状态。当然,更好的技术会将这些变量封装在一个结构中(允许例如多个游戏/重置套牌)。

简而言之,您真正想要解决的问题不是删除数组中的条目。相反,它是一个更具包容性的问题:如何从固定域执行重复随机选择而不重复选择值。上述方法是一种方法。下面使用20个插槽阵列的技术示例如下(对于短输出线(易于读取)选择值20。它可以很容易地为52,1000等):

<强>代码

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define DECK_SIZE  20

typedef struct Deck
{
    int cards[DECK_SIZE];
    size_t size;
} Deck;

Deck init_deck()
{
    // no shuffle is needed. each selection in draw_card will
    //  pick a random location in the remaining cards.
    Deck deck;
    for (int i=0; i<DECK_SIZE; ++i)
        deck.cards[i] = i+1;
    deck.size = DECK_SIZE;
    return deck;
}

int draw_card(Deck *deck)
{
    // reset when we run out of cards.
    if (deck->size == 0)
    {
        printf("- reset \n");
        deck->size = DECK_SIZE;
    }

    // generate random location in remaining cards.
    //  note: susceptible to modulo bias
    size_t idx = rand() % deck->size;

    // decrement size to consume card and index where
    //  the card will be swap-stored.
    --deck->size;

    // swap with last card in remaining cards
    int tmp = deck->cards[deck->size];
    deck->cards[deck->size] = deck->cards[idx];
    deck->cards[idx] = tmp;

    // return the drawn card
    return deck->cards[deck->size];
}

int main()
{
    Deck deck = init_deck();

    srand((unsigned)time(NULL));

    // draw 300 times. this should reset 15 times, and
    //  each time a new set of 1..20 should result
    for (int i=0; i<300; ++i)
        printf("%d ", draw_card(&deck));

    fputc('\n', stdout);
}

输出(显然会有所不同)

7 16 3 20 9 13 6 4 1 12 18 10 14 2 8 17 11 5 15 19 - reset 
6 20 14 16 11 2 10 13 4 12 18 5 3 7 19 9 17 8 15 1 - reset 
14 1 8 15 13 2 19 16 11 17 5 18 9 12 7 6 3 20 4 10 - reset 
18 17 12 2 15 19 1 4 14 10 20 16 9 5 11 13 6 8 3 7 - reset 
4 18 5 1 19 16 8 10 9 14 13 17 12 20 7 2 15 6 11 3 - reset 
14 16 18 1 5 10 17 3 19 9 8 2 7 13 12 20 4 15 11 6 - reset 
16 15 12 13 6 1 17 10 9 7 11 20 8 19 2 18 3 4 14 5 - reset 
7 1 8 16 17 5 2 12 13 6 18 20 9 11 14 19 15 3 4 10 - reset 
20 13 4 18 7 17 12 15 5 14 2 16 11 3 9 10 1 19 8 6 - reset 
5 19 4 17 18 13 8 2 12 7 9 1 11 10 3 14 6 15 16 20 - reset 
3 5 10 7 1 15 19 13 16 12 9 8 6 20 4 11 17 18 14 2 - reset 
11 14 4 7 15 9 16 18 8 13 12 5 10 19 2 6 20 1 3 17 - reset 
10 18 2 4 12 20 14 11 16 13 3 9 8 6 5 7 17 1 15 19 - reset 
19 12 20 11 13 9 5 1 10 15 7 2 17 6 3 4 8 14 16 18 - reset 
10 3 19 4 6 14 18 11 1 7 9 16 8 13 17 20 2 5 15 12 

注意每行上有20个选项,1..20中的每个数字每行一次。这项技术如何为您服务取决于您。一个有价值的挑战将是如何在重置(通常称为牌组翻转)发生时枚举现有玩家所持有的牌。这是一个需要解决的有趣问题,而不是一个看似直观的问题。

希望它有所帮助。

答案 3 :(得分:-2)

您可以使用几种解决方案来解决问题:

首先,使用vector类,尤其是insert和remove函数。这是一个很好的解释它是如何工作的: C++ Vectors

其次,我最喜欢的是使用数字或布尔值作为指标。例如,声明一个与你的牌组长度相同的简单布尔数组。所有元素的初始值都是真的。对于每张卡,你想从牌组中删除,只需将其值更改为false即表示它已消失。