堆排序

堆排序

   堆排序是利用这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。首先简单了解下堆结构。

 堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:

同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子

该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是:

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]  

小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]  

ok,了解了这些定义。接下来,我们来看看堆排序的基本思想及基本步骤:

堆排序基本思想及步骤

  堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了

步骤一 构造初始堆。将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。

  a.假设给定无序序列结构如下

2.此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整。

4.找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换。

这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。

此时,我们就将一个无需序列构造成了一个大顶堆。

步骤二 将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。

a.将堆顶元素9和末尾元素4进行交换

b.重新调整结构,使其继续满足堆定义

c.再将堆顶元素8与末尾元素5进行交换,得到第二大元素8.

后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序

再简单总结下堆排序的基本思路:

  a.将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;

  b.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;

  c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

数组实现堆排序

#include<stdio.h>
//交换两个数 
void swap(int arr[],int a,int b){
	int temp=arr[a];
    arr[a] = arr[b];
    arr[b] = temp;
}
//调整大顶堆
void adjustHeap(int arr[],int i,int n){
	int temp=arr[i];
	for(int k=i*2+1;k<n;k=k*2+1){
		if(k+1<n && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点
            k++;
        }
        if(arr[k] >temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
       	    arr[i] = arr[k];
        	i = k;
        }else{
            break;
        }
	}
	arr[i]=temp;
}

void heapSort(int arr[],int n){
	//1.构建大根堆
	for(int i=n/2-1;i>-1;i--){
		adjustHeap(arr,i,n);
	}
	//2.调整堆并且交换堆顶和末尾元素
	for(int j=n-1;j>0;j--){
		swap(arr,0,j);
		//调整大顶堆 
		adjustHeap(arr,0,j); 
	} 
}
 
int main(void){
	int a[8]={3,2,5,8,4,7,6,9};
	int i;
	heapSort(a,8);
	for(i=0;i<8;i++){
		printf("%3d",a[i]);
	}
} 


二叉树实现堆排序

#include<stdio.h>
#include<stdlib.h>
#define N 10
typedef struct node{
 	int data;
 	struct node*left;
	struct node*right;
}BTnode;
 
 
 
//完全二叉树的创建 
BTnode* Creatheap(int a[],int n){
 	BTnode **Q,*p,*pa,*root;
 	int front=0,rear=0;
 	Q=(BTnode**)malloc((n+2)*sizeof(BTnode*));   //创建一个队列 
 	root=pa=(BTnode*)malloc(sizeof(BTnode));  //创建根节点 
 	pa->data=a[0];
 	pa->left=pa->right=NULL;
 	for(int i=1;i<n;i++){
 		p=(BTnode*)malloc(sizeof(BTnode));
 		p->data=a[i];
 		p->left=p->right=NULL;
 		if(!pa->left){
 			pa->left=p;
		}else{
		 	pa->right=p;
		 	pa=Q[rear++];    //出队列 
		}
		Q[front++]=p;        //入队列 
	}
	return root;
}



void HeapSort(BTnode *root){
	BTnode **Q,*pa,*Pmin;
	int front=0,rear=0,end,tag,temp;
	Q=(BTnode**)malloc((N+2)*sizeof(BTnode*));
	Q[++rear]=root;    //根节点先入队列 
	while(1){
		pa=Q[++front];     // 父节点出队列 
		if(!pa->left&&!pa->right)      //当父节点无左右子女时,二叉树存入队列结束,跳出循环 
		break;
		if(pa->left){
			Q[++rear]=pa->left;       //当父节点有左孩子时,左孩子入队列
		}
		if(pa->right){
			Q[++rear]=pa->right;       //当父节点有右孩子时,右孩子入队列
		}
	}
	end=rear;   //岗哨 
	
	while(root->left){
		while(1){
			tag=1;       //设置交换标志位 
			for(int k=front-1;k>0;k--){
				pa=Q[k];
				Pmin=pa;
				if(pa->left->data<Pmin->data){
					Pmin=pa->left;
				}
				if(pa->right&&pa->right->data<Pmin->data){
					Pmin=pa->right;
				}
				if(Pmin-pa){
					temp=Pmin->data;
					Pmin->data=pa->data;
					pa->data=temp;
				}
			}
			if(tag)   //当标志位为1,说明无交换,跳出循环 
			break;	
		}
		temp=root->data;                   //交换根节点和尾节点的值 
		root->data=Q[rear]->data;
		Q[rear]->data=temp;
		if(Q[front-1]->right){
			Q[front-1]->right=NULL;
		}else{
			Q[front-1]->left=NULL;
			front--;
		}
		rear--;
	}
	for(;end>0;end--){
		printf("%5d",Q[end]->data);    //从尾向头打印队列 
	}
}
 
 


  int main(void)
 {
 	int a[N]={3,2,5,8,4,7,6,9,0,1};
 	BTnode*root;
 	root=Creatheap(a,N);//完全二叉树的创建 
 	HeapSort(root);
 }

 

运行结果:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值