简体   繁体   English

从C中的数组中删除元素

[英]Deleting an element from an array in C

so Im supposed to make a game an assignment for class. 因此,我应该将游戏分配给班级。

Essentially, I decided to re-create a heads up Poker game, running different functions such as int deal(a, b, x, y) where a and b are the hero's cards, and x and y are the villan's. 本质上,我决定重新创建一个平视的扑克游戏,运行不同的功能,例如int deal(a, b, x, y) ,其中ab是英雄的牌, xy是反派的。

This function in particular, has me a bit stumped. 特别是这个功能让我有些难过。 Effectively, I loop through the array deck , and assign random numbers to a , b , x and y . 实际上,我遍历了数组deck ,并为abxy分配了随机数。 Then, I will translate each assigned value into a real, unique card, and return that to int main() . 然后,我将每个分配的值转换为一个真实的唯一卡,并将其返回给int main()

The part that I am stuck at is " as each card is selected, it is deleted from the array. It seems that there is no easy way in C to simply remove an element from deck array. 我被卡住的部分是“ 选择了每张卡时,它会从阵列中删除。似乎C中没有简单的方法从deck阵列中删除元素。

int deal (a, b, x, y)
{

    int deck[52] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30
                    31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52};
    int heroFirst;
    int heroSecond;

    int villainFirst;
    int villainSecond;

    srand(0);

}

Any thoughts? 有什么想法吗?

You don't have to delete anything. 您无需删除任何内容。

Shuffle your deck array (using a Fisher-Yates shuffle or similar algorithm). 洗牌的deck阵列(使用费雪耶茨洗牌或类似的算法)。 Deal each card from the "top" of the deck: 从卡组的“顶部”处理每张卡:

int top = 0;

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

etc. The top variable is the index of the next available card in the deck. 等等。 top变量是卡组中下一张可用卡的索引。

The general outline of your code will be something like 您的代码的总体轮廓将类似于

#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.
     */
  }
};

Your deal function would look something like 您的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;
}

This function will return 0 if we run out of cards in deck before we've dealt the hand, in which case you'll need to do...something. 如果我们在发deck之前牌deck的牌用完了,则此函数将返回0 ,在这种情况下,您需要执行某些操作。 Good luck! 祝好运!

If you want to deal a partial hand (such as to replace 3 cards), you'd do something like 如果您想发偏手(例如替换三张牌),则可以执行类似的操作

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

This call will overwrite hero[2] through hero[4] with three new cards drawn from deck . 该调用将使用从deck抽取的三张新牌覆盖hero[2]hero[4] With each call to deal , top will be advanced to point to the next available card in the deck. 每次dealtop都会前进,以指向卡片组中下一张可用的卡。

You can write a discard function that returns cards to the deck. 您可以编写一个discard功能,以将卡返回到牌组。 It means keeping a separate bottom variable and updating that: 这意味着要保留一个单独的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;
}

Obviously, bottom should be strictly less than the deck size and strictly less than top on a discard; 显然, bottom应该严格小于甲板的尺寸,并且必须严格小于丢弃物的top otherwise, we haven't managed our deck or hands properly. 否则,我们无法正确处理牌组或牌手。 With a little work, you could make your array "circular", such that top and bottom "wrap around" as necessary. 只需做一些工作,就可以使阵列“圆形”,从而根据需要使topbottom “环绕”。 If you exhaust the deck, you can reshuffle (minus the cards in hand, which will be the deck entries between bottom and top ) and reset top and bottom as necessary. 如果你用尽了甲板上,你可以重新洗牌(减去手上的牌,这将是deck之间的条目bottomtop )和复位topbottom是必要的。

Play with this on paper for a little while, and it should become obvious. 在纸上玩一会儿,它应该很明显。

EDIT 编辑

Addressing questions here: 在这里解决问题:

At which point do you assign deck[5] to a card, for instance 例如,您在哪一点将卡牌[5]分配给一张牌

That happens in the deal function, in the for loop. 这发生在deal函数的for循环中。 The first time we call deal , we tell it to deal to the hero hand: 第一次调用deal ,我们告诉它交易到hero手上:

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

At this point, top is 0. Assuming we're dealing 5 cards at a time, the first call to deal effectively does this: 此时, top为0。假设我们一次要处理5张卡片,则第一个有效地进行deal电话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]

When the function returns, the top variable has been updated to 5. The next time we call deal , we tell it to deal to the villain hand: 当函数返回时, top变量已更新为5。 下次我们调用deal ,我们告诉它处理villain

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

Again, assuming we're dealing 5 cards at a time, the loop effectively does this: 再一次,假设我们一次要处理5张卡,则循环将有效地做到这一点:

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];

After the second call to deal , top has been updated to 10. 在第二个deal请求之后, top已更新为10。

Each time you call deal with the top variable, it will start dealing from deck at the position specified by top , and each time through the loop it will add 1 to top . 每次通话时间dealtop变量,它会开始从交易deck在由指定的位置top ,并通过循环每次它会增加1至top The loop will exit if one of two conditions is true: 如果满足以下两个条件之一,则循环将退出:

  1. i == handsize - we've dealt all the cards necessary for this hand i == handsize手牌i == handsize -我们已经处理了这手牌所需的所有纸牌
  2. *top == decksize - we've reached the end of the array, nor more cards may be dealt. *top == decksize卡牌*top == decksize -我们已经到达数组的末尾,不能再发更多的牌。

So, suppose you've dealt a number of hands, and there are only 3 cards left in the deck - if you try to deal 5 more cards, the loop will exit before you've dealt all 5 cards, and we'll return a 0 to indicate that no more cards are left in the deck. 因此,假设您已经发了很多手牌,而卡组中只剩下3张牌-如果您尝试再发5张牌,则您全部发完5张牌之前 ,循环将退出,我们将返回0表示卡座中没有剩余的纸牌。

at which point is the desk shuffled randomly? 桌子什么时候随机洗了?

You would call a shuffle function to do that before the first call to deal : 您可以在第一个调用deal之前调用shuffle函数来执行此操作:

int deck[DECKSIZE] = { ... };
...
shuffle( deck, DECKSIZE );
...
if ( deal( hero, HANDSIZE, deck, DECKSIZE, &top ) &&
     deal( villain, HANDSIZE, deck, DECKSIZE, &top ) )
{
  ...
}

A simplistic (and not terribly good) implementation of the shuffle function would be: 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;
  }
}

Basically, what this does is swap each deck[i] with a randomly chosen element from deck[i] through deck[decksize-1] (meaning the element may remain in place). 基本上,这是将每个deck[i]交换为一个从deck[i]deck[decksize-1]随机选择的元素(这意味着该元素可能会保留在原位)。 Assume we have 5 cards. 假设我们有5张卡。 The first time through the loop, i points to the first card. 第一次通过循环, i指向第一张卡片。 We pick an offset from i at random and call it r : 我们从i随机选择一个偏移量,并将其称为r

i --> 1
      2
      3
      4 <-- r
      5

We then swap the contents of deck[i] and deck[i+r] , and then advance i : 然后,我们交换deck[i]deck[i+r] ,然后前进i

      4
i --> 2
      3
      1 
      5

We pick another r at random from the remaining elements: 我们从其余元素中随机选择另一个r

      4
i --> 2
      3
      1 
      5 <-- r

and do another swap and advance i : 并进行另一次交换并推进i

      4
      5
i --> 3
      1 
      2 

Lather, rinse, repeat - by the end of the loop, the array is more-or-less randomly shuffled. 泡沫,冲洗,重复-循环结束时,阵列或多或少随机洗牌。

No, there is no way in C to delete an element from an array. 不可以,C语言无法从数组中删除元素。 Indeed, as far as I know there is no way to delete an element from an array in C++, C# or Java either. 实际上,据我所知,也没有办法从C ++,C#或Java中删除数组中的元素。

Now, that being the case, you have a few options. 现在,既然如此,您有几个选择。 You can use a sentinel value in your code to mark an element as absent. 您可以在代码中使用哨兵值将元素标记为不存在。 You can resize your array to actually shrink it by one and relocate all elements backwards one place whenever an element is deleted. 您可以调整数组的大小,以实际将其缩小一,并在删除元素时将所有元素向后移到一个位置。

In your example, either 0 or -1 should serve fine as sentinel values. 在您的示例中,0或-1应该可以很好地用作哨兵值。

First at all, make the array deck static . 首先,使数组卡座为static Next, introduce another static variable initially equal to the lenght of array=52. 接下来,引入另一个静态变量,该变量最初等于array = 52的长度。

Now when a card at a random position is dealt, it can be swapped with the last card in the deck, indexed with arraysize-1, and the arraysize being decreased. 现在,在随机位置处理一张牌时,可以将其与牌组中的最后一张牌交换,并用arraysize-1进行索引,并减小arraysize。

Making these two variables static allows the function to maintain a state between calls. 将这两个变量设为静态允许函数在调用之间保持状态 Of course better techniques would encapsulate these variables in a struct (allowing eg multiple games / resetting the deck). 当然,更好的技术会将这些变量封装在一个结构中(例如允许进行多个游戏/重置牌组)。

In short, the problem you're really trying to solve isn't one of deleting an entry in an array.. Rather, it is a more encompassing issue: How to perform repeated random selection from a fixed domain without repeating selected values. 简而言之,您真正要解决的问题不是删除数组中的一项。而是,它具有更广泛的意义:如何在不重复选定值的情况下从固定域执行重复随机选择。 The method described above is one way to do that. 上述方法是实现此目的的一种方法。 An example of the latter technique using a 20-slot array is below (the value 20 was chosen for short output lines (easy to read). It could just as easily be 52, 1000, etc.): 下面是使用20插槽数组的后一种技术的示例(为短输出线选择了值20(易于读取)。它也很容易为52、1000等):

Code

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

Output (obviously varies) 输出 (明显不同)

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 

Notive that on each line, there are twenty selections, and each number in 1..20 appears exactly once per line. 请注意,每行上有20个选择,并且1..20中的每个数字每行仅出现一次 How this technique serves you is up to you. 该技术如何为您服务取决于您。 A worthy challenge will be how to enumerate the hands held by existing players when a reset (often called a deck-flip) happens. 一个值得挑战的挑战是如何在重置(通常称为套牌翻转)发生时枚举现有玩家的手牌。 That's an interesting problem to solve, and not one that is as intuitive as it may seem. 这是一个有趣的问题,并不是看起来那么直观的问题。

Hope it helps. 希望能帮助到你。

There are a couple of solutions you could use to solve your problem: 您可以使用几种解决方案来解决问题:

First, is using the vector class, especially the insert and remove functions. 首先,使用向量类,尤其是插入和删除函数。 here's a good explanation on how it works: C++ Vectors 这是一个很好的解释: C ++向量

Second, which is also my favorite is to use numbers or booleans as indicators. 其次,我最喜欢的是使用数字或布尔值作为指标。 for example, declare a simple boolean array of the same length as your deck. 例如,声明一个简单的布尔数组,其长度与卡片组的长度相同。 the initial value of all elements would be true. 所有元素的初始值都是真实的。 for each card, you'd like to remove from the deck simply change its value to false to indicate it's gone. 对于每张卡,您都希望将其从卡组中删除,只需将其值更改为false即可表示已消失。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM