赞
踩
个人专栏:数据结构
(注意:大堆和小堆的实现都是用数组来实现的,其物理结构是数组 但其逻辑结构是一颗完全二叉树)
用数组实现堆,假设其双亲节点的下标为i,则其左孩子节点的下标为2*i+1,右孩子节点的下标为2*i+2
若孩子节点的坐标为i(这里的孩子节点无论是左孩子还是右孩子),其双亲节点的坐标都为(i-1)/2;
如果想要一个数组变为堆,首先要把其看做完全二叉树的结构
如数组a[4]={2,1,3,4}
将其理解为树的结构就变为:(理解为树的结构后就可以对其进行操作,将其变为大堆或小堆)
堆分为大堆和小堆
- typedef int HPDataType;
-
- typedef struct Heap
- {
- HPDataType* a;
- int size;
- int capacity;
- }HP;
- void HPInit(HP* php)
- {
- php->a = NULL;
- php->capacity = php->size = 0;
- }
向上调整法要从第一个元素开始,向上调整法建小堆时,当孩子节点小于双亲节点时,孩子节点的值就和父亲节点的值交换,孩子节点坐标等于双亲节点的坐标,双亲节点的坐标变为(双亲节点的坐标-1)/ 2,当孩子节点==0时循环结束。
- void Swap(HPDataType* a, HPDataType* b)
- {
- HPDataType tmp = *a;
- *a = *b;
- *b = tmp;
- }
-
- void AdjustUp(HPDataType* a, int child)
- //不传结构体,为了后面排序做准备,原因后面会解释
- {
- assert(a);
- int parent = (child - 1) / 2;
- while (child > 0)
- //parent>=0可以作为结束条件,但是一个巧合,
- //因为到最后是依靠当child==0,parent=(child-1)/2=-1/2=0时
- //a[parent]==a[child]结束的
- {
- if (a[child] < a[parent])//建小堆,若a[child]>a[parent]就建大堆
- {
- Swap(&a[child], &a[parent]);
- child = parent;
- parent = (parent - 1) / 2;
- }
- else
- {
- break;
- }
- }
- }
- void Swap(HPDataType* a, HPDataType* b)
- {
- HPDataType tmp = *a;
- *a = *b;
- *b = tmp;
- }
-
- void AdjustUp(HPDataType* a, int child)
- //不传结构体,为了后面排序做准备,原因后面会解释
- {
- assert(a);
- int parent = (child - 1) / 2;
- while (child > 0)
- //parent>=0可以作为结束条件,但是一个巧合,
- //因为到最后是依靠当child==0,parent=(child-1)/2=-1/2=0时
- //a[parent]==a[child]结束的
- {
- if (a[child] < a[parent])//建小堆,若a[child]>a[parent]就建大堆
- {
- Swap(&a[child], &a[parent]);
- child = parent;
- parent = (parent - 1) / 2;
- }
- else
- {
- break;
- }
- }
- }
-
- void HPInitArray(HP* php, HPDataType* a, int n)
- {
- assert(php);
- php->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
- if (php->a == NULL)
- {
- perror("malloc fail!");
- return;
- }
- memcpy(php->a, a, n * sizeof(HPDataType));
- php->capacity = php->size = n;
- //向上调整
- for (int i = 1; i < n; i++)//这里遍历要从下标为1的元素开始,即从第一个孩子节点开始
- {
- AdjustUp(php->a, i);
- }
- }
-
- int main()
- {
- HPDataType a[] = { 50,100,480,76,31,31,226 };
- HPSort(a, sizeof(a) / sizeof(a[0]));//之前向上调整和向下调整就是为了这里
- //直接传a数组,而不是
- for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
- {
- printf("%d ", a[i]);//排升序
- }
- HP hp;
- HPInit(&hp);
- HPInitArray(&hp,a,sizeof(a)/sizeof(HPDataType));
-
- return 0;
- }
所以向上调整建堆的时间复杂度为O(N*logN),向上调整法的时间复杂度为O(logN)(即调整高度次)
转换之后就变为小堆
向下调整法时要从最后一个元素的双亲节点开始,在向下调整建小堆的过程中,用假设法选出左右孩子中最小的一个,当孩子节点大于双亲节点时,孩子节点就和双亲节点交换,双亲节点等于孩子节点,孩子节点等于双亲节点*2+1(不可以为双亲节点*2+2,必须为有孩子·,以便后面比较左右孩子的大小),当孩子节点的坐标大于等于总数组的大小时,就跳出循环
- void Swap(HPDataType* a, HPDataType* b)
- {
- HPDataType tmp = *a;
- *a = *b;
- *b = tmp;
- }
-
- void AdjustDown(HPDataType* a, int n, int parent)
- //不传结构体,为了后面排序做准备,原因后面会解释
- {
- assert(a);
- int child = parent * 2 + 1;
- while (child < n)
- {
- //假设法
- if (child + 1 < n && a[child] < a[child + 1])
- {
- child++;
- }
- if (a[parent] < a[child])
- {
- Swap(&a[parent], &a[child]);
- parent = child;
- child = parent * 2 + 1;//右孩子,不可以为左孩子,因为上面要比较
- }
- else
- {
- break;
- }
- }
- }
- void Swap(HPDataType* a, HPDataType* b)
- {
- HPDataType tmp = *a;
- *a = *b;
- *b = tmp;
- }
-
- void AdjustDown(HPDataType* a, int n, int parent)
- //不传结构体,为了后面排序做准备,原因后面会解释
- {
- assert(a);
- int child = parent * 2 + 1;
- while (child < n)
- {
- //假设法
- if (child + 1 < n && a[child] < a[child + 1])
- {
- child++;
- }
- if (a[parent] < a[child])//建大堆
- {
- Swap(&a[parent], &a[child]);
- parent = child;
- child = parent * 2 + 1;//右孩子,不可以为左孩子,因为上面要比较
- }
- else
- {
- break;
- }
- }
- }
-
- void HPInitArray(HP* php, HPDataType* a, int n)
- {
- assert(php);
- php->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
- if (php->a == NULL)
- {
- perror("malloc fail!");
- return;
- }
- memcpy(php->a, a, n * sizeof(HPDataType));
- php->capacity = php->size = n;
- //向下调整
- for (int i = (php->size - 1 - 1) / 2; i >= 0; i--)
- //数组的大小为size,对应下标为size-1,其数组最后一个元素
- //的双亲为(size-1-1)/2;
- {
- AdjustDown(php->a, n, i);
- }
- }
-
- int main()
- {
- HPDataType a[] = { 50,100,480,76,31,31,226 };
- HPSort(a, sizeof(a) / sizeof(a[0]));//之前向上调整和向下调整就是为了这里
- //直接传a数组,而不是
- for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
- {
- printf("%d ", a[i]);//排升序
- }
- HP hp;
- HPInit(&hp);
- HPInitArray(&hp,a,sizeof(a)/sizeof(HPDataType));
-
- return 0;
- }
所以向下调整法建堆的时间复杂度为O(N),向下调整法的时间复杂度为logN
- void HPPush(HP* php, HPDataType x)
- {
- assert(php);
- if (php->size == php->capacity)
- {
- int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
- HPDataType* tmp = (HPDataType*)realloc(php->a,sizeof(HPDataType) * newcapacity);
- if (tmp == NULL)
- {
- perror("realloc fail!");
- return;
- }
- php->a = tmp;
- php->capacity = newcapacity;
- }
- php->a[php->size] = x;
- php->size++;
- AdjustUp(php->a, php->size - 1);
- }
运用了一次向上调整法时间复杂度为O(logN)
堆的删除要交换堆顶元素和最后一个元素,把size--,在用一次向下调整法就可以了,不可以直接删除堆顶元素再建堆,一来这样时间复杂度增大了,二是删除堆顶元素,元素之间关系错乱了,父子变叔侄,兄弟变父子
- void HPPop(HP* php)
- {
- assert(php);
- assert(php->size > 0);
-
- Swap(&php->a[0], &php->a[php->size - 1]);
- php->size--;
-
- AdjustDown(php->a, php->size, 0);
- }
运用了一次向下调整法,时间复杂度为O(logN)
- bool HPEmpty(HP* php)
- {
- assert(php);
-
- return php->size == 0;
- }
时间复杂度为O(1)
- HPDataType HPTop(HP* php)
- {
- assert(php);
- return php->a[0];
- }
时间复杂度为O(1)
- void HPDestroy(HP* php)
- {
- assert(php);
- free(php->a);
- php->a = NULL;
- php->size = php->capacity = 0;
- }
- void HPSort(HPDataType* a, int n)
- {
- //这里不用建大堆选出最大数,建堆的时间复杂度为O(N)
- // 取出最大数后再N-1个数进行建堆,执行次数为N-1
- //n个数,执行次数为N+N-1+N-2.....+1;时间复杂度为O(N^2)
- //,和冒泡排序时间复杂度为一样,没有时间上的优势
- for(int i=(n-1-1)/2;i>=0;i--)
- {
- AdjustDown(a, n, i);
- }
- //对数组进行升序排列
- //需要建大堆
- //收尾交换
- //把最后一个值不放在堆中,建大堆选出最大数,将其放在最后
- int end = n-1;
- while (end > 0)
- {
- Swap(&a[end], &a[0]);
- AdjustDown(a, end, 0);
- end--;
- }
- }
原因:由于向上调整法和向下调整法传结构体每次都需要建一个堆,太耗费时间,但如果传数组,在用调整法时就不需要写一个堆的结构体,比如堆的排序
向下调整法建堆的时间复杂度为O(N),排序的时间复杂度为和向上调整时间一样,计算方式一样,为O(NlogN),总的时间复杂度为O(NlogN)。
- #define _CRT_SECURE_NO_WARNINGS 1
- #include<stdio.h>
- #include<assert.h>
- #include<time.h>
- #include<stdlib.h>
-
- void Swap(int *a,int *b)
- {
- int tmp = *a;
- *a = *b;
- *b = tmp;
- }
-
- void createNData()
- {
- srand((unsigned int)time(NULL));
- const char* file = "data.txt";//要打开的文件
- FILE* fin = fopen(file, "w");
- if (fin == NULL)
- {
- perror("fopen fail");
- return;
- }
- for (int i = 1; i <= 20; i++)
- {
- int x = rand() % 10000 + 1;
- fprintf(fin, "%d\n", x);
- }
- fclose(fin);
- }
-
- void AdjustDown(int* a, int n, int parent)
- {
- int child = parent * 2 + 1;
- while (child < n)
- {
- if (child + 1 < n && a[child] > a[child + 1])
- {
- child++;
- }
- if (a[parent] > a[child])
- {
- Swap(&a[parent], &a[child]);
- parent = child;
- child = parent * 2 + 1;
- }
- else
- {
- break;
- }
- }
- }
-
- void topk()
- {
- int k;
- int x;
- const char* file = "data.txt";//要打开的文件
- printf("要找出的最大数字的个数:>");
- scanf("%d", &k);
-
- FILE* fout = fopen("data.txt", "r");
- if (fout == NULL)
- {
- perror("fopen fail");
- return;
- }
- int* minheap = (int*)malloc(sizeof(int) * k);
- if (minheap == NULL)
- {
- perror("malloc fail");
- return;
- }
- for (int i = (k - 1 - 1)/2 ; i >= 0; i--)
- {
- AdjustDown(minheap, k, i);
- }
- while (fscanf(fout, "%d", &x) != EOF)
- {
- if (x > minheap[0])
- {
- minheap[0] = x;
- AdjustDown(minheap, k, 0);
- }
- }
- for (int i = 0; i < k; i++)
- {
- printf("%d\n", minheap[i]);
- }
- fclose(fout);
- }
- int main()
- {
- createNData();
- topk();
- return 0;
- }
- #define _CRT_SECURE_NO_WARNINGS 1
-
- #include"Heap.h"
-
- int main()
- {
- HPDataType a[] = { 50,100,480,76,31,31,226 };
- HPSort(a, sizeof(a) / sizeof(a[0]));//之前向上调整和向下调整就是为了这里
- //直接传a数组,而不是
- for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
- {
- printf("%d ", a[i]);//排升序
- }
- HP hp;
- //HPInitArray(&hp,a,sizeof(a)/sizeof(HPDataType));
- //HPInit(&hp);
- //HPInitArray(&hp, a, sizeof(a) / sizeof(HPDataType));
-
- //for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
- //{
- // HPPush(&hp, a[i]);
- //}
- //printf("%d", HPTop(&hp));
- //HPPop(&hp);
- //for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
- //{
- // printf("%d ", (&hp)->a[i]);
- //}
- return 0;
- }
-
-
- #define _CRT_SECURE_NO_WARNINGS 1
-
- #include"Heap.h"
-
- void HPInit(HP* php)
- {
- php->a = NULL;
- php->capacity = php->size = 0;
- }
-
-
- void Swap(HPDataType* a, HPDataType* b)
- {
- HPDataType tmp = *a;
- *a = *b;
- *b = tmp;
- }
-
- void AdjustUp(HPDataType* a, int child)
- //不传结构体,为了后面排序做准备,原因后面会解释
- {
- assert(a);
- int parent = (child - 1) / 2;
- while (child>0)
- //parent>=0可以作为结束条件,但是一个巧合,
- //因为到最后是依靠当child==0,parent=(child-1)/2=-1/2=0时
- //a[parent]==a[child]结束的
- {
- if (a[child] < a[parent])
- {
- Swap(&a[child], &a[parent]);
- child = parent;
- parent = (parent - 1) / 2;
- }
- else
- {
- break;
- }
- }
- }
-
- void HPPush(HP* php, HPDataType x)
- {
- assert(php);
- if (php->size == php->capacity)
- {
- int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
- HPDataType* tmp = (HPDataType*)realloc(php->a,sizeof(HPDataType) * newcapacity);
- if (tmp == NULL)
- {
- perror("realloc fail!");
- return;
- }
- php->a = tmp;
- php->capacity = newcapacity;
- }
- php->a[php->size] = x;
- php->size++;
- AdjustUp(php->a, php->size - 1);
- }
-
- HPDataType HPTop(HP* php)
- {
- assert(php);
- return php->a[0];
- }
-
- void AdjustDown(HPDataType* a, int n, int parent)
- //不传结构体,为了后面排序做准备,原因后面会解释
- {
- assert(a);
- int child = parent * 2 + 1;
- while (child < n)
- {
- //假设法
- if (child + 1 < n && a[child] < a[child + 1])
- {
- child++;
- }
- if (a[parent] < a[child])
- {
- Swap(&a[parent], &a[child]);
- parent = child;
- child = parent * 2 + 1;//右孩子,不可以为左孩子,因为上面要比较
- }
- else
- {
- break;
- }
- }
- }
-
- void HPInitArray(HP* php, HPDataType* a, int n)
- {
- assert(php);
- php->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
- if (php->a == NULL)
- {
- perror("malloc fail!");
- return;
- }
- memcpy(php->a, a, n * sizeof(HPDataType));
- php->capacity = php->size = n;
- //向上调整
- //for (int i = 1; i < n; i++)
- //{
- // AdjustUp(php->a, i);
- // HPPush(php, php->size)//不可以,因为调用函数
- // 时php->size在函数内还会增加
- //}
- //向下调整
- for (int i = (php->size - 1 - 1) / 2; i >= 0; i--)
- //数组的大小为size,对应下标为size-1,其数组最后一个元素
- //的双亲为(size-1-1)/2;
- {
- AdjustDown(php->a, n, i);
- }
- }
-
- //时间复杂度为logN
- void HPPop(HP* php)
- {
- assert(php);
- assert(php->size > 0);
-
- Swap(&php->a[0], &php->a[php->size - 1]);
- php->size--;
-
- AdjustDown(php->a, php->size, 0);
- }
-
- bool HPEmpty(HP* php)
- {
- assert(php);
-
- return php->size == 0;
- }
-
- void HPDestroy(HP* php)
- {
- assert(php);
- free(php->a);
- php->a = NULL;
- php->size = php->capacity = 0;
- }
-
- void HPSort(HPDataType* a, int n)
- {
- //这里不用建大堆选出最大数,建堆的时间复杂度为O(N)
- // 取出最大数后再N-1个数进行建堆,执行次数为N-1
- //n个数,执行次数为N+N-1+N-2.....+1;时间复杂度为O(N^2)
- //,和冒泡排序时间复杂度为一样,没有时间上的优势
- for(int i=(n-1-1)/2;i>=0;i--)
- {
- AdjustDown(a, n, i);
- }
- //对数组进行升序排列
- //需要建大堆
- //收尾交换
- //把最后一个值不放在堆中,建大堆选出最大数,将其放在最后
- int end = n-1;
- while (end > 0)
- {
- Swap(&a[end], &a[0]);
- AdjustDown(a, end, 0);
- end--;
- }
- }
- #define _CRT_SECURE_NO_WARNINGS 1
-
- #include<stdio.h>
- #include<stdlib.h>
- #include<assert.h>
- #include<stdbool.h>
- #include<string.h>
- #include<time.h>
-
- typedef int HPDataType;
-
- typedef struct Heap
- {
- HPDataType* a;
- int size;
- int capacity;
- }HP;
-
- void HPInit(HP* php);
- void HPInitArray(HP* php, HPDataType* a, int n);
-
- void HPDestroy(HP* php);
- // 插入后保持数据是堆
- void HPPush(HP* php, HPDataType x);
- HPDataType HPTop(HP* php);
-
- // 删除堆顶的数据
- void HPPop(HP* php);
-
- void HPSort(HPDataType* a, int n);
-
- bool HPEmpty(HP* php);
-
- void AdjustUp(HPDataType* a, int child);
- void AdjustDown(HPDataType* a, int n, int parent);
- void Swap(HPDataType* px, HPDataType* py);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。