刻意练习-堆排序C语言

一、堆排序

  1. 先了解什么是堆。这里的堆,其实是一个完全二叉树。有两个性质

    • 大顶堆的父节点比子节点要大。小顶堆的父节点比子节点要小。
    • 父节点和子节点之间的关系是.父节点的下标是k,子节点就是2k+1/2k+2。
  2. 假设要排序一个数组{1,2,4,3,5}

    按照层序遍历,则现在这个数组在数中的位置是:

    在这里插入图片描述

  3. 现在这个堆还不是一个大顶堆。所以要做如下调整。

    在这里插入图片描述

  4. 得到大顶堆之后,根节点就是整个堆上最最最大的元素。所以可以做如下操作:

    在这里插入图片描述

    将最大的元素换到屁股后面,这样,最大的元素就找到了它的位置。

  5. 然后把剩下的元素继续调整成大顶堆。继续换位置。这种重复的感觉,让我不由自主的想到了递归。

在这里插入图片描述

  1. 最终得到有序的堆。

    在这里插入图片描述

二、抽象

  1. 首先需要一个调整函数,把乱序的堆调整成大顶堆。

    heap_ajust() 函数

  2. 然后需要swap函数,将堆顶的元素放到屁股后面。

    swap函数

  3. 然后需要把剩下的元素调整成大顶堆,把堆顶元素放到屁股后面。

    因为做的操作完全一样,采用递归处理。

  4. 然后拆解heap_ajust函数。我需要先找到不合法的父节点。然后把这个不合法的父节点下移。和子节点调换。

    find_invalid_node_index()函数。

    heap_shift_down()函数。把一个节点下移。

  5. 考虑父节点和子节点的下标

    父节点是index

    子节点就是2*index+1和 2*index+2

三、实现

  1. heap_ajust函数。把数组调整成大顶堆~

    int heap_ajust(int *arr,int arr_len)
    {
    	if(arr == NULL || arr_len <= 1)
    	{
    		return 0;
    	}
    	/*先找到不合法的节点,让它下移,-1表示找不到了,说明这个堆已经OK*/
    	int index_need_down = find_invalid_node_index(arr,arr_len);
    	if(index_need_down == -1)
    	{
    		return 0;
    	}
    	/*把不合法的节点下移*/
    	heap_shift_down(arr,arr_len,index_need_down);
    	
    	/*继续做同样的事情,直到所有的节点合法*/
    	heap_ajust(arr,arr_len);
    	return 0;
    }
    
  2. heap_sort函数。不断的把最大的元素放到屁股后面,这样得到一个从小到大的数组。

    
    int heap_sort(int *arr,int arr_len)
    {
    	if(arr_len <= 1)
    	{
    		return 0;
    	}
    	/*先把数组调整成大顶堆*/
    	heap_ajust(arr,arr_len);
    	
    	/*把第一个和最后一个数字调换*/
    	swap(&arr[0],&arr[arr_len-1]);
    	
    	/*对剩下的堆元素做同样的事情*/
    	heap_sort(arr,arr_len-1);
    	return 0;
    }
    

四、整理

  1. 完整代码

    #include<stdio.h>
    
    int print_arr(int *arr,int arr_len)
    {
    	if(arr == NULL || arr_len == 0)
    	{
    		return -1;
    	}
    	int i = 0;
    	for(i = 0;i < arr_len;i++)
    	{
    		printf("%d,",arr[i]);
    	}
    	printf("\n");
    	return 0;
    }
    
    //换值
    int swap(int *a,int *b)
    {
    	int tmp = *a;
    	*a = *b;
    	*b = tmp;
    	return 0;
    }
    
    int compare(int *a,int *b)
    {
    	if(*a > *b)
    	{
    		return 1;
    	}
    	else if(*a == *b)
    	{
    		return 0;
    	}
    	return -1;
    }
    
    //找到不符合大顶堆规则的父节点下标。
    int find_invalid_node_index(int *arr,int arr_len)
    {
    	if(arr == NULL || arr_len <= 1)
    	{
    		return -1;
    	}
    	int index = 0;
    	for(index = 0;2*index+1 < arr_len;index++)
    	{
    		if(compare(&arr[index],&arr[2*index+1])<0 
    			|| (2*index +2 < arr_len && compare(&arr[index],&arr[2*index+2])<0))
    		{
    			break;
    		}
    	}		
    	
    	if(2*index+1 >= arr_len)
    	{
    		index = -1;
    	}
    	return index;
    }
    
    //将某下标节点上移。
    int heap_shift_up(int *arr,int arr_len,int index)
    {
    	return 0;
    }
    
    //将某下标节点下移。
    int heap_shift_down(int *arr,int arr_len,int index)
    {
    	if(arr == NULL || arr_len <=1 || index < 0 || 2*index+1 >= arr_len)
    	{
    		return -1;
    	}
    	/*先获取到两个子节点比较大的那个的下标*/
    	if(2*index + 2 >= arr_len 
    		|| (compare(&arr[2*index+1],&arr[2*index+2])>0))
    	{
    		/*只有父节点比子节点小,才需要交换*/
    		if(compare(&arr[index],&arr[2*index+1])<0)
    		{
    			swap(&arr[index],&arr[2*index+1]);
    		}
    	}
    	else
    	{
    		if(compare(&arr[index],&arr[2*index+2])<0)
    		{
    			swap(&arr[index],&arr[2*index+2]);
    		}
    	}
    	return 0;
    }
    
    int heap_ajust(int *arr,int arr_len)
    {
    	if(arr == NULL || arr_len <= 1)
    	{
    		return 0;
    	}
    	/*先找到不合法的节点,让它下移,-1表示找不到了,说明这个堆已经OK*/
    	int index_need_down = find_invalid_node_index(arr,arr_len);
    	if(index_need_down == -1)
    	{
    		return 0;
    	}
    	/*把不合法的节点下移*/
    	heap_shift_down(arr,arr_len,index_need_down);
    	
    	/*继续做同样的事情,直到所有的节点合法*/
    	heap_ajust(arr,arr_len);
    	return 0;
    }
    
    int heap_sort(int *arr,int arr_len)
    {
    	if(arr_len <= 1)
    	{
    		return 0;
    	}
    	/*先把数组调整成大顶堆*/
    	heap_ajust(arr,arr_len);
    	
    	/*把第一个和最后一个数字调换*/
    	swap(&arr[0],&arr[arr_len-1]);
    	
    	/*对剩下的堆元素做同样的事情*/
    	heap_sort(arr,arr_len-1);
    	return 0;
    }
    
    
    int main(int argc, const char *argv[])
    {
        int arr[]={1,2,4,3,5};
    	int arr_len = sizeof(arr)/sizeof(arr[0]);
    	heap_sort(arr,arr_len);
    	print_arr(arr,arr_len);
        return 0;
    }
    
    

五、方法记忆

  1. 堆排序,是不断的调整出一个大顶堆,然后将最大的元素不断地放到最后面。

    在这里插入图片描述

六、参考

  1. https://www.cnblogs.com/chengxiao/p/6129630.html
  2. libevent源码
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值