1. 从队列到优先队列:
无论栈还是队列,它们最重要最本质的操作都是入集合,出集合。队列是在队尾入队(插入),在队头出队(删除)。也即是说先出队的是先入队的,那么存在这么一种情况,如果排队的人有着一种优先级(与排队顺序无关的优先级)那么我们就需要一种新的数据结构:优先队列。它需要插入操作和删除优先级最高的元素两种操作。,
2. 优先队列的堆实现
在二叉堆的数组中,每个元素都要保证大于等于另外两个特定位置的元素。可以用一个二叉树很好的表示即所有结点都大于等于它的子结点(如果有的话)。完全二叉树用数组就可以表示,因为索引可以把父子结点联系起来,向上一层,索引除以二,向下一层,索引是两倍或者两倍加一。
3. 优先队列的对实现的核心操作
我们的父子结点的大小关系和层层递进的父子关系结合起来维护了整个堆的有序,如果我们要插入元素,就必须放在合适的位置上才能继续维持有序,这显然需要移动。当我们将元素插入到最后一位之后(那个位置最小)应该将它向上层移动,直到它的父节点不小于它,这个操作我们称之为上浮。同时,另一个操作,删除最大的元素,最大的元素就是根节点,删除之后,我们将最后那个元素(最小的元素)放到顶端,让它向下层移动,我们可以用两个子节点中大的那个和它交换,直到它比两个子节点都大。
4. 优先队列的堆实现步骤
我们选用的数据结构是数组,索引可以描述这个数据结构,基于优先队列的操作是插入和删除最大的元素。,插入我们需要实现上浮操作。上浮操作其实就是基于条件判断的不断交换角标对应元素和角标除以二的元素。而删除最大的元素就是将最后一个元素放到第一个元素,然后实现下沉操作。下沉操作是角标对应元素和角标乘以二或者乘以二加一的元素交换直到条件适合为止。
5. 优先队列的代码实现
public class MaxPQ
{
private int n;
private int[] a;
public MaxPQ(int n)
{
a = new int[n+1];
}
public void insert(int x)
{
n++;
a[n] = x;
swim(n);
}
private void swim(int x)
{
while(x>1 && a[x/2]<a[x])
{
int t = a[x];
a[x] = a[x/2];
a[x/2] = t;
x = x/2;
}
}
public int delete()
{
int max = a[1];
a[1] = a[n];
n--;
sink(1);
return max;
}
private void sink(int x)
{
while(2*x <= n)
{
int y = 2*x;
if(2*y<n && y < y+1)
{
y++;
}
if(x >= y)
break;
int t = a[x];
a[x] = a[y];
a[y] = t;
x = y;
}
}
}
堆排序:
如上所知,堆排序可以找出最大的元素,如果我们不断找出最大的元素放到最后面,然后恢复优先队列的结构,再进行,就可以实现排序。这个过程分为两个阶段,堆的构造阶段和下沉排序阶段。我们可以递归的调用sink()函数,从而构造起一个个小堆,进而完成整个堆的构造,第二个阶段是先把最大元素放在最后面,然后恢复堆。
public class DuiSort
{
private static void sink(int x,int[] a,int n)
{
while(2*x <= n)
{
int y = 2*x;
if(y<n && y < y+1)
{
y++;
}
if(x >= y)
break;
int t = a[x];
a[x] = a[y];
a[y] = t;
x = y;
}
}
public static void sort(int[] a)
{
int n = a.length;
for(int x=n/2; x>=1; x--)
{
sink(x,a,n);
}
while(n > 1)
{
int t = a[n];
a[n] = a[1];
a[1] = t;
n--;
sink(1,a,n);
}
}
public static void main(String[] args)
{
int[] arr = {0,4,5,8,7,6,9,3,1,2};
DuiSort.sort(arr);
for(int i=1; i<9 ; i++)
{
System.out.println(arr[i]);
}
}
}
代码没调好,改天慢慢调。。