持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天,点击查看活动详情
接昨天的思路,今天梳理如何编写代码
代码思路
(1)结合文章的代码,需要注意的是这里i不是从1开始的,是从0开始的,则i其左孩子结点为2i+1,右孩子结点为2i
(2)先看adjustHeap(int paraStart, int paraLength)这个方法中,for循环是根据i处父节点向下去筛选最大的结点,第一个if是判断父节点的孩子结点中的最大结点 第二个if则是与父节点进行比较,是否大于父节点,若大于则需要交换位置,在这里,交换了父节点并不能确定父节点的最终位置,因为可能交换了位置会破坏下面的堆结构,还需要向下级继续调整。
(3)堆排序方法heapSort(),第一个for循环是构建初始堆,第二个循环则是在循环输出堆排序,我们从代码也可以看到,每取一个堆顶元素则要进行一次堆调整、
public void heapSort() {
//构建堆
DataNode tempNode;
for (int i = length/2 -1; i >= 0; i--) {
adjustHeap(i, length);
}
System.out.println("The initial heap: " + this + "\r\n");
// Step 2. Swap and reconstruct.
for (int i = length-1; i > 0; i--) {
tempNode = data[0];
data[0] = data[i];
data[i] = tempNode; //把最大的数放在堆底
adjustHeap(0, i); //调整剩余的堆
System.out.println("Round " + (length - i) + ": " + this);
}
}
public void adjustHeap(int paraStart, int paraLength) {
DataNode tempNode = data[paraStart];
int tempParent = paraStart;
int tempKey = data[paraStart].key;
//这里i是从0开始的 注意
for (int tempChild = paraStart*2 + 1; tempChild < paraLength; tempChild = tempChild*2 + 1) {
if (tempChild + 1 < paraLength) {
//右孩子大于左孩子
if (data[tempChild].key < data[tempChild+1].key) {
tempChild++; //找到孩子结点中最大的结点与双亲比较
}
}
System.out.println("The parent position is " + tempParent + " and the child is " + tempChild);
if (tempKey < data[tempChild].key) {
//将孩子结点中较大的与父节点交换位置
data[tempParent] = data[tempChild];
System.out.println("Move " + data[tempChild].key + " to position " + tempParent);
tempParent = tempChild; //修改父节点位置,可能交换位置会破坏结构 继续向下筛选
}else {
break; //筛选结束
}
}
data[tempParent] = tempNode; //将这个父节点放在这一趟堆排序的最终位置
}
总结:
今天学习了堆排序,我认为学习堆排序最重要的是建堆,自己能够去手动模拟出建堆的过程,堆排序近似完全二叉树,在实现堆排序的过程中也用到了完全二叉树的特点。对比昨天学习的选择排序,其实堆排序也是选择排序的一种,但是堆排序的选择相比昨天的直接选择排序改进了许多。