目录
一、排序
1、最简单的排序——桶排序
#include <stdio.h>
#include <string.h>
#define LEN(arr) (sizeof(arr)/sizeof(typeof(arr[0])))
/* 计算数组中分数出现的次数,分数在0-20之间
*/
int main()
{
int a[10] = {2,0,19,4,8,2,16,1,8,7};
关键是b的大小,要能包含分数区间。把分数当b的下标
int b[21] = {};
memset(b, 0, 21);
for(int i = 0; i < 10; i++)
{
b[a[i]]++;
}
for(int j = 0; j < 21; j++)
printf("%d %d times\n", j, b[j]);
printf("\n");
return 0;
}
2、冒泡排序
#include <stdio.h>
该宏定义可以自动计算数组长度
#define LEN(arr) (sizeof(arr)/sizeof(typeof(arr[0])))
从小到大排序
int main()
{
int arr[10] = {2,3,5,4,8,2,9,1,11,7};
int tmp;
需要肌肉记忆,len-1
for(int i = 0; i < LEN(arr) - 1; i++)
{
需要肌肉记忆len-1-i
for(int j = 0; j < LEN(arr) - 1 - i; j++)
{
if(arr[j] >= arr[j+1])
{
tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
for(int i = 0; i < LEN(arr); i++)
printf("%d ", arr[i]);
printf("\n");
return 0;
}
3、最常用的排序——快速排序
#include <stdio.h>
#include <string.h>
#define LEN(arr) (sizeof(arr)/sizeof(typeof(arr[0])))
void quick_sort(int *arr, int left, int right)
{
int i, j, tmp, t;
if(left > right)
return;
保存基准值,放在后面做交换
tmp = arr[left];
哨兵位置
i = left;
j = right;
while(i != j)
{
一定是右哨兵先循环
while(arr[j] >= tmp && i < j)
j--;
while(arr[i] <= tmp && i < j)
i++;
if(i < j)
{
两个哨兵停止后交换值
t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
基准值放到哨兵“相遇”处
arr[left] = arr[i];
arr[i] = tmp;
哨兵左右两边重复上述操作
quick_sort(arr ,left, i-1);
quick_sort(arr, i+1, right);
}
/* 计算数组中分数出现的次数,分数在0-20之间
*/
int main()
{
int a[10] = {1,2,3,6,5,4,7,9,8,10};
quick_sort(a, 0, LEN(a) - 1);
for(int j = 0; j < 10; j++)
printf("%d \n", a[j]);
printf("\n");
return 0;
}
以a[0],a[9]为left,right哨兵。a[0]是基准
如果右哨兵大于基准,则right--;当右哨兵小于基准时,停止循环
如果左哨兵小于基准,则left++;当左哨兵大于基准时,停止循环
把left和right位置的值交换,下图红色交换,绿色交换。
重复上面,直到左右哨兵相遇,把基准换到i++的位置,如下图的第二行,就是3和6交换后
再把左右两边重复以上整个操作
4、买书
对数组,去重并打印出来
快速排序在最耗时情况下时间复杂度才跟冒泡算法一样,所以优先使用快排,不要再冒泡了
桶排序其实比快速排序还快,但是局限多,如负数不能排,会浪费空间
int main()
{
int a[10] = {20,40,31,67,40,20,89,300,400,15};
quick_sort(a, 0, LEN(a) - 1);
去重打印,j要小于9而不是10
for(int j = 0; j < 9; j++)
{
if(a[j] != a[j+1])
printf("%d ", a[j]);
}
打印最后一个数
printf("%d ", a[9]);
return 0;
}
二、栈,队列,链表
1、解密QQ号——队列
主要学习队列这种,用首尾指针的思想
要求:把一串数,第一个删除,第二个加到数组尾部,第三个删除,第四个加到数组尾部,循环至只剩两个数,都删除。打印删除的数
QQ加密后转换出来6 1 5 9 4 7 2 8 3
这种用首尾指针操作好的多,普通做法可能就删,插数组,开销很大的。用指针虽然浪费几个空间,但很省时
#include <stdio.h>
int main()
{
int arr[101] = {6,3,1,7,5,8,9,2,4};
int head, tail, i;
head = 0;
tail = 9;
tail是在最后一个数组元素之后,并不是4
6,3,1,7,5,8,9,2,4
^ ^
| |
head tail
while (head < tail)
{
printf("%d ", arr[head]);
head++;
arr[tail] = arr[head];
tail++;
head++;
}
return 0;
输出:6 1 5 9 4 7 2 8 3
}
在VS中创建的,可以输入10个数进行“解密”
#include <stdio.h>
typedef struct queue
{
int data[100];
int head;
int tail;
}QUEUE, *pQUEUE;
int main()
{
QUEUE q;
int i;
q.head = 0;
q.tail = 0;
for (i = 0; i <= 9; i++)
{
scanf("%d", &q.data[q.tail]);
q.tail++;
}
while (q.head < q.tail)
{
printf("%d ", q.data[q.head]);
q.head++;
q.data[q.tail] = q.data[q.head];
q.tail++;
q.head++;
}
return 0;
}
2、解密回文——栈
本题是确定一个字符串是否是回文数,这里用“栈”的思想,先进后出,先把前一半字符串进
“栈”,再拿后一半字符串跟栈里的比较,栈里的部分是“弹”,出来的
#include <stdio.h>
#include <string.h>
int main()
{
char s[101];
char arr[10] = "abcdcba";
int i, len, mid, next, top;
len = strlen(arr);
mid = len/2 - 1;
top = 0;
printf("len = %d\n", len);
printf("mid = %d\n", mid);
前一半字符串进栈
for(i = 0; i <= mid; i++)
{
先++,s[0]没存数据,top==0用来判断是否弹出全部字符串的
s[++top] = arr[i];
}
if(len % 2 == 0)
next = mid + 1;
else
next = mid + 2;
printf("next = %d\n", next);
for(i = next; i <= len - 1; i++)
{
printf("top = %d\n", top);
printf("%c == %c ?\n", s[top], arr[i]);
if(s[top] != arr[i])
break;
top--;
}
if(top == 0)
printf("yes\n");
else
printf("no\n");
return 0;
}
3、小猫钓鱼游戏
两人一次出牌,桌面有相同牌的时候则收回这部分。
出牌就是出队,赢牌则把相同部分放到队尾,没赢则入栈
队列和栈的结合
4、链表
第一次循环:head,p,q都指向首节点
第二次循环:p,q后移,head不变
第二次循环:p,q后移,head不变
注:下面的释放不一定对
必须有p和q两个,p用来指向新分配,此时q留下来指着链表尾,p分配好后,q再指向p,然后q=p。所以qp总是在链表尾。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define LEN(arr) (sizeof(arr)/sizeof(typeof(arr[0])))
typedef struct node
{
int data;
struct node *next;
}NODE, *pNODE;
int main()
{
pNODE head, p, q, t;
int i, n, a;
head = NULL;
printf("input 5 numbers\n");
for(i = 1; i <= 5; i++)
{
scanf("%d", &a);
p = (pNODE)malloc(sizeof(NODE));
p->data = a;
p->next = NULL;
if(head == NULL)
head = p;
else
q->next = p;
q = p;
printf("q->data = %d,p->data = %d\n", q->data, p->data);
}
t = head;
while(t != NULL)
{
printf("%d \n",t->data);
t = t->next;
}
free(head);
return 0;
}
三、暴力枚举
1、奥数
在方框中填入相同的数使等式成立:口3*6528=3口*8256
挨个试,这是最简单最暴力的
int main()
{
for(int i = 0; i <= 9; i++)
if( (i * 10 + 3) * 6528 == (30 + i) * 8256 )
printf(
"%d\n", i);
return 0;
}
2、炸弹人
炸弹人放哪才能一次炸死最多敌人?(炸弹是个十字,可炸砖墙,但不能穿透)
用#表示墙,用G表示敌人,用.表示空地
,用二维数组存储,判断即可。
实际问题转模型
四、万能的搜索
1、不撞南墙不回头——深度优先搜索
输入一个数,如3,输出123所有的排序
123
132
213
231
312
321
输入4,输出1234的所有排序
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int a[10], book[10], n;
void dfs(int step)
{
int i;
//step = m + 1 时表示前面n个盒子都放好了
//n个数字,公有n + 1个盒子
if(step == n + 1)
{
for(i = 1; i <= n; i++)
{
printf("%d", a[i]);
}
printf("\n");
return;
}
for(i = 1; i <= n; i++)
{
if(book[i] == 0)//等于0表明数字还在手上
{
a[step] = i;//将i号数字放到第step个盒子里
book[i] = 1;//等于1表明数字i放到盒子里了
dfs(step + 1);//放好第step个盒子后放下一个,调用自己
book[i] = 0;//回收刚才尝试的的数字
}
}
return;
}
int main()
{
printf(
"input a number in 1-9\n");
scanf("%d", &n);
dfs(1);
return 0;
}
原理很简单,以3个数为例
1 2 3放到编号为1234的四个桶,第四个其实没用到,用作判断
先放第一个桶,1号放到1号桶
2号放到2号桶
3号放到3号桶
到第4个桶时,号没了表示放完了,打印
输出:123
回收3号和2号
3号放到2号桶
2号放到3号桶
到第4个桶时,号没了表示放完了,打印
输出:132
后面两个数排完了,该把1号桶里的换换了,换成2
有213
231
换成3有
312
321
下图递归调用自己就只是返回了123和132,return的地方,b2=0,b3=0,表明从桶里拿出来,交换后再排
这里的深度搜索是指,一条道走到底,如123,排好了再把最后两个能动的调换,变为132
1---->2-----3
|---->3---->2
3.1节的遗留问题
填入1-9的数字使口口口+口口口=口口口
,数字不能重复使用,就可以用深度搜索
2、层层递进——广度优先搜索
两种解法见4.2,4.3
节
下图,从(1,1)点走到终点最短路径,一开始就有两种走法,记录走过的和不能走的,然后这两步再继续走其他的,这叫广度优先
,地毯式的
下面这个是一条路走到底,直到终点。到达终点后再从(1,1)到(1,2)这条路再试,这叫深度优先
五、图的遍历
--------------------------深度优先-----------一条路走到黑
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int e[101][101], book[101], sum, n;
void dfs(int cur)//当前所在顶点编号,一般是从最高点,即1出发
{
int i;
printf("cur = %d\n", cur);
sum++;//访问一个点就加1
if(sum == n)//所有的点都访问完了,退出
return;
for(i = 1; i <= n; i++)//从1号点往其他地方去
{
if(e[cur][i] == 1 && book[i] == 0)//如果可以到达,并且改点未被访问过就去到这个点
{
book[i] = 1;//标记已访问
dfs(i);//从i点开始往其他点去
}
}
return;
}
int main()
{
int i, j, m, a, b;
scanf("%d %d",&n, &m);
//初始化二维矩阵,就是初始化e
for(i = 1; i <= n; i++)
{
for(j = 1; j <= n; j++)
if(i == j)
e[i][j] = 0;
else
e[i][j] = 99999999;//用这个表示正无穷
}
//读取顶点之间的边,可到达的边
for(i = 1; i <= m; i++)
{
scanf("%d %d",&a, &b);
e[a][b] = 1;
e[b][a] = 1;
}
book[1] = 1;
dfs(1);
return 0;
}
输入:
5 5(表明生成一个5*5的二维数组,见上图)
1 2(表明1到2是通的,标记为1,同时2到1也会被标记为1,这是无方向的)
1 3
1 5
2 4
3 5
输出:
cur = 1(当前在1点)
cur = 2
cur = 4
cur = 3
cur = 5
先从1到2,2只能到4。2这条线遍历完了
再到3,3也只能到5。3这条线遍历完了
--------------------------广度优先
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int i, j, m, n, a, b, cur, e[101][101], book[101] = {0};
int que[10001], head, tail;
scanf("%d %d",&n, &m);
//初始化二维矩阵,就是初始化e
for(i = 1; i <= n; i++)
{
for(j = 1; j <= n; j++)
if(i == j)
e[i][j] = 0;
else
e[i][j] = 99999999;//用这个表示正无穷
}
//读取顶点之间的边,可到达的边
for(i = 1; i <= m; i++)
{
scanf("%d %d",&a, &b);
e[a][b] = 1;
e[b][a] = 1;
}
//队列初始化
head = 1;
tail = 1;
//从1号顶点出发,把1号顶点加入队列
que[tail] = 1;
tail++;
book[1] = 1;
//队列不空开始循环
while(head < tail)
{
cur = que[head];//刚开始que[head] = que[tail] = que[1] = 1
for(i = 1; i <= n; i++)//尝试1可访问的点
{
if(e[cur][i] == 1 && book[i] == 0)
{
que[tail] = i;//可访问且未被访问过,把点i入队,从1可到的依次为2,3,5
tail++;
book[i] = 1;
}
if(tail > n)//已经访问完了
{
break;
}
}
head++;
//上面从1点访问到2,3,5后,要分别从2,3,5下手了,所以head要++,跳到第二层的2,3,5开始
}
for(i = 1; i < tail; i++)
printf("%d ", que[i]);
return 0;
}
输入:
5 5
1 2
1 3
1 5
2 4
3 5
输出:
1 2 3 5 4
广度访问的顺序,如下
如下图,从1点开始访问,先加入队列,1能访问2,3,5。分别把235加入队列,看235分别能访
问哪些点,只有2能访问4,队列为尾部加入4
2、城市地图——图的深度优先遍历
像这种从1号到5号城市,求最短路程的。要注意这是有方向的
,只能1到2,不能2到1
最短路程,需要把每条路都尝试,比较最短,所以用深度优先
3、最少转机——图的广度优先
同样从1号到5号城市,求转机次数最少(假设每段路程都一样),注意这是无向的
,即1可到3,3可到1
对于次数问题,当1可到2,3时,从2,3开始寻找路线,当从3找到5时,就可以结束了,因为这是
广度搜索,要求是次数最少,从2找的路线已经转2次了。
如下,竖着看,从1-3-5就可以结束了,从4到5没必要,同样从1-2-3-5也没必要了
1---2---3
|---4
1---3---5
总结:广度适合求这种次数问题,就像二叉树中找某个值一样,不要一条路到底,找不到换条路,万一要找的是3,广度优先一下就找到了。深度优先适合路程问题,如下,要到5,深度在1-2-4,1-2-5就找到了,广度优先就慢。
1
2 3
4 5
六、最短路径
1、只有五行的算法
问题:求任意两城市之间的最短路径
由前面可以,可以求两城市之间的最短,现在是任意两个。这种问题被称为”多源最短路径“
同样转成4*4的二维数组
思路:任意两点之间有路径,有的无法到达。数组记录了能到达和无法到达的(无穷大表示无法到达)
当必须经过两个节点,如节点1和2,那1到3就不能直接到达,要用1–>2–>3,然后跟1–>3的路程比较,哪个小用哪个,替换二维数组中的e[1][3]的值。很明显1-->2-->3
路程是5小于7。其他同理
#include <stdio.h>
int main()
{
int e[10][10], k, i, j, n, m, t1, t2, t3;
int inf = 99999999;
//读取n和m
scanf("%d %d", &n, &m);
//初始化,先初始化为inf
for (i = 1; i <= n; i++)
{
for (j = 1; j <= n; j++)
{
if (i == j)
e[i][j] = 0;
else
e[i][j] = inf;
}
}
//读取边,改变上面的初始化inf
for (i = 1; i <= m; i++)
{
scanf("%d %d %d", &t1, &t2, &t3);
e[t1][t2] = t3;
}
//核心算法语句
for (k = 1; k <= n; k++)//只允许经过k个节点时,当k=2时
//必须经过2个节点时(即共3个节点):i=1,j=3表示从1到3,有123,143两条路,显然123更近,为5
//1直接到3为6,超过5,就更新一下e[1][3]=5。e[1][3]是默认两点之间的
for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
if (e[i][j] > e[i][k] + e[k][j])
e[i][j] = e[i][k] + e[k][j];
for (i = 1; i <= n; i++)
{
for (j = 1; j <= n; j++)
{
printf("%10d", e[i][j]);
}
printf("\n");
}
return 0;
}
输入和输出
4 8
1 2 2
1 3 6
1 4 4
2 3 3
3 1 7
3 4 1
4 1 5
4 3 12
0 2 5 4
9 0 3 4
6 8 0 1
5 7 10 0
2、Dijkstra算法——通过边实现松弛
指定一个点到其余各点的最短路径,也叫做“单源最短路径
”
思路:
a.1点可到2和3,目前1-2和1-3最短,记录一下1,12
1)用2拓展,可到4,3,目前1-2-4最短,记录4,。前面记录了1-3为12,现在中转1-2-3为10更短,记录一下
2)用3拓展到5,目前1-3-5最短为17
b.用4拓展,可到3,5,6。1-4最短已经记录了为4,那刚才从1-2-3是最短的,现在从1-2-4-3为8更短,再记录8
......
每次都记录最短的,发现新的路径就拿出来跟上次最短的比较
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int i, j, m, n, t1, t2, t3, u, v, min, e[10][10], book[10], dis[10];
int inf = 99999999;
scanf("%d %d",&n, &m);
//初始化二维矩阵,就是初始化e
for(i = 1; i <= n; i++)
{
for(j = 1; j <= n; j++)
if(i == j)
e[i][j] = 0;
else
e[i][j] = inf;//用这个表示正无穷
}
//读取顶点之间的边,可到达的边
for(i = 1; i <= m; i++)
{
scanf("%d %d %d",&t1, &t2, &t3);
e[t1][t2] = t3;
}
//初始化dis,这是1号顶点到其余各顶点的初始路程,用dis[i]表示
for(i = 1; i <= n; i++)
dis[i] = e[1][i];
//book数组初始化,book标记1点到其他点的最短距离有没有被确定,0没确定,1确定了
for(i = 1; i <= n; i++)
book[i] = 0;
book[1] = 1;
//核心算法
for(i = 1; i <= n -1; i++)
{
min = inf;
for(j = 1; j <= n; j++)
{
if(book[j] == 0 && dis[j] < min)
{
min = dis[j];
u = j;
}
}
//上边一个for循环下来,找到了1点到其相邻点的最近点,从图上看就是点2
//然后找点2相邻的最近点,第一遍循环u=2
book[u] = 1;
printf("point %d\n", u);
//找2到各个点的其他距离
for(v = 1; v <= n; v++)
{
//如果2到其他点的距离小于inf
if(e[u][v] < inf)
{
//如果从1到v点的值大于通过点u(2)中转,则保存较小值,
//这里1到3显然通过2中转最近,所以保存,同理1到4通过中转也最近,保存一下
//到u=4时,1到4最近为1-2-4,距离是4,4可到3,比较上次1-3最近的10,发现1+3+4<1+9,再替换
if(dis[v] > dis[u] + e[u][v])
{
dis[v] = dis[u] + e[u][v];
printf("%d = %d + %d\n", dis[v], dis[u], e[u][v]);
}
}
}
printf("\n\n");
}
for(i = 1; i <= n; i++)
printf("%d ", dis[i]);
return 0;
}
后边还有解决路径是“负值”的情况及其优化,几种算法的比较,就不再整了,用得到看pdf
七、神奇的树
1、树
1、二叉树
满二叉树:对于深度为h且有2^h-1个节点的二叉树
完全二叉树:高度为h,除h层外其他都达到最大节点个数
,第 h 层从右向左连续缺若干结点
,还TM有方向
3、堆——神奇的优先队列
所有父结点都比子结点要小
(注意:圆圈里面的数是值,圆圈上面的数是这个结点的编号,此规定仅适用于本节)。符合这样特点的完全二叉树
我们称为最小堆
。
反之,如果所有父结点都比子结点要大
,这样的完全二叉树
称为最大堆
完全二叉树特性:节点数为n的完全二叉树,最后一个非叶节点的序号是n/2,如上14/2=7
序号为k的父节点,其左子节点序号为2k,右子节点序号为2k+1(对本节标序号有效)
--------------------------最小堆排序
#include <stdio.h>
int h[101];//用来存放堆的数组
int n; //用来存储堆中元素的个数,也就是堆的大小,也是树的总结点数
//交换函数,用来交换堆中的两个元素的值 p
void swap(int x, int y)
{
int t;
t = h[x] ;
h[x] = h[y] ;
h[y] = t;
}
//向下调整函数
void siftdown(int i)//传入一个需要向下调整的结点编号i,这里传入1,即从堆的顶点开始向下调整
{
int t ,flag = 0;//flag用来标记是否需要继续向下调整
//当i结点有儿子(其实是至少有左儿子)并且有需要继续调整的时候循环就执行
while( i*2 <= n && flag == 0)
{
//首先判断它和左儿子的关系,并用t记录值较小的结点编号
if(h[i] > h[i*2])
t = i*2;
else
t = i;
//如果它有右儿子,再对右儿子进行讨论
if(i*2 + 1 <= n)
{
//如果右儿子的值更小,更新较小的结点编号
if(h[t] > h[i*2 + 1])
t = i*2 + 1;
}
//如果发现最小的结点编号不是自己,说明子结点中有比父结点更小的,交换,更新
if(t != i)
{
swap(t, i);
i = t;
}
else
flag = 1;//否则说明当前的父结点已经比两个子给点都要小,不需要再进行调整了
}
}
//建立堆函数
void creat()
{
int i;
//从最后一个非叶结点到第1个结点依次进行向上调整
for(i = n/2; i >=1; i--)
siftdown(i);
}
//删除最大的元素
int deletemax()
{
int t;
t = h[1]; //用一个临时变量记录堆顶点的值
h[1] = h[n];//将堆的最后一个点赋值到堆顶
n--; //堆的元素减少 1
siftdown(1);//向下调整,每次都成了最小堆,然后返回堆顶的元素,即最小元素
return t; //返回之前记录的堆的顶点的最大值
}
int main()
{
int i, num;
//读入要排序的数字的个数
scanf("%d", &num);
for(i = 1; i <= num; i++)
scanf("%d", &h[i]);
n = num;
creat();
//删除顶部元素,连续删除n次,其实也就是从大到小把数输出来
for(i = 1; i <= num; i++)
printf("%d ", deletemax());
printf("\n ");
return 0;
}
输入:
14
99 5 36 7 22 17 46 12 2 19 25 28 1 92
输出:
1 2 5 7 12 17 19 22 25 28 36 46 92 99
----------------------------分割线---------------------
--------------------------最大堆排序-----
与最小堆排序区别是,deletemax被换成heapsort,打印换成h[i]
#include <stdio.h>
int h[101];//用来存放堆的数组
int n; //用来存储堆中元素的个数,也就是堆的大小,也是树的总结点数
//交换函数,用来交换堆中的两个元素的值 p
void swap(int x, int y)
{
int t;
t = h[x] ;
h[x] = h[y] ;
h[y] = t;
}
//向下调整函数
void siftdown(int i)//传入一个需要向下调整的结点编号i,这里传入1,即从堆的顶点开始向下调整
{
int t ,flag = 0;//flag用来标记是否需要继续向下调整
//当i结点有儿子(其实是至少有左儿子)并且有需要继续调整的时候循环就执行
while( i*2 <= n && flag == 0)
{
//首先判断它和左儿子的关系,并用t记录值较小的结点编号
if(h[i] > h[i*2])
t = i*2;
else
t = i;
//如果它有右儿子,再对右儿子进行讨论
if(i*2 + 1 <= n)
{
//如果右儿子的值更小,更新较小的结点编号
if(h[t] > h[i*2 + 1])
t = i*2 + 1;
}
//如果发现最小的结点编号不是自己,说明子结点中有比父结点更小的,交换,更新
if(t != i)
{
swap(t, i);
i = t;
}
else
flag = 1;//否则说明当前的父结点已经比两个子给点都要小,不需要再进行调整了
}
}
//建立堆函数
void creat()
{
int i;
//从最后一个非叶结点到第1个结点依次进行向上调整
for(i = n/2; i >=1; i--)
siftdown(i);
}
//堆排序
void heapsort()
{
while(n > 1)
{
swap(1, n);
n--;
siftdown(1);
}
}
//删除最大的元素
int deletemax()
{
int t;
t = h[1]; //用一个临时变量记录堆顶点的值
h[1] = h[n];//将堆的最后一个点赋值到堆顶
n--; //堆的元素减少 1
siftdown(1);//向下调整,每次都成了最小堆,然后返回堆顶的元素,即最小元素
return t; //返回之前记录的堆的顶点的最大值
}
int main()
{
int i, num;
//读入要排序的数字的个数
scanf("%d", &num);
for(i = 1; i <= num; i++)
scanf("%d", &h[i]);
n = num;
creat();
heapsort();
//删除顶部元素,连续删除n次,其实也就是从大到小把数输出来
for(i = 1; i <= num; i++)
printf("%d ", h[i]);
printf("\n ");
return 0;
}