質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

934閲覧

トランプ 並べ替え問題 split関数

program777

総合スコア7

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2020/06/01 00:27

前提・実現したいこと

C++で問題を解いています。
以下の関数が問題通り動いているかわかりません。

発生している問題・エラーメッセージ

ジョーカーを除いた52枚のトランプカードが上から順番に
s:スペード, C:クローバー,D:ダイヤ, H:ハートとして
SK SQ SJ S10 S9 S8 S7 S6 S5 S4 S3 S2 SA CK CQ CJ C10 C9 C8 C7 C6 C5 C4 C3 C2 CA DK DQ DJ D10 D9 D8 D7 D6 D5 D4 D3 D2 DA HK HQ HJ H10 H9 H8 H7 H6 H5 H4 H3 H2 HA
積まれています。
これを昇順にする問題です。
その際に使う関数として、一番上(head)から n 枚カードを残しその後に続くカードを切り取って 2 組に分割するsplit関数を作ります。実行後 head 自体は変化なく残った n 枚の組の一番上とします。切り取られた組の一番上のカードのポインタが return されるようにします。
今のところコンパイルは通るのですが、実行しても何も起きません。

該当のソースコード

#include <iostream>
using namespace std;

class Card {//トランプの一枚のカード
private:
int suit; //0-3 各々がH(Heart), D(Diamond), C(Clover), S(Spade)に対応
int number; //1-13
Card *next; //一枚下にあるカードを指すポインタ

public:
Card(int i, int j, Card current_next=NULL){suit = i; number = j; next = current_next;}
friend ostream& operator<<(ostream&, Card);
friend void printCards(Card
head);
friend Card *split(Card *, int );
friend void pileup(Card *, Card *);
};

// Cardの出力オペレータ
ostream& operator<<(ostream &fout, Card c){
// suit = 0, 1, 2, 3 をH, D, C, Sに変換してsuitを表示
if(c.suit==0) fout << "H";
else if(c.suit==1) fout << "D";
else if(c.suit==2) fout << "C";
else fout << "S";
// number = 1 の時はA(Ace)を表示
// number = 2-10の時はそのまま数を表示
// number = 11, 12, 13 の時はJ(Jack), Q(Queen), K(King)を表示
if(c.number==1) fout << "A";
else if(c.number==11) fout << "J";
else if(c.number==12) fout << "Q";
else if(c.number==13) fout << "K";
else fout << c.number;
// 末尾にスペースを一つ表示
fout << ' ';

return fout;
}

//一番上のheadから順番に全てのカードをたどって表示する関数
void printCards(Card *head){

Card *current;

current = head;
int i = 0;
while(current != NULL){//次のカードがなくなるまで繰り返す
//currentの内容を表示し、次のカードに移動
cout << *current;
current = current->next;
// 13枚毎に改行する
i++;
if(i == 13){
cout << endl;
i = 0;
}
}
}

// 一番上(head)から n 枚カードを残しその後に続くカードを切り取って 2 組に分割する関数
// 実行後 head 自体は変化なく残った n 枚の組の一番上となる
// 切り取られた組の一番上のカードのポインタが return される
Card *split(Card *head, int n){
int i = 0;
while(head!=NULL){
head = head->next;
i++;
if(i==n)
break;
}
return head;
}

// head1 のカードの組を head2 の組の上にのせて 1 組にする関数
// 実行後 head1 は変化がなくまとめた組の一番上となる
void pileup(Card *head1, Card *head2){
while(head1!=NULL){
Card *tmp;
tmp = head2;
head2 = head1;
head1->next = tmp;
}
}

// 与えられたリストを逆順にしたリストを返す関数
Card *reverse(Card *head){
Card *new_head, *reverse_head;
reverse_head = NULL;
// カードを 1 枚ずつ head の上から取り去る
// そのカードを別の場所に 1 枚ずつ積んでいく
// 結果として逆順に積まれる
while(head != NULL){
new_head = split(head,1);
pileup(head,reverse_head);
reverse_head = head;
head = new_head;
}
return reverse_head;
}

int main() {

Card *head;//一番上のカードを指すポインタ

for(int i = 0; i < 4; i++){
for(int j = 1; j <= 13; j++){
// 最初のi=0,j=1の時のみ、next=NULLとしてカードを生成
// それ以降は現在のheadをnextとしてカードを生成し、新しいカードをheadとする
if(i==0 && j==1)
head = new Card(0, 1, NULL);
else
head = new Card(i, j, head);
}
}

// カードの表示
printCards(reverse(head));

return 0;
}

試したこと

split関数とpileup関数はすでに自分なり考えて書いてみました。

補足情報(FW/ツールのバージョンなど)

split関数とpileup関数以外は変えずに、2つの関数を追記することでプログラムを完成させます。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

episteme

2020/06/01 01:10

単方向リストを実装し、要素を反転させるのが目的ですか? それとも「リストの実装とか、できればやりたくない(出来合いのがあればそのまま使いたい)」ですか?
program777

2020/06/01 03:22

単方向リストを実装して、要素を反転させるのが目的です! split関数とpileup関数以外はすでに与えられているので、追記するのは変えるとしたらsplit関数とpileup関数ですね。。。また、main 関数内では reverse 関数を利用してカードを昇順に並び替えてから表示しますが、reverse関数の入れる場所は変えても大丈夫です。
guest

回答3

0

ベストアンサー

※ コレも多分望まれる回答ではありません

Deck(Cardの山)を用意し、append(末尾に挿入)/prepend(先頭に挿入)/draw(先頭を取り出す)を定義。
それらを使って反転してみた

C++

1#include <iostream> 2#include <string> 3#include <cassert> 4 5enum class Suit { Heart=0, Diamond=1, Club=2, Spade=3 }; 6 7// Card : 単方向リストの要素 8class Card { 9private: 10 Suit suit_; 11 int rank_; 12 Card* next_; 13 void set_next(Card* card) { next_ = card; } 14public: 15 Card(Suit suit, int rank) : suit_(suit), rank_(rank), next_(nullptr) { 16 assert( rank_ > 0 && rank_ < 14); 17 } 18 19 Suit suit() const { return suit_; } 20 int rank() const { return rank_; } 21 Card* next() const { return next_; } 22 std::string to_string() const; 23 24 friend class Deck; 25}; 26 27std::string Card::to_string() const { 28 std::string result; 29 result += "HDCS"[static_cast<int>(suit_)]; 30 if ( rank_ == 10 ) { 31 result += "10"; 32 } else { 33 result += "_A23456789_JQK"[rank_]; 34 result += " "; 35 } 36 return result; 37} 38 39std::ostream& operator<<(std::ostream& stream, const Card& card) { 40 return stream << card.to_string(); 41} 42 43// Deck : Cardを要素とする単方向リスト 44class Deck { 45private: 46 Card* head_; 47public: 48 Deck() : head_(nullptr) {} 49 ~Deck() { while ( !empty() ) delete draw(); } 50 51 // 内包するCardがなければtrue 52 bool empty() const { 53 return head_ == nullptr; 54 } 55 56 // 内包するCardの枚数 57 int size() const { 58 int result = 0; 59 for ( const Card* p = head_; p; p=p->next() ) { 60 ++result; 61 } 62 return result; 63 } 64 65 // 末尾にcardを挿入 66 void append(Card* card) { 67 assert(card != nullptr); 68 assert(card->next() == nullptr); 69 if ( head_ == nullptr ) { 70 head_ = card; 71 } else { 72 Card* last = head_; 73 for ( Card* p = head_; p; p=p->next() ) { 74 last = p; 75 } 76 last->set_next(card); 77 } 78 } 79 80 // 先頭にcardを挿入 81 void prepend(Card* card) { 82 assert(card != nullptr); 83 assert(card->next() == nullptr); 84 if ( head_ == nullptr ) { 85 head_ = card; 86 } else { 87 card->set_next(head_); 88 head_ = card; 89 } 90 } 91 92 // 先頭のCardを取り出す 93 Card* draw() { 94 Card* card = head_; 95 if ( card != nullptr ) { 96 head_ = head_->next(); 97 card->set_next(nullptr); 98 } 99 return card; 100 } 101 102 // 内包するCardをプリントする 103 void print_to(std::ostream& stream) const { 104 int n = 0; 105 for ( const Card* p = head_; p; p=p->next() ) { 106 stream << *p << ' '; 107 if ( ++n == 13 ) { 108 stream << std::endl; 109 n = 0; 110 } 111 } 112 } 113 114 //内包する全Cardをotherと交換する 115 void swap(Deck& other) { 116 Card* p = head_; 117 head_ = other.head_; 118 other.head_ = p; 119 } 120}; 121 122std::ostream& operator<<(std::ostream& stream, const Deck& d) { 123 d.print_to(stream); 124 return stream; 125} 126 127int main() { 128 Deck deck; 129 for ( Suit suit : { Suit::Heart, Suit::Diamond, Suit::Club, Suit::Spade} ) { 130 for ( int rank = 1; rank < 14; ++rank ) { 131 deck.append(new Card(suit,rank)); 132 } 133 } 134 std::cout << deck << std::endl; 135 136 // deckの並びを反転する 137 { 138 Deck tmp; 139 while ( !deck.empty() ) { 140 // deckの先頭をtmpの先頭に移動 141 tmp.prepend(deck.draw()); 142 } 143 // deckとtmpを交換 144 deck.swap(tmp); 145 } 146 std::cout << "after reverse...\n"; 147 std::cout << deck << std::endl; 148}

投稿2020/06/04 01:27

編集2020/06/04 02:09
episteme

総合スコア16612

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

episteme

2020/06/04 02:20

Deck::draw が (一枚だけの)splitに相当しますね。
program777

2020/06/07 23:04

ちなみにpileupに相当するするところはどこですか?
episteme

2020/06/07 23:14

わかんないの?
program777

2020/06/08 00:28

すみません。 swap関数ですかね...?
episteme

2020/06/08 00:55 編集

Deck::prepend/append でDeckにCardを追加しています。 # ってコメント付けてますし、Deckの基本操作はdraw/append/prependの3つだけです。
program777

2020/06/12 03:56

解決しました! ありがとうございました!
guest

0

C++

1void pileup(Card* head1, Card* head2) { 2 while (head1 != NULL) { 3 Card* tmp; 4 tmp = head2; 5 head2 = head1; 6 head1->next = tmp; 7 } 8}

while内でhead1が変わらないので無限ループになってるのが、いま止まらない理由でしょう。

C++

1// 一番上(head)から n 枚カードを残しその後に続くカードを切り取って 2 組に分割する関数 2// 実行後 head 自体は変化なく残った n 枚の組の一番上となる 3// 切り取られた組の一番上のカードのポインタが return される 4Card *split(Card *head, int n){ 5 int i = 0; 6 while(head!=NULL){ 7 head = head->next; 8 i++; 9 if(i==n) 10 break; 11 } 12 return head; 13}

もう一つここの動作も怪しいです。コメントで「実行後 head 自体は変化なく」と言っているように、この関数は実際には分割しません。いくらかのカード分進めたポインターを返すだけで、元も山は元の状態で残ったままです
(もう一つ小さなミスとしては引数のnが残ったカードの数ではなく、実際は進めるカードの数になっているということ)。ところがreverse内での実際の使用法を見ると、分割される前提で元の山に積み上げていっているので、どんどんカードの枚数が増えていきます(pileupの実装次第ではループした単方向リストになる)。
pileupとsplit以外は与えられてるということなので、実際にsplitするような実装に変えるべきなんでしょう。


split関数の理想的な動作としては[1-> 2-> 3-> 4-> 5-> Null]というようなリストが与えられたとき、[1-> Null]と[2-> 3-> 4-> 5-> Null]の二つのリストができる関数です。つまりどこかでnextにNullを代入するはずです。

コメントのsplit関数については最初のsplit関数と同じ動作です。もしかしたらポインターの働きを勘違いしてるんじゃないでしょうか。

投稿2020/06/01 04:20

編集2020/06/03 05:57
yudedako67

総合スコア2047

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

program777

2020/06/03 05:25

ではsplit関数はこのような感じでいいですか? Card *split(Card *head, int n){ Card *current; current = head; int i = 0; while(current!=NULL){ current = current->next; i++; if(i==n) break; } return current; }
episteme

2020/06/03 06:06

「 つまりどこかでnextにNullを代入するはずです。」 なのですが、そのコードのどこでNULLを代入していますか?
program777

2020/06/03 21:56 編集

すみません、間違えました。 if文の中にreturn currentを入れると、n=1のときのcurrent->next、 つまり、切り取られた組の一番上のカードがリターンされているという感じになると思いますが、、、 Card *split(Card *head, int n){ Card *current; current = head; int i = 0; while(current!=NULL){ current = current->next; i++; if(i==n) return current; break; } } }
program777

2020/06/03 21:55

これで、切り取られた組の一番上のカードがリターンされているという感じになりませんか?
episteme

2020/06/03 22:02

nextの連鎖の途中にNULLを入れて前半と後半に"分断"せんならんのでは?
program777

2020/06/04 01:26

なるほど。。。 Card *split(Card *head, int n){ Card *current; current = head; int i = 0; while(current!=NULL){ current = current->next; i++; if(i==n){ current = NULL; break; } } return head->next; } これで分断できてますかね?
episteme

2020/06/04 01:28

そんなんわざわざ訊かんでも、前半と後半をプリントすりゃわかるやん。
guest

0

※ 望まれる回答ではありません

出来合いの std::list を使った例:

C++

1#include <iostream> 2#include <list> 3 4class Card {//トランプの一枚のカード 5private: 6 int suit_; //0-3 各々がH(Heart), D(Diamond), C(Clover), S(Spade)に対応 7 int rank_; //1-13 8public: 9 Card(int suit, int rank) : suit_(suit), rank_(rank) {} 10 friend std::ostream& operator<<(std::ostream&, const Card&); 11}; 12 13// Cardの出力オペレータ 14std::ostream& operator<<(std::ostream& fout, const Card& c) { 15 return fout << "HDCS"[c.suit_] << "_A23456789TJQK"[c.rank_]; 16} 17 18int main() { 19 std::list<Card> deck; 20 for (int suit = 0; suit < 4; ++suit ) { 21 for (int rank = 1; rank <= 13; ++rank) { 22 deck.emplace_back(suit, rank); 23 } 24 } 25 26 // deck内のカードの表示 27 auto printCard = [](const std::list<Card>& deck ) { 28 int i = 0; 29 for (const Card& card : deck) { 30 std::cout << card << ' '; 31 if (++i == 13) { i = 0; std::cout << std::endl; } 32 } 33 }; 34 35 // fromの先頭からn枚をtoの末尾に移動(splitに相当) 36 auto draw = [](std::list<Card>& from, std::list<Card>& to, size_t n) { 37 to.splice(to.end(), from, from.begin(), std::next(from.begin(), n)); 38 }; 39 40 std::cout <<"deck:\n"; 41 printCard(deck); 42 std::cout << "\n"; 43 44 // 反転して表示 45 deck.reverse(); 46 std::cout << "reversed:\n"; 47 printCard(deck); 48 std::cout << "\n"; 49 50 // デッキトップから13枚を手札に 51 std::cout << "draw 13 cards from deck...\n"; 52 std::list<Card> hands; 53 draw(deck, hands, 13); 54 55 std::cout << "deck:\n"; 56 printCard(deck); 57 std::cout << "\nhand:\n"; 58 printCard(hands); 59 60 61 return 0; 62}

投稿2020/06/01 04:45

episteme

総合スコア16612

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問