自己瞎写的堆排序算法(C语言实现)

读者朋友,如果你想直接看代码块,可以拉到下面。这里我把我一开始写的,和看了老师的代码后写的,都列出来了,请留意我的备注。

自己看了一遍老师画的图示,就想写堆排序。写到是写出来了,也可以正常编译运行,甚至到得出排序结果都没有任何问题。

堆排序的算法主要有两个关键之处:

1.在无序数组的基础上创建大根堆或小根堆。

2.对首位互换后的堆进行调整,使其重新变为标准的大根堆或小根堆。

我自己写的算法中,为了解决上述两个问题,写了一个核心的函数,即下面代码块中的SingleExchangeforCreate,主要输入需要处理的数组的开始之处的下标,把这个下标的元素与其两个孩子进行比较,如需要,则进行交换。顾名思义,SingleExchangeforCreate就是一次性交换,这个函数每次只处理一个双亲和它的左右孩子。

这个函数本身是没有问题的,但是问题在于实现1和2时,需要以不同的方式调用。

实现1时,我的思路是从i = n/2个元素开始,进行i–,依次对所有的下标为n/2到下标为1的双亲进行操作、与其孩子进行置换。

实现2时,我有两个思路。第一个思路就是上面实现1的思路。我从堆尾开始执行SingleExchangeforCreate一直执行到堆头。这样做总觉得效率会低,但是我又说不出个为啥。另一种思路是递归,即在主函数里调用SingleExchange函数,这个函数与SingleExchangeforCreate相比几乎唯一的区别就是加了递归入口。但是递归效率必然更低,因为还要用到递归工作栈之类的。

再后来我看了老师的写法,感觉清爽了不少。老师用了一个HeapAdjust,输入的参数分别是数组本身,需调整的堆的首下标以及其末下标。这个函数的输入值都这么清晰,即就是要调整这个堆从开始到结束的位置的元素。

我的体会就这些。下面把两份代码块贴上来,供大家参考。其中,函数CreateArray和Display分别是生成随机数组和遍历输出数组的函数,与堆排序本身无关。

下面这个代码块里的代码,是我一开始没怎么看老师的代码时写的。

#include<time.h>
#include<stdlib.h>
#include<stdio.h>
//目的:调整大根堆,把小的数换到底下,把大数放到上面
void CreateArray(int*a, int length)//该函数用于生成无序数组,用以排序 
{
	srand((unsigned)time(NULL));//用当前系统时间设置种子
	for(int i=0;i<100;i++)
	{
		a[i]=rand()%121; //用rand函数生成随机数,并赋值给数组a[i]
	}
}
void InnerExchange(int*a, int x, int y)
{
	int t;
	t = a[x];
	a[x] = a[y];
	a[y] = t;
}
void SingleExchangeforCreate(int*a, int n, int randomlength)//n是当前根结点,randomlength是无序部分的长度 
{
	int t;
	if(2*n<randomlength)//length应为下标 
	{
		if(a[2*n] >a[n]) 
		{
			InnerExchange(a, n, 2*n);
		}
		if(a[2*n+1]>a[n]) 
		{
			InnerExchange(a, n, 2*n+1);
		} 
		
	}
	else if(2*n==randomlength)
	{
		if(a[2*n]>a[n]) 
		{
			InnerExchange(a, n, 2*n);
		}
	}
}
void SingleExchange(int*a, int n, int randomlength)//n是当前根结点,randomlength是无序部分的长度 
{
	int t;
	if(2*n<randomlength)//length应为下标 
	{
		if(a[2*n] >a[n]) 
		{
			InnerExchange(a, n, 2*n);
			SingleExchange(a, 2*n, randomlength);
		}
		if(a[2*n+1]>a[n]) 
		{
			InnerExchange(a, n, 2*n+1);
			SingleExchange(a, 2*n+1, randomlength);
		} 
		
	}
	else if(2*n==randomlength)
	{
		if(a[2*n]>a[n]) 
		{
			InnerExchange(a, n, 2*n);
			SingleExchange(a, 2*n, randomlength);
		}
	}
}
void CreateHeap(int*a, int length)//length应为下标,即接受的值应该比数组长度大1 
{
	for(int j = length/2; j>=1; j--)
	{
		SingleExchangeforCreate(a, j, length);
	}
}
void HeapSort(int*a, int length)//length应为下标,即接受的值应该比数组长度大1 
{
	CreateHeap(a, length);
	while(length!=1)
	{
		a[0]= a[1];
		a[1] = a[length];
		a[length--] = a[0];
//		for(int j = length/2; j>=1; j--)
//		{
//			SingleExchange(a, j, length);
//		}
		SingleExchange(a,1,length);
	}
}
void Display(int *a, int length)
{
	for(int i=0; i<length; i++)
	{
		printf("%4d", a[i]);
		if(i%10==9) putchar('\n');
	}
}
int main(void)
{
	int a[100];
	CreateArray(a, sizeof(a)/sizeof(a[0]));
	Display(a, sizeof(a)/sizeof(a[0]));
	puts("\n\n");
	HeapSort(a, sizeof(a)/sizeof(a[0])+1);
	Display(a, sizeof(a)/sizeof(a[0]));
	return 0;
}

下面这个代码块里的代码,是我看了老师的代码后写的。

#include <time.h>
#include <stdlib.h>
#include <stdio.h>
//目的:调整大根堆,把小的数换到底下,把大数放到上面,这样,就可以从小到大排序 
void CreateArray(int*a, int length)//该函数用于生成无序数组,用以排序 
{
	srand((unsigned)time(NULL));//用当前系统时间设置种子
	for(int i=0;i<100;i++)
	{
		a[i]=rand()%121; //用rand函数生成随机数,并赋值给数组a[i]
	}
}
void HeapAdjust(int*a, int imin, int imax)//imin = MIN(the index of the array) and imax = MAX(the index of the array)
{
	a[0] = a[imin];//use a[0] to temporarily restore a[imin] 
	for(int j = 2*imin; j<=imax; j*=2)
	{
		if(j<imax && a[j]<a[j+1]) j++;
		if(a[j]<a[0]) break;
		a[imin] = a[j]; imin = j;
	}
	a[imin] = a[0];
} 
void HeapCreate(int*a, int imax)
{
	for(int i=imax/2; i>=1; i--)
	{
		HeapAdjust(a, i, imax);
	}
}
void Swap(int* a, int imax)
{
	a[0] = a[imax];
	a[imax] = a[1];
	a[1] = a[0];
}
void HeapSort(int*a, int imax)
{
	HeapCreate(a, imax);
	while(imax>1)
	{
		Swap(a, imax--);//把数组a中下标为1的元素与下标为imax的元素进行互换 
		HeapAdjust(a, 1, imax); 
	}
} 

void Display(int *a, int length)
{
	for(int i=0; i<length; i++)
	{
		printf("%4d", a[i]);
		if(i%10==9) putchar('\n');
	}
}

int main(void)
{
	int a[100];
	CreateArray(a, sizeof(a)/sizeof(a[0]));
	Display(a, sizeof(a)/sizeof(a[0]));
	puts("\n\n");
	HeapSort(a, sizeof(a)/sizeof(a[0])+1);
	Display(a, sizeof(a)/sizeof(a[0]));
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值