1. 程式人生 > >資料結構————堆(TopK、堆)

資料結構————堆(TopK、堆)

是資料結構的一種,可用於排序和海量資料處理中的TopK問題
堆的邏輯結構:是一顆完全二叉樹,由於是是一顆完全二叉樹我們就可通過陣列來實現他的儲存方式。
在這裡插入圖片描述
在這裡插入圖片描述
上面是一顆完全二叉樹,分別為樹狀儲存、陣列儲存。
堆的性質
1.堆分為大堆和小堆。
2.大堆的對頂大於它的左子樹和右子樹(小堆相反)。
3.左右子樹分別為大堆(或者小堆)。
4.為方便計數滿足陣列的儲存方式有以下規律:
a.lchild(左孩子下標) = parent2+1;
b.rchild(右孩子下標) = parent
2+2;
c.parent(父親節點下標) = (child-1)/2;
有了上述性質對堆在陣列中表示和操作就方便多了


對堆有一下操作:
1.建堆(利用向下調整)
2.刪除(利用向下調整)
3.插入(利用向上調整)
在講建堆之前先說明一下向下調整演算法(以大堆為例項)()
建堆:通過了解到向下調整演算法建堆的步驟就很簡單了(從第一個非葉子節點開始呼叫向下調整演算法)
在這裡插入圖片描述
刪除:實際就是刪除堆頂元素,只需要交換堆頂和最後一個元素(即陣列末尾)然後刪除最後一個元素。對堆頂進行一次向下調整即可。
插入:實際上是在堆尾插入一個元素而非任意位置。插入只會影響一條路徑上的元素,故只需呼叫向上調整演算法即可。
向上調整演算法:
在這裡插入圖片描述
通過上面分析,整體思路已經有了,下面是實現的程式碼(由於為了說明TopK問題和堆排問題,用到堆,所以將一些介面放開以便TopK和堆排呼叫):

#include<iostream>
using namespace std;
#define MAX 100
//仿函式
template<class T>
class Greate{
public:
	bool operator()(const T& L, const T& R)
	{
		return L > R;
	}
};
	
template<class T>
class Little{
public:
	bool operator()(const T& L, const T& R)
	{
		  return L < R;
	}
};
template<class T,class com>
class Heap
{
public:
	Heap(int size)
		:_size(size)
		,heap(new T[MAX])
	{ }
	void CreatHeap(int* array,int size)
	{
		for (int i = 0; i <size; i++)
		{
			heap[i] = array[i];
		}
		//建堆
		for (int i = (_size - 2) / 2; i >= 0; i--)
		{
			AdjustDown(heap, i);
		}
	}
	void InsertHeap(int num)
	{
		heap[_size] = num;
		_size += 1;
		AdjustUp(heap, _size-1);
	}
	bool EarseHeap()
	{
		if (_size)
		{
			swap(heap[0], heap[_size - 1]);
			_size--;
			AdjustDown(heap, 0);
			return true;
		}
		return false;
	}
	//向下調整
	void AdjustDown(int* h, int root)
	{
		int parent = root;
		int lchild = parent * 2 + 1;
		while (lchild < _size)
		{
			if (lchild + 1 < _size && com(h[lchild+1] , h[lchild]))
				lchild++;
			if (com(h[lchild], h[parent]))
			{
				swap(h[parent], h[lchild]);
				parent = lchild;
				lchild = parent * 2 + 1;
			}
			else
				break;
		}
	}
	//向上調整
	void AdjustUp(int* h, int child)
	{
		int parent = (child - 1) / 2;

		while (parent >= 0)
		{
			if (com(h[child], h[parent]))
			{
				swap(h[child], h[parent]);
				child = parent;
				parent = (child - 1) / 2;
			}
			else
				break;
		}

	}
	void print()
	{
		for (int i = 0; i < _size; i++)
		{
			cout << heap[i] << " ";
		}
		cout << endl;
	}
public:
	T _size;
	T* heap;
	com com;
};

為了實現大小堆,這裡用到了仿函式,可以通過模板來選擇構建大堆或小堆。
上述堆已經實現了,下面我們藉助堆來解決一些實際問題:TopK、堆排
1.TopK問題描述:有百萬資料需要我們處理,即找到這百萬資料中的前K個最大的或者最小的。
在此之前我將一個現實生活的例項:比如我們想進入全國學霸的前二十個,我們需要做神魔,其實我們的分數只需要比前二十個中成績最低的同學高就可以進入學霸排行榜了。現在我們可以類似的套用這種模式解決我們的TopK問題了吧!
步驟(以前k個最大的為例):1.建一個大小為k小堆(百萬箇中1~k來建立)2.從第k+1個數據開始和堆頂比較比堆頂大,入堆,然後從根開始向下調整,最後堆裡面剩下的就是最大的k個。(介於方法一樣,這裡就不講前k個最小的了)。
程式碼實現:

void TopK(int array[],int size,int K)
{
	Heap<int, Little<int>> h1(3);
	h1.CreatHeap(array, 3);
	for (int i = 3; i < size; i++)
	{
		if (array[i]>h1.heap[0])
			h1.heap[0] = array[i];
		h1.AdjustDown(h1.heap, 0);
	}
	h1.print();
}
void testTopK()
{
	int array[] = { 1000, 99, 78, 53, 17, 78, 9, 45, 65, 87, 23, 31 };
	int k = 3;
	TopK(array, sizeof(array) / sizeof(array[0]), k);
}
											

說明:為了直觀看到效果,我在堆裡實現了列印的介面
堆排:利用堆排序的演算法。
還是畫圖比較好理解:
在這裡插入圖片描述
程式碼實現:

void HeapSort(int array[],int size)
{
	Heap<int, Greate<int>> Gheap(size);
	Gheap.CreatHeap(array, size);
	//堆排序
	while (Gheap._size-1)
	{
		swap(Gheap.heap[0], Gheap.heap[Gheap._size - 1]);
		Gheap._size--;
		Gheap.AdjustDown(Gheap.heap,0);
	}
	Gheap._size = size;
	Gheap.print();
}
void test_HeapSort()
{
	int array[] = { 1000, 99, 78, 53, 17, 78, 9, 45, 65, 87, 23, 31 };
	int size = sizeof(array) / sizeof(array[0]);
	HeapSort(array, size);
}