C言語で生徒の成績をソート(単純選択ソートとヒープソート)

1. 選択ソート

選択ソートの基本的な考え方は次のとおりです。各パス (i 番目のパスなど) は、次の n-i+1 (i=1,2...,n-1) の中で最小のキーを持つ要素を選択します。順序付けされたソート対象の要素 サブシーケンスの i 番目の要素。n-1 番目のパスが完了するまで、ソート対象の要素は 1 つだけであるため、再度選択する必要はありません。選択ソートのヒープ ソート アルゴリズムは、長年にわたり研究の焦点となってきました。

2. 簡易選択ソート

1. アルゴリズム思考

上記の選択ソートの考え方によれば、単純な選択ソート アルゴリズムのアイデアは直感的に導き出すことができます。ソート テーブルが [L...n] であると仮定すると、i 番目のソートは次の要素を選択することです。 Li.n] と I(i) Exchange からの最小キーを使用すると、各ソートによって要素の最終位置が決定されるため、n-1 個のソート後にソート テーブル全体を順序付けることができます。

2. アルゴリズムの実装

//简单选择排序
void selectsort(SqList &L){
    
    
	for(int i=0;i<L.length-1;i++){
    
          //一共进行n-1趟
		Elemtype min=L.data[i];        //记录最小的元素位置
		int n=0;
		for(int j=i+1;j<L.length;j++){
    
      //从未排序部分开始遍历
			if(L.data[j].grade<min.grade) {
    
    
				min=L.data[j];
				n=j;
			}
		}
		if(min.grade!=L.data[i].grade){
    
    
			Elemtype temp=L.data[i];
			L.data[i]=L.data[n];
			L.data[n]=temp;
		}
	}
}

3. 効率分析

単純な選択ソート アルゴリズムのパフォーマンス分析は次のとおりです。

  • スペース効率: 一定数の補助ユニットのみが使用されるため、スペース効率は 0(1) です。
  • 時間効率: 上記の疑似コードから、単純な選択と並べ替えのプロセスでは、要素の移動の操作回数が非常に少なく、3(n-1) 回以下であることがわかります。このとき、対応するリストはすでに順序付けされていますが、要素間の比較回数はシーケンスの初期状態とは関係なく、常に n(n- 1)/2 回になります。したがって、時間計算量は常に 0(n 2 )になります。
  • 安定性: i 番目のパスで最小要素を見つけた後、i 番目の要素と交換すると、i 番目の要素と同じキーワードを含む要素の相対位置が変化する可能性があります。たとえば、テーブル L={2, 2,1 } では、L={1, 2,2 } をソートした後、最終的なソート順序も L={1,2,2} になります。これは明らかに 2 と 2 の相対順序です。 2が変わりました。したがって、単純な選択ソートは不安定なソート方法です。

3. ヒープソート

1. アルゴリズム思考

ヒープのソートの考え方は非常に単純です: まず、L1...n] に格納されている n 個の要素が初期ヒープに組み込まれます。ヒープ自体の特性により (例として大きな上部ヒープを取り上げます)、ヒープの最上位要素が最大値です。通常、ヒープの最上位要素を出力した後、ヒープの最下位要素をヒープの最上位に送信しますが、このとき、ルートノードは最上位ヒープの性質を満たさなくなり、ヒープは破棄されます。ヒープの最上位要素を下方に移動して、大きな最上位ヒープの性質を維持し続けてから、ヒープの最上位要素を出力します。ヒープ内に要素が 1 つだけ残るまで、これを繰り返します。ヒープのソートでは次の 2 つの問題を解決する必要があることがわかります: ①順序のないシーケンスを最初のヒープにどのように構築するか? ② ヒープの最上位の要素を出力した後、残りの要素を新しいヒープにどのように調整するか?

ヒープソートの鍵は、初期ヒープを構築することです。n 個のノードからなる完全な二分木では、最後のノードは Ln/2 番目のノードの子になります。Ln/2 番目のノードをルートとするサブツリーをフィルタリングし (大きなルート ヒープの場合、ルート ノードのキーが左右の子の中で大きいキーを持つキーよりも小さい場合は交換します)、サブツリーはヒープです。次に、各ノード (Ln/2J-1~1) をルートとするサブツリーをフィルタリングして、ノードの値がその左右の子ノードの値より大きいかどうかを確認します。そうでない場合は、左右の子ノードノード内の大きい値が交換され、交換後に次のレベルのヒープが破棄される可能性があるため、ノードをルートとするサブツリーがヒープを形成するまで、上記の方法を使用して次のレベルのヒープを構築し続けます。上記のヒープ調整方法を繰り返し使用して、ルート ノードまでヒープを構築します。

2. 調整例

最初に L(4) サブツリー 09 < 32 を調整し、交換し、交換後のヒープの定義を満たします。引き続き L(3) サブツリーを前方に調整します (78<左右の子の大きい方 87)。交換し、交換後のヒープの定義を満たす; L(2) サブツリーを前方に調整します (17 < 左右の子のうち大きい方 45)。交換後のヒープの定義を満たす; ルート ノード L まで前方に調整します(1), 53 < 左右の子のうち大きい方 87. 交換 交換後、L(3) サブツリーのヒープは破棄されます。上記の方法を使用して L(3) を調整します。53< 大きい方78. Exchange では、これまでのところ、完全なバイナリ ツリーはヒープの定義を満たしています。

ここに画像の説明を挿入

3. C言語の実装

void Heapsort(SqList &L){
    
    
	buildMaxheap(L);
	for(int i=L.length-1;i>0;i--){
    
    
		Elemtype temp=L.data[i];
		L.data[i]=L.data[0];
		L.data[0]=temp;
		HeadAdjust(L,0, i);
	}
}

4. 効率分析

ヒープ ソート アルゴリズムのパフォーマンス分析は次のとおりです。

  • スペース効率: 一定数の補助ユニットのみが使用されるため、スペースの複雑さは 0(1) です。
  • 時間効率: ヒープの構築にかかる時間は O(n) で、その後 n-1 回の下方調整操作があり、各調整の時間計算量は O(h) であるため、最良の場合、最悪の場合、および平均的な場合において、ヒープはソート時間計算量は O(nlog2n) です。
  • 安定性:スクリーニングの際、同じキーワードを持つ以下の要素を前に調整することが可能であるため、ヒープソートアルゴリズムは不安定なソート方法です。たとえば、テーブル L= {1, 2 , 2} の場合、初期ヒープを構築するときに、2 がヒープの先頭に交換される可能性があります。このとき、L= { 2 , 1,2} となり、最終的なソート順序は次のようになります。 L={1,2, 2 } 、明らかに2と 2 の相対順序が変更されています。

4. C言語実装の完全な例

/*我们今天的主角插入排序是基于查找算法来的,所以我们还是利用线性表来进行模拟*/

/*为了便于我们后面演示希尔排序,所以我们采用顺序存储结构*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define MaxSize 50                //这里只是演示,我们假设这里最多存五十个学生信息

//定义学生结构
typedef struct {
    
    
	char name[200];              //姓名
	int  grade;               //分数,这个是排序关键字
} Elemtype;

//声明使用顺序表
typedef struct {
    
    
	/*这里给数据分配内存,可以有静态和动态两种方式,这里采用动态分配*/
	Elemtype  *data;            //存放线性表中的元素是Elemtype所指代的学生结构体
	int length;                 //存放线性表的长度
} SqList;						//给这个顺序表起个名字,接下来给这个结构体定义方法

//初始化线性表
void InitList(SqList &L){
    
    
	/*动态分配内存的初始化*/
	L.data = (Elemtype*)malloc(MaxSize * sizeof(Elemtype));  //为顺序表分配空间
	L.length = 0;                                            //初始化长度为0
}

//求表长函数
int Length(SqList &L){
    
    
	return L.length;
}

//求某个数据元素值
bool GetElem(SqList &L, int i, Elemtype &e) {
    
    
	if (i < 1 || i > L.length)
		return false;         //参数i错误时,返回false
	e = L.data[i - 1];      //取元素值
	return true;
}

//输出线性表
void DispList(SqList &L) {
    
    
	if (L.length == 0)
		printf("线性表为空");
	//扫描顺序表,输出各元素
	for (int i = 0; i < L.length; i++) {
    
    
		printf("%s        %d", L.data[i].name,  L.data[i].grade);
		printf("\n");
	}
	printf("\n");
}

//插入数据元素
bool ListInsert(SqList &L, int i, Elemtype e) {
    
    
	/*在顺序表L的第i个位置上插入新元素e*/
	int j;
	//参数i不正确时,返回false
	if (i < 1 || i > L.length + 1 || L.length == MaxSize)
		return false;
	i--;                //将顺序表逻辑序号转化为物理序号
	//参数i正确时,将data[i]及后面的元素后移一个位置
	for (j = L.length; j > i; j--) {
    
    
		L.data[j] = L.data[j - 1];
	}
	L.data[i] = e;      //插入元素e
	L.length++;         //顺序表长度加1
	return true;
	/*平均时间复杂度为O(n)*/
}

//简单选择排序
void selectsort(SqList &L){
    
    
	for(int i=0;i<L.length-1;i++){
    
          //一共进行n-1趟
		Elemtype min=L.data[i];        //记录最小的元素位置
		int n=0;
		for(int j=i+1;j<L.length;j++){
    
      //从未排序部分开始遍历
			if(L.data[j].grade<min.grade) {
    
    
				min=L.data[j];
				n=j;
			}
		}
		if(min.grade!=L.data[i].grade){
    
    
			Elemtype temp=L.data[i];
			L.data[i]=L.data[n];
			L.data[n]=temp;
		}
	}
}

void HeadAdjust(SqList &L,int k, int len){
    
    
	Elemtype temp=L.data[k];
	for(int i=2*k+1;i<len;i=2*i+1){
    
    
		if(i<len-1 && L.data[i].grade<L.data[i+1].grade)
			i++;
		if(temp.grade>=L.data[i].grade)
			break;
		else{
    
    
			L.data[k]=L.data[i];
			k=i;
		}
	}
	L.data[k]=temp;
}

void buildMaxheap(SqList &L){
    
    
	for(int i=L.length/2-1;i>=0;i--)
		HeadAdjust(L, i, L.length);
}

void Heapsort(SqList &L){
    
    
	buildMaxheap(L);
	for(int i=L.length-1;i>0;i--){
    
    
		Elemtype temp=L.data[i];
		L.data[i]=L.data[0];
		L.data[0]=temp;
		HeadAdjust(L,0, i);
	}
}

int main(){
    
    
	SqList L;
	Elemtype stuents[10]={
    
    {
    
    "张三",649},{
    
    "李四",638},{
    
    "王五",665},{
    
    "赵六",697},{
    
    "冯七",676},
		{
    
    "读者",713},{
    
    "阿强",627},{
    
    "杨曦",649},{
    
    "老六",655},{
    
    "阿黄",604}};
	//这一部分忘了的请回顾我的相关博客
	printf("初始化顺序表并插入开始元素:\n");
	InitList(L);         //这时是一个空表,接下来通过插入元素函数完成初始化
	for (int i = 0; i < 10; i++)
		ListInsert(L, i + 1, stuents[i]);
	DispList(L);
	/*printf("根据分数进行简单选择排序后结果为:\n");
	selectsort(L);
	DispList(L);          //到这一步我们的简单选择排序没什么问题的
	 */
	printf("根据分数进行堆排序后结果为:\n");
	Heapsort(L);
	DispList(L);
}

5. 走行結果

1. 簡易選択ソート

ここに画像の説明を挿入

2. ヒープソート

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/weixin_51496226/article/details/131673994