アルゴリズムとデータ構造は、慎重に言葉を理解していれば、プログラマは、数学の知識を知っておく必要があり説明ほとんどのジュニアプログラマは、前のプログラムを説明するために、教師のいくつかを見ていた、前に、すべての後に、数学的アルゴリズムの多くが含ま知って好きではないかもしれそれを見つける必要があり、どんなに数学的概念でなく、アルゴリズムとデータ構造のより多くの知識を必要とする、より詳細な研究が含まれているどのようなプログラミング言語
序文
特に複雑ではありませんし、理解することができます必要な慎重な理解は、主に研究に関与したソースのこれを書き、それ自体でもある、それ自体でソートにおけるヒープソートアルゴリズムは、学習アルゴリズムとデータ構造の時間に行われる必要があります要約、それはここで説明された知識のヒープソート
定義
ウィキペディアの説明を参照します。
ヒープソート(英語:ヒープソート)が使用ヒープのために設計されたソートアルゴリズムようなデータ構造を指します。バルク特性を満たしながらスタックは、ほぼ完全な二分木構造である:すなわち、キーまたはインデックスの子ノードは、常により小さい(又はより大きい)その親ノードです。
私は、ルート以外の各ノードについて:バルク特性が最大(最大ヒーププロパティ)を満たす最大スタック・アレイ(最大ヒープヒープ)を、変換する記載された順序、バイナリツリーを上昇する点で[親(I)]≥A [i]に。最大スタックノード(ルートノードと最後の交換、スタックを除去した後の最後のスイッチングノード)から採取された最大値を繰り返し、そのため残留バルク特性の最大蓄積を維持します。
ヒープソートは、このデータ構造の「ヒープ」機能を理解する必要があり、丸暗記ではない、ヒープは、このデータ構造が最も重要であることを理解します
説明
最大ヒーププロパティは、親が以上の子ノード必要であることを示しているため、昇順の面では、我々は、値の最大値を見つけるために、最大ヒープを使用する必要があり、その後、バイナリーツリーのルートは、このヒープの最大値、最大値でなければなりません最後のスイッチングノードと、そのようなノードは、最後の最大値です。最後の後にノードによって表される最大値は、最大ヒープソート処理に関与しない、残りのノードは、このプロセスを、昇順の列の数を繰り返すことによって得ることができます
あなたは上記のようにすることを他のプロセスの最小値を見つけるために最小ヒープを使用する必要が降順
典型的には、スタック全体の実装プロセスは以下の通りである例として昇順に配列された一次元アレイによって達成されます。
- アレイのすべてのバイナリ形式の非難の数に従ってソート
- それが最大の子ノードよりも小さい場合、最後の非リーフノードからは、その後、比較値が存在し、子ノードと子の大きさ、ノードは最大値のサブツリーに更新されるスイッチングノード、
- 親と子ノードが交換される場合、それはスタックの特性を満たすために、子ノードのパイル特性を破壊することができる、すべての子ノード2が条件を満たして再帰的になるまで、処理条件の子ノードを満足する必要があります
- フォワードルートノードトラバーサルサイクルまで、ステップ2と3を繰り返します
- ルートノードは、すなわち、スタック(配列で今最初の値)の上面とは、最後の値を交換します
- ソートされた配列の長さが1になるまで、それはソートが完了したことを示している配列の長さマイナス1(最大値を除いては、最後に計算された)、1-5を繰り返します
最初のステップは、バイナリツリーを想像ソートする配列の一部、任意のコードその操作することなく、アレイ内の二分木を表現することです
全体は、2つの部分、最大スタック・アレイ、アレイノード交換の前記別の部分の前記部分に分割されています
イラスト
例えば次のようにこのようなアレイ[32、4、14、56、33、78]のように、実装のプロセスは、本明細書に記載の、ソーティングプロセスであります
最初のステップは、アレイの二分木構造
第二のステップ、最後の非14を見つけると、リーフノード14と子の大きさを比較するためには、以下の78、78および14は、ダウン検証せずに、ノード14は、位置に子ノードを見ていないし続ける切り替え、切り替えられますされます
第3のステップと、ノード4を続ける前に比べ、サブノード56の最大、4、56交換位置とダウン検証することなく、スイッチ・ノード4、子ノードの世話をし続けます
第四工程、継続ノード前に比べ、最大32の子ノード78は、32及び78は、交換位置32の後に見て確認するために、32続ける交換は、スワップすることなく、14よりも大きいです
前のステップを実行した後、我々はスタック全体のバランスがとれていることがわかります、配列がそのように理解することは、注文されていない、我々は最大ヒープ・パイルトップことがわかり、ヒープのトップは、ヒープの最大値であり、それがルートでありますノード、素子アレイインデックス0は、次のものは処理サイクルを維持することです
スタックの上部とアレイ素子78スタック素子14の上面となるた位置を切り替える最後の値は、スタックの容量が1だけデクリメントされ、我々はスタック全体のバランスを再調整する必要がある(最後のスタック78がもはや関与バランスです)
第五のステップが、残高が最後の非リーフノードから再開し、ノード56が有するバランス、交換機14及び56は、スイッチングノード14の位置を見て続けて、33 14、33及び14の交換よりも大きい、交換位置14を見て続けます子ノードは、ダウン検証を進めません。
以前に実行され、スタック全体のバランスを取る、反応器のスタックエレメント交換容量の上部には、1つのマイナスバランスを続けました
最後の非リーフノードに続く第6の工程は、平衡を開始する、残高が33個のノード、14及び33の交換を有し、交換位置14に探し続け、14はバランスされている、それが交換することなく動作させます
以前に実行され、スタック全体のバランスを取る、反応器のスタックエレメント交換容量の上部には、1つのマイナスバランスを続けました
第7のステップは、バランスは、交換することなく、子ノードが、動作しない交換位置4を見て続ける4と32の交換、最後の非リーフノードから再開します
以前に実行され、スタック全体のバランスを取る、反応器のスタックエレメント交換容量の上部には、1つのマイナスバランスを続けました
ステップ8は、最後の残高から非リーフノードを起動し続け、この時間だけ二つのノード4と14が、ヒープの特性を満たしていない、我々は継続する必要があり、14と4交換、バランス、ヒープ要素交換の上、ソート完了
コードの実装
public class HeapSort {
/**
* 数组变动次数,只是为了记录
*/
private static int time = 0;
public static void sort(int[] arrays) {
// 记录需要排序的数组长度,已经交换排好的部分需要排除
int heapLength = arrays.length;
// 循环堆化和交换的过程
while (heapLength > 1) {
// 1.将数组最大堆化
maxHeapify(arrays, heapLength);
System.out.println("数组堆化后:"+Arrays.toString(arrays));
// 2.交换堆顶元素和最后一个元素,这样就排好了最后一个元素
swap(arrays, heapLength);
System.out.println("数组交换堆顶元素后:"+Arrays.toString(arrays));
// 每次heapLength需减1
heapLength--;
}
}
private static void maxHeapify(int[] arrays, int heapLength) {
// 从最后一个非叶子节点开始,最后一个非叶子节点 为 (heapLength >>> 1) - 1
for (int i = (heapLength >>> 1) - 1; i >= 0; i--) {
// 保存当前索引位置
int currentIndex = i;
// (currentIndex << 1) + 1 为当前节点左子节点索引
// (currentIndex << 1) + 2 为当前节点右子节点索引
int leftChildIndex = (currentIndex << 1) + 1;
int rightChildIndex = leftChildIndex + 1;
// 子节点中最大值的索引
int maxChildIndex = -1;
// 判断当前节点是否有子节点
while (leftChildIndex <= (heapLength - 1)) {
// 先赋值
maxChildIndex = leftChildIndex;
// 右子节点存在,则找子节点中的最大值
if (rightChildIndex <= (heapLength - 1) && arrays[leftChildIndex] < arrays[rightChildIndex]) {
maxChildIndex = rightChildIndex;
}
if (arrays[maxChildIndex] > arrays[currentIndex]) {
// 和子节点交换当前索引值
int temp = arrays[currentIndex];
arrays[currentIndex] = arrays[maxChildIndex];
arrays[maxChildIndex] = temp;
time++;
System.out.println("数组第" + time + "次变动" + Arrays.toString(arrays));
}
// 继续判断交换后原子节点处是否满足堆的特性,直到当前节点下的局部二叉树完全满足堆的特性
leftChildIndex = (maxChildIndex << 1) + 1;
rightChildIndex = leftChildIndex + 1;
currentIndex = maxChildIndex;
}
}
}
private static void swap(int[] arrays, int heapLength) {
// 将最后一个数据与堆顶数据交换
int temp = arrays[0];
arrays[0] = arrays[heapLength - 1];
arrays[heapLength - 1] = temp;
}
public static void main(String[] args) {
int[] arrays = { 32, 4, 14, 56, 33, 78 };
System.out.println("原数组:" + Arrays.toString(arrays));
HeapSort.sort(arrays);
System.out.println("排序后:" + Arrays.toString(arrays));
}
}
結果:
原数组:[32, 4, 14, 56, 33, 78]
数组第1次变动[32, 4, 78, 56, 33, 14]
数组第2次变动[32, 56, 78, 4, 33, 14]
数组第3次变动[78, 56, 32, 4, 33, 14]
数组堆化后:[78, 56, 32, 4, 33, 14]
数组交换堆顶元素后:[14, 56, 32, 4, 33, 78]
数组第4次变动[56, 14, 32, 4, 33, 78]
数组第5次变动[56, 33, 32, 4, 14, 78]
数组堆化后:[56, 33, 32, 4, 14, 78]
数组交换堆顶元素后:[14, 33, 32, 4, 56, 78]
数组第6次变动[33, 14, 32, 4, 56, 78]
数组堆化后:[33, 14, 32, 4, 56, 78]
数组交换堆顶元素后:[4, 14, 32, 33, 56, 78]
数组第7次变动[32, 14, 4, 33, 56, 78]
数组堆化后:[32, 14, 4, 33, 56, 78]
数组交换堆顶元素后:[4, 14, 32, 33, 56, 78]
数组第8次变动[14, 4, 32, 33, 56, 78]
数组堆化后:[14, 4, 32, 33, 56, 78]
数组交换堆顶元素后:[4, 14, 32, 33, 56, 78]
排序后:[4, 14, 32, 33, 56, 78]
全体の注文プロセスノード変更処理も上にプリントアウトされた、と11の前に描かれた図形が確認され、彼らは下でテストされるだろう
本質的に同じ次のようにWikipediaの実現は、読者は以下を参照することができます。
public class HeapSort {
/**
* 堆排序数组
*/
private int[] arrays;
/**
* 数组变动次数,只是为了记录
*/
private static int time = 0;
public HeapSort(int[] arrays) {
this.arrays = arrays;
}
public void sort() {
// 1.将数组堆化
// 从第一个非叶子节点length >> 1 - 1开始,叶子节点不需要堆化调整
// maxHeapify 调整index处及其子节点满足堆的特性
int length = arrays.length - 1;
for (int index = arrays.length >> 1 - 1; index >= 0; index--) {
maxHeapify(index, length);
}
// 第一次初始化堆之后的数组:
System.out.println("初始化堆后的数组:" + Arrays.toString(arrays));
// 2.堆化数据排序
// 先将已经堆化的数据堆顶数据(数组索引为0)与堆中最后一个元素交换
// 交换完毕后对剩余节点重新堆化
// 循环执行
for (int i = length; i > 0; i--) {
swap(0, i);
System.out.println("交换堆顶元素后数组:" + Arrays.toString(arrays));
maxHeapify(0, i - 1);
}
}
private void maxHeapify(int index, int length) {
// (index << 1) + 1 为当前节点左子节点索引
// leftChildIndex + 1 为当前节点右子节点索引
int leftChildIndex = (index << 1) + 1;
int rightChildIndex = leftChildIndex + 1;
// 子节点中最大值的索引,默认左子节点
int maxChildIndex = leftChildIndex;
// 左子节点已经超过堆化数组的长度,直接返回
if (leftChildIndex > length) {
return;
}
// 右子节点对应值比左子节点大,则替换maxChildIndex
if (rightChildIndex <= length && arrays[rightChildIndex] > arrays[leftChildIndex]) {
maxChildIndex = rightChildIndex;
}
// 判断是否需要交换
if (arrays[index] < arrays[maxChildIndex]) {
// 交换父子节点
swap(index, maxChildIndex);
// 这里主要是打印日志查看变化过程
time++;
System.out.println("数组第" + time + "次变动" + Arrays.toString(arrays));
// 交换之后对子节点位置进行maxHeapify操作,使其保持堆特性
maxHeapify(maxChildIndex, length);
}
}
private void swap(int a, int b) {
// 数组数据交换
int temp = arrays[b];
arrays[b] = arrays[a];
arrays[a] = temp;
}
public static void main(String[] args) {
int[] arrays = { 32, 4, 14, 56, 33, 78 };
System.out.println("原数组:" + Arrays.toString(arrays));
new HeapSort(arrays).sort();
System.out.println("排序后:" + Arrays.toString(arrays));
}
}
概要
全体的に、ヒープの並べ替えは、理解することは難しいことではありません、非常に複雑ではない、新しい人々の多くは、アルゴリズムを覚えることができ、すべての最初の、必要ではない、最も重要なの名前からわかるように「スタック」の単語、ときに、このようなものです私たちはこの山の特性を考えると、この機能はそれを自分で実装することができることを覚えて、その実現のプロセスということを理解することが重要です
時間の複雑さと空間の複雑さは、私はここで説明しませんが、この複雑さは、この値がある理由を理解するために、自分の情報を検索することが必要である、結局のところ、我々は使用このアルゴリズムは、私たちのために、分析の複雑さ、いくつかのシーンに適しています必要
ご質問がある場合は上記の指摘してください、私は速やかに修正検証します、ありがとうございました