Hark的資料結構與演算法練習之錦標賽排序

weixin_34402090發表於2015-03-25

演算法說明

錦標賽排序是選擇排序的一種。

實際上堆排序是錦標賽排序的優化版本,它們時間複雜度都是O(nlog2n),不同之處是堆排序的空間複雜度(O(1))遠遠低於錦標賽的空間複雜度(O(2n-1))

堆排序是基於二叉樹的, 所以錦標賽排序也是基於二叉樹的,並且是完美二叉樹。

我嘗試用最通俗的方法來做一下解釋,如果我說的不大清楚,建議大家直接看下邊的程式碼啦。

1、例如我們要對int[] arrayData = { 5, 9, 6, 7, 4, 1, 2, 3, 8 };進行升序排序

2、我們根據錦標賽演算法的要求,建立一個滿足以上陣列要求的完美二叉樹陣列,並且以上陣列中的值是在二叉樹的葉節點,並且葉節點必須滿足完美二叉樹,如果不滿足者,給上Integer.MAX_VALUE(20至25行程式碼,29至37行)

程式碼中大家發現使用的並不是int陣列,而是Node類的陣列,該類中有data和id兩個欄位。 data是實際的值, id是在陣列中的實際索引。 好,大家記住這個,id這塊後邊會用,並且是演算法中的精髓

3、葉節點我們在上邊已經填充上數字了,然後我們需要計算父節點們的值。  父節點i是i*2+1與i*2+2的值比較後,小的值。(程式碼40至46行)

4、OK,我們發現,在我們建立競賽陣列成功後,競賽陣列索引0就是最小值啦,哈哈,同時我們知道該最小值在競賽陣列中的索引位置(Node物件中的i就是索引位置啦)。 那麼!!重要的來了,我們從這個最小值索引位置與鄰節點兩兩比較,然後向上比最終比到nodes[0]。 然後就又是一個最小值出現啦!!!!重複這個過程,那麼最終原始陣列就變成降序嘍~~!!!!(51至55行程式碼)

5、至此,結束,希望大家能看懂。。。。

 

PS:錦標賽排序我看了N個部落格,我發現最後還是搞不懂,最後看了下邊《參考》中的部落格中的程式碼,才最終弄懂。 再次感嘆我的資質真的挺差勁的……

 

程式碼

使用的是java

package hark.sort.selectionsort;

import java.awt.Adjustable;

/*
 * 錦標賽排序
 */
public class TournamentTreeSort {
	public static void main(String[] args) {
		int[] arrayData = { 5, 9, 6, 7, 4, 1, 2, 3, 8 };
		TournamentTreeSortMethod(arrayData);
		for (int integer : arrayData) {
			System.out.print(integer);
			System.out.print(" ");
		}
	}

	public static void TournamentTreeSortMethod(int[] arrayData) {
		// 第一步,建立錦標賽使用的是競賽樹節點
		int leafNum = 1; // 葉子節點的數量,因為是需要完美二叉樹,所以葉節點數量需要一直乘以2
		while (leafNum < arrayData.length) {
			leafNum *= 2;
		}
		int nodeCount = leafNum * 2 - 1; // 這個是競賽陣列的數量
		Node[] nodes = new Node[nodeCount];

		// 將葉子節點資料進行初始化, 即將arrayData資料初始化至nodes節點中,並且是葉節點,如果不夠,
		// 則初始化為Integer.MAX_VALUE
		int dataIndex;
		for (int i = leafNum - 1; i < nodeCount; i++) {
			dataIndex = i - (leafNum - 1);
			if (dataIndex < arrayData.length) {
				nodes[i] = new Node(arrayData[dataIndex], i);
			} else {
				nodes[i] = new Node(Integer.MAX_VALUE, i);
			}
		}

		// 計算父節點的值。 父節點(n)的值是n*2+1與n*2+2的值的比較
		for (int i = leafNum - 2; i >= 0; i--) {
			if (nodes[i * 2 + 1].GetData() < nodes[i * 2 + 2].GetData()) {
				nodes[i] = nodes[i * 2 + 1];
			} else {
				nodes[i] = nodes[i * 2 + 2];
			}
		}

		// 這裡是真正的排序
		// 在建競賽樹的時候,nodes中的索引0已經是最小值了,所以將它放到原始資料索引0中,因為它是最小的。
		// 接著我們知道最小值在競賽陣列中的索引位置,那麼我們將該值向上進行兩兩比較,最終比較至最頂點
		for (int i = 0; i < arrayData.length; i++) {
			arrayData[i] = nodes[0].GetData();
			nodes[nodes[0].GetId()].SetData(Integer.MAX_VALUE);
			Adjust(nodes, nodes[0].GetId());
		}
	}

	/*
	 * 當去除最小的元素後,我們需要從該元素的索引往上進行陣列(是個樹喲)的調整
	 */
	public static void Adjust(Node[] nodes, int index) {
		while (index > 0) {
			// 如果是奇數節點,則臨節點是i+1,父節點是(i-1)/2
			// 如果是偶數節點,則臨節點是i-1,父節點是i/2-1

			if (index % 2 == 1) { // 奇數節點
				if (nodes[index].GetData() < nodes[index + 1].GetData()) {
					nodes[(index - 1) / 2] = nodes[index];
				} else {
					nodes[(index - 1) / 2] = nodes[index + 1];
				}
				index = (index - 1) / 2;
			} else { // 偶數結點
				if (nodes[index].GetData() < nodes[index - 1].GetData()) {
					nodes[index / 2 - 1] = nodes[index];
				} else {
					nodes[index / 2 - 1] = nodes[index - 1];
				}
				index = index / 2 - 1;
			}
		}
	}
}

class Node {
	private int data;
	private int id;

	public int GetData() {
		return data;
	}

	public void SetData(int data) {
		this.data = data;
	}

	public int GetId() {
		return id;
	}

	public Node(int data, int id) {
		this.data = data;
		this.id = id;
	}
}

  

 

 

參考

http://blog.csdn.net/hopeztm/article/details/7921686

相關文章