:电子科技大学 格拉斯哥学院 2017级 徐方则
虽然这是老师强制要求的作业,要写与新生研讨课(雷达通信,图像识别。。。)有关的科普调研博客。但对比大多数科普论文(参见其它抬头带相同格式的博客),它们其实讲得已经很清楚了。而且它们也含有相当大的缺点:其实很多是有原版的,即不是原创。
在如今这个python满天飞的信息时代里,许多人可能会问自己:学C语言有用吗?本次报告便想从底层一些很简单的例子来说明C语言在这些领域的应用。说到C,就一定离不开数据结构。在大量数据需处理时,不同算法与相应的数据结构的选择所花的时间与空间可能是天差地别。这里我想讲一讲堆排序和优先队列。
下面从百度百科里找了张堆的图片,这是一个建最小堆(e)的过程。我们可以发现最小堆一个很显而易见的特性,子节点的值永远比父节点大。而最大堆与其相反,子节点的值永远比父节点小。
结合树的性质,堆其实是一个完全二叉树。根据这一点,若我们把堆存储在数组里,把第一个的下标当成1,我们不难发现,左儿子对应的下标是父节点下表的两倍,右儿子是父节点的两倍加一,以此,我们便可以编码建堆了。其算法复杂度较为紧确的界为O(n)。
第一张图其实已经讲了如何建最小堆,从根结点开始,若其比子节点大,选取子节点中最小的与其互换,一直执行到叶节点。
而堆排序更为简单,只要将堆顶的值取出,并将剩余的数组重新建堆即可。
以下是c语言代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef struct node MaxHeap;
struct node{
int *a;
int size;
};
void ArrayToMaxHeap(MaxHeap *heap,int *n);
void MaxHeapify(MaxHeap *heap,int i);
void BuildMaxHeap(MaxHeap *heap);
void MaxHeapSort(MaxHeap *heap);
int main(int argc, const char * argv[]) {
time_t start,finish;
float time=0;
int i,n;
MaxHeap heap;
ArrayToMaxHeap(&heap,&n);
BuildMaxHeap(&heap);
start=clock();
MaxHeapSort(&heap);
finish=clock();
for(i=1;i<n;i++)printf("%6d",heap.a[i]);
time=(float)(finish-start)/CLOCKS_PER_SEC;
printf("\n%5f\nHello, World!\n",time);
return 0;
}
void ArrayToMaxHeap(MaxHeap *heap,int *n){
int num,i;
scanf("%d",&num);
num+=1;//set heap[0] to be the biggest one, which is not included in the following sorting
heap->a=malloc(num*sizeof(int));
for(i=0;i<num;i++)scanf("%d",&heap->a[i]);
heap->size=num-1;
*n=num;
}
void MaxHeapify(MaxHeap *heap,int i){
int l,r,largest,tmp;
tmp=0;
largest=0;
l=i*2;
r=i*2+1;
if(l<=heap->size&&heap->a[l]>heap->a[i])
largest=l;
else largest=i;
if(r<=heap->size&&heap->a[r]>heap->a[largest])
largest=r;
if(largest!=i){
tmp=heap->a[i];
heap->a[i]=heap->a[largest];
heap->a[largest]=tmp;
MaxHeapify(heap,largest);
}
}
void BuildMaxHeap(MaxHeap *heap){
int i;
i=heap->size/2;
for(;i>=1;i--)
MaxHeapify(heap,i);
}
void MaxHeapSort(MaxHeap *heap){
int i,tmp;
for(i=heap->size;i>1;i--){
tmp=heap->a[i];
heap->a[i]=heap->a[1];
heap->a[1]=tmp;
heap->size--;
MaxHeapify(heap,1);
}
}
而优先队列与堆排序很像,相当于将最大的数逐一从队列中取出,但其本身复杂度(优先队列)要比排序低不少,当然更比直接暴力求最大快多了。
以下是c优先队列代码,基本沿用了上面的代码
#include <stdio.h>
#include <stdlib.h>
//***************************HEAP PART************************************
typedef struct node MaxHeap;
struct node{
int *a;
int size;
};
void ArrayToMaxHeap(MaxHeap *heap,int *n);
void MaxHeapify(MaxHeap *heap,int i);
void BuildMaxHeap(MaxHeap *heap);
void MaxHeapSort(MaxHeap *heap);
//************************************PRIORITY QUEUE PART*************************
int HeapExtractMax(MaxHeap *heap);
int Maximum(MaxHeap *heap);
void HeapIncreaseKey(MaxHeap *heap,int i,int k);
void MaxHeapInsert(MaxHeap *heap,int key);
//********************************MAIN FUNCTION***************************
int main(int argc, const char * argv[]) {
int n;
int key;
MaxHeap heap;
printf("Array numbers:\n");
ArrayToMaxHeap(&heap,&n);
BuildMaxHeap(&heap);
printf("Insert number:\n");
scanf("%d",&key);
MaxHeapInsert(&heap,key);
printf("The maximum number is:%d\n",Maximum(&heap));
return 0;
}
//********************************HEAP PART***************************
void ArrayToMaxHeap(MaxHeap *heap,int *n){
int num,i;
scanf("%d",&num);
num++;//set heap[0] to be the biggest one, which is not included in the following sorting
heap->a=(int *)malloc(num*sizeof(int));
for(i=0;i<num;i++)scanf("%d",&heap->a[i]);
heap->size=num-1;
*n=num;
}
void MaxHeapify(MaxHeap *heap,int i){
int l,r,largest,tmp;
tmp=0;
largest=0;
l=i*2;
r=i*2+1;
if(l<=heap->size&&heap->a[l]>heap->a[i])
largest=l;
else largest=i;
if(r<=heap->size&&heap->a[r]>heap->a[largest])
largest=r;
if(largest!=i){
tmp=heap->a[i];
heap->a[i]=heap->a[largest];
heap->a[largest]=tmp;
MaxHeapify(heap,largest);
}
}
void BuildMaxHeap(MaxHeap *heap){
int i;
i=heap->size/2;
for(;i>=1;i--)
MaxHeapify(heap,i);
}
void MaxHeapSort(MaxHeap *heap){
int i,tmp;
for(i=heap->size;i>1;i--){
tmp=heap->a[i];
heap->a[i]=heap->a[1];
heap->a[1]=tmp;
heap->size--;
MaxHeapify(heap,1);
}
}
//******************************PRIORITY PART********************
int HeapExtractMax(MaxHeap *heap){
int max=0;
if(heap->size<1)
{
printf("Heap underflow!");
return 0;
}
max=heap->a[1];
heap->a[1]=heap->a[heap->size];
MaxHeapify(heap,1);
heap->size--;
return max;
}
int Maximum(MaxHeap *heap){
return heap->a[1];
}
void HeapIncreaseKey(MaxHeap *heap,int i,int k){
if(k<heap->a[i]){
printf("Key is smaller than the original value");
return ;
}
heap->a[i]=k;
while(i>1&&heap->a[i/2]<heap->a[i]){
heap->a[i]=heap->a[i/2];
i/=2;
heap->a[i]=k;
}
}
void MaxHeapInsert(MaxHeap *heap,int key){
heap->size++;
heap->a=realloc(heap->a,(heap->size+1)*sizeof(int));
heap->a[heap->size]=key;
HeapIncreaseKey(heap,heap->size,heap->a[heap->size]);
}
最大堆最小堆本身应用相当的多,比如优化问题。可以通过一定模型将一个事件的优先度转化为数字,再从中提取优先度最高的,比如最大似然估计里,对不同的点估计有不同的值,将每个点的数值做成优先队列。那么maimum likelihood estimator(其实就是最大似然估计的值) 就可以看成是一个优先队列里最大的数,而采集的样本越多,值越准确,所以其需要大量的数据。在大量数据下,堆就不失为一个很好的数据结构,应为其本身的复杂度为O(n),比O(nlogn)快。