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

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

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

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ソート

複数のデータを、順序性に従って並べ替えること。 データ処理を行う際に頻繁に用いられ、多くのアルゴリズムが存在します。速度、容量、複雑さなどに違いがあり、高速性に特化したものにクイックソートがあります。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

2回答

1028閲覧

ヒープソートでうまくソートできていないときがある

langhtorn

総合スコア104

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ソート

複数のデータを、順序性に従って並べ替えること。 データ処理を行う際に頻繁に用いられ、多くのアルゴリズムが存在します。速度、容量、複雑さなどに違いがあり、高速性に特化したものにクイックソートがあります。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

0クリップ

投稿2020/11/09 04:47

###実現したいこと
ヒープソートで配列aを小さい順に並べたい。
###問題点
できたとき。

n=7 34 12 54 23 72 345 100 12 23 34 54 72 100 345

できないとき。

n=10 2 6 3 213 12 543 84 23 15 100 2 3 6 12 15 23 84 213 100 543

213より100が大きいことになってしまいます。
途中経過を出力したりしてみましたが原因がわかりません。
どうしてこのようになるのか教えてください。
###コード

C

1//ヒープソート 2#include<stdio.h> 3 4//aとbの値を交換する関数 5void swap(int *a,int *b) 6{ 7 int tmp; 8 tmp=*b; 9 *b=*a; 10 *a=tmp; 11} 12 13//ヒープソート 14void heap_sort(int a[],int n) 15{ 16 int i,j,k,h[n],w; 17 18 h[0]=a[0]; 19 for(k=1;k<n;k++){ 20 i=k; 21 h[i]=a[i]; //新しいデータを一番下段・左から詰めた最後にi●仮置きします 22 j=(i-1)/2; 23 while (1){ 24 if(h[j]>h[i]){ //親jのデータが子iのデータよりも大きければ 25 swap(&h[j],&h[i]); //二つのデータを交換します 26 i=j; //ヒープ条件を調べる子をj 27 j=(i-1)/2; //親をjの親のように更新します 28 }else{ 29 break; 30 } 31 } 32 } 33 /* 34 printf("ソ\ート前 "); 35 for(i=0;i<n;i++){ 36 printf("%d ",h[i]); 37 } 38 printf("\n"); 39 */ 40 //ソートする 41 i=1,j=0; 42 int num=n; //配列hのデータ数 43 for(k=0;k<n;k++){ 44 i=1,j=0; 45 a[k]=h[0]; //取り除いて別に用意するソート列の最初に置きます. 46 h[0]=h[num-1]; //ヒープの空いた場所に,ヒープの一番最後のデータを移動します● 47 num--; //ヒープのデータ数を1減らします 48 //printf("ソ\ート中 "); 49 /* 50 for(w=0;w<num;w++){ 51 printf("%d ",h[w]); 52 } 53 */ 54 55 while(i<num){ 56 //printf("j=%d i=%d\n",j,i); 57 //printf("while中 \n"); 58 /* 59 for(w=0;w<num;w++){ 60 printf("%d ",h[w]); 61 } 62 */ 63 64 if(h[j]>h[i]){ //親が子よりも大きい場合 65 if(h[2*j+1]<h[2*(j+1)]){ //子の中で小さい方と交換すること 66 //printf("h[2*j+1]=%d\n",h[2*j+1]); 67 swap(&h[2*j+1],&h[j]); 68 j=i; 69 i=2*(j+1); //下の段へ 70 }else{ 71 //printf("h[2*(j+1)]=%d\n",h[2*(j+1)]); 72 swap(&h[2*(j+1)],&h[j]); 73 j=i; 74 i=2*(j+1); 75 } 76 }else{ 77 //printf("while出る\n"); 78 break; 79 } 80 } 81 82 83 } 84 85} 86 87 88int main(void) 89{ 90 int i,n,a[10]; 91 printf("n="); 92 scanf("%d",&n); 93 for(i=0;i<n;i++){ 94 scanf("%d",&a[i]); 95 } 96 heap_sort(a,n); 97 98 printf("\n"); 99 for(i=0;i<n;i++){ 100 printf("%d ",a[i]); 101 } 102 103 return 0; 104}

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

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

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

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

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

guest

回答2

0

ベストアンサー

質問のコードがよく分からないので、全部書き直してみました。

C

1#include <stdio.h> 2 3#define swap(x, y) (c = x, x = y, y = c) 4 5void heap_sort(int a[], int n) 6{ 7 int c, i, j, k = 0; 8 while (++k < n) 9 for (j = k; j > 0 && a[i = (j-1)/2] < a[j]; j = i) 10 swap(a[i], a[j]); 11 while (--k > 0) { 12 swap(a[0], a[k]); 13 for (i = j = 0; ; j = i) { 14 c = (j + 1) * 2; // c: right child of j 15 if (c > k) break; 16 if (a[c-1] > a[i]) i = c-1; // c-1: left child of j 17 if (c < k && a[c] > a[i]) i = c; 18 if (i == j) break; 19 swap(a[i], a[j]); 20 } 21 } 22} 23 24int main(void) 25{ 26 int i,n,a[10]; 27 printf("n="); 28 scanf("%d",&n); 29 for(i=0;i<n;i++){ 30 scanf("%d",&a[i]); 31 } 32 heap_sort(a,n); 33 34 for(i=0;i<n;i++){ 35 printf("%d ",a[i]); 36 } 37 printf("\n"); 38 39 return 0; 40}

追記
元のコードの while だけを次のように修正すればよいでしょう。

diff

1- while(i<num){ 2- //printf("j=%d i=%d\n",j,i); 3- //printf("while中 \n"); 4- /* 5- for(w=0;w<num;w++){ 6- printf("%d ",h[w]); 7- } 8- */ 9- 10- if(h[j]>h[i]){ //親が子よりも大きい場合 11- if(h[2*j+1]<h[2*(j+1)]){ //子の中で小さい方と交換すること 12- //printf("h[2*j+1]=%d\n",h[2*j+1]); 13- swap(&h[2*j+1],&h[j]); 14- j=i; 15- i=2*(j+1); //下の段へ 16- }else{ 17- //printf("h[2*(j+1)]=%d\n",h[2*(j+1)]); 18- swap(&h[2*(j+1)],&h[j]); 19- j=i; 20- i=2*(j+1); 21- } 22- }else{ 23- //printf("while出る\n"); 24- break; 25- } 26- } 27 28+ i = j = 0; // j は親、i は親子のうち小さいもの 29+ while (2*j+1 < num) { // 子がヒープの範囲内 30+ if (h[2*j+1] < h[j]) i = 2*j+1; // 左の子が親より小さい 31+ if (2*(j+1) < num && h[2*(j+1)] < h[i]) i = 2*(j+1); 32+ // 右の子が最も小さい 33+ if (i == j) break; // 親が子より小さい 34+ swap(&h[i], &h[j]); // 小さいほうの子を親と交換 35+ j = i; // 子を新しい親とする 36+ }

heap_sort関数の中で、int h[n]; を宣言していますが、
これは可変長配列で、コンパイラによってはこの機能をサポートしていません。

質問のコードでは、h[0] が最小値になるヒープを構成し、h[0] を a[0] にコピー。
h[0] に h[n-1] をコピーし、長さが 1小さくなったヒープを再構成し、
新しい最小値 h[0] を a[1] にコピー。
これを繰り返して、ソート結果を配列 a に入れています。

次のようにしてもソートはできます。

h[0] が「最大値」になるヒープを構成し、h[0] を a[n-1] にコピー。
h[0] に h[n-1] をコピーし、長さが 1小さくなったヒープを再構成し、
新しい「最大値」 h[0] を a[n-2] にコピー。
これを繰り返して、ソート結果が配列 a に入る。

こうすると、ヒープが小さくなるたびに最大値を後ろに置くことができます。
すなわち、元の配列 a をそのままソートでき、配列 h は要らないということです。

投稿2020/11/11 06:25

編集2020/11/12 08:36
kazuma-s

総合スコア8224

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

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

langhtorn

2020/11/12 13:20

丁寧な説明をありがとうございます。おかげでよくわかりました。
guest

0

if(h[j]>h[i]){ //親が子よりも大きい場合 if(h[2*j+1]<h[2*(j+1)]){ //子の中で小さい方と交換すること

子は2つあるのに親が子よりも大きい場合がif(h[j]>h[i])のみで判定はできません。

また、2*(j+1) < numかつh[2*j+1]が親よりも小さい場合を考慮していない事が気になります。

投稿2020/11/09 07:52

asm

総合スコア15147

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

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

langhtorn

2020/11/12 13:20

なぜできなかったのか理由がわかりました。ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問