package arithmetic;
import java.io.PrintStream;
import java.math.*;
import java.util.Arrays;
/*
* 小顶堆的自适应程序
* */
public class Heap {
/*
* data @ 待排序的数组
* s @ 除 data[s] 均满足堆的特性
* n 数组的长度
* */
public static void adjust(int data[],int s,int n)
{
int parent = s;//顶堆的结点
int j = 2*parent;//其子结点
while(j<n)
{
if(j+1<n&&data[j]>data[j+1])//取出两个子结点中的一个最小的
j++;
if(data[parent]>data[j])//把子结点中最小的那个与其父亲结点的值比较,如果小,与父亲结点交换
{
int temp = data[parent];
data[parent] = data[j];
data[j] = temp;
parent = j ;//父亲结点为当前的结点
j = 2*j;//当前结点下移
}else
{
break;
}
}
}
/*
* 这个利用完全二叉树的一个很重要的性质:最后一个非叶子结点为 第 n/2个
* 1.从最后一个非叶子结点开始,把其代表的堆都自适应成一个极小堆0
* 2.然后从整个堆的最后一个元素开始,与堆顶交换,然后再自适应成一个堆
* */
public static void sort(int data[],int n)
{
for(int i=n/2;i>0;i--)
{
adjust(data,i,n);
}
for(int i=n-1;i>0;i--)//从堆的最后一个元素开始
{
int temp = data[1];
data[1] = data[i];
data[i] = temp;//交换数据后自适应成堆
adjust(data,1,i);
}
}
public static void main(String args[]) throws Exception
{
int data[] = {0,35,18,20,65,40,46,58,25};// 0 为填充数 data[0] 无用,使节点下标从1开始
int test[] = new int[1000];
for(int i=999;i>0;i--)
{
test[i] = i;
}
System.setOut(new PrintStream("F:\\test.txt"));
sort(test,1000);
for(int i=1;i<1000;i++)
System.out.println(test[i]);
}
}
/*
*
* 堆排序
* 小顶堆:堆顶点的值比两个叶子节点的值都要小,把子节点还可以看作一个堆,也满足以上面条件
* 堆排序思路:
* 1.先将待排序的数据存入在数组中
* 2.将数据利用筛选法创建成一个小顶堆
* 3.取出当前小顶堆的堆头与叶子结点的最后一个交换,后又形成一个不符条件的堆
* 4.然后再利用筛选法将不符全条件的堆适应成一个符和条件的堆
*
*
* 筛选法:
* 从当前堆顶元素不满足堆的条件,其余都满足的堆顶点开始
* 拿其与其两个叶子结点中比较小的相比较,如果堆顶元素比它们的结果大,
* 那么就要拿堆顶元素与叶子结点中比较小的那个交换,从而使自己又达到一个堆
* 而对于其子叶结点也是使用相同的方法
* 有一个问题:为什么在构建堆的过程中,要从下到上构造,也就是为什先选择最后一个非叶子结点[n/2]?而不是从第一结点开始
* 答:在排序的时候,我们是在一个极小顶堆的基础上拿去了一个堆顶的元素,而下面的堆都是符合堆的条件的,只有第一个不符合,
* 而当我们构建堆的时候,也是用的这种方法,我们找到一个只有顶堆不符合堆的条件后,然后再进行适应,而
* 在一个错乱的数据里怎么才能找到一个只有顶堆不符合条件的结点,这个很难办,那我们就认为所有的叶子结点
* 都符合极小堆的条件,而对于第一个非叶子结点,我们就不能保证,我们我们就从最后一个非叶子结点开始构建
* */