真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

堆的結(jié)構(gòu)分析與應(yīng)用

    在二叉樹中,我們用兩種方法表示二叉樹,一個(gè)是鏈表,一個(gè)是數(shù)組,但是數(shù)組比較適用于滿二叉樹或者完全二叉樹。

創(chuàng)新互聯(lián)公司是一家集網(wǎng)站建設(shè),石阡企業(yè)網(wǎng)站建設(shè),石阡品牌網(wǎng)站建設(shè),網(wǎng)站定制,石阡網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷,網(wǎng)絡(luò)優(yōu)化,石阡網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力。可充分滿足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。

    堆數(shù)據(jù)結(jié)構(gòu)是一種數(shù)組對(duì)象,它可以被視為一棵完全二叉樹結(jié)構(gòu)。

    堆結(jié)構(gòu)的二叉樹存儲(chǔ)有兩種方法:

        最大堆:每個(gè)父節(jié)點(diǎn)的都大于孩子節(jié)點(diǎn)。

        最小堆:每個(gè)父節(jié)點(diǎn)的都小于孩子節(jié)點(diǎn)。

#include
#include
#include

using namespace std;

template
class Heap
{
public:
	Heap()//無(wú)參類型的構(gòu)造函數(shù)
	{}
	Heap(T *a, size_t size)//有參類型的構(gòu)造函數(shù)
	{
		assert(a);
	
		for (size_t i = 0; i < size; i++)
		{
			_a.push_back(a[i]);
		}
		//建堆,用 向下調(diào)整 算法
		//對(duì)于一個(gè)完全二叉樹,其結(jié)點(diǎn)的父子關(guān)系與數(shù)組的下標(biāo)的關(guān)系:
		//左子下標(biāo)= 父結(jié)點(diǎn)下標(biāo)*2+1
		//右子下標(biāo)=父結(jié)點(diǎn)下標(biāo)*2+2
		for (int j = (_a.size() - 2) / 2; j >= 0; j--)//找到倒數(shù)第一個(gè)非葉子結(jié)點(diǎn)(最后一個(gè)元素一定是非葉子結(jié)點(diǎn)的子節(jié)點(diǎn))
		{
			_AdjustDown(j);
		}
		/*
		在第一次寫時(shí)定義了j為size_t類型,由于j不管怎么運(yùn)算都是無(wú)符號(hào)類型所以
		j>=0并沒(méi)有起到限制的作用,導(dǎo)致了死循環(huán)
		*/
	}
public:
	void Push(const T &x)//在最后加入一個(gè)節(jié)點(diǎn)
	{
		_a.push_back(x);
		_AdjustUp(_a.size() - 1);//向上調(diào)整,傳入最后一個(gè)元素下標(biāo)(調(diào)整x的位置)
	}
	
	void Pop()//把最大的刪掉(根)
	{
		assert(!_a.empty());

		swap(_a[0], _a[_a.size() - 1]);//把根節(jié)點(diǎn)和最后一個(gè)節(jié)點(diǎn)交換
		_a.pop_back();//刪掉最后一個(gè)節(jié)點(diǎn)
		_AdjustDown(0);//向下調(diào)整
	}
	
	void Print()
	{
		for (size_t i = 0; i < _a.size(); i++)
		{
			cout << _a[i]<<" ";
		}
		cout << endl;
	}
	size_t size()
	{
		return _a.size();
	}
	bool empty()
	{
		return _a.empty();
	}
protected:
	void _AdjustDown(size_t parent)//時(shí)間復(fù)雜度O(log2(N))
	{
		size_t child = parent * 2 + 1; //找到其左孩子
		while (child < _a.size())
		{
			if ((child + 1) < _a.size() && _a[child] < _a[child + 1])//若左孩子小于右孩子(且必須結(jié)點(diǎn)下標(biāo)不超過(guò)范圍)
			{
				++child;//指向大的
			}
			if (_a[child]>_a[parent])//若孩子大于父,則把大的放在父結(jié)點(diǎn)上
			{
				swap(_a[child], _a[parent]);
				parent = child;//父和子調(diào)整后要繼續(xù)向下調(diào)整交換后的子節(jié)點(diǎn)
				child = parent * 2 + 1;//現(xiàn)在這個(gè)調(diào)整后的節(jié)點(diǎn)的子節(jié)點(diǎn)
			}
			else
				break;
		}
	}
	
	void _AdjustUp(size_t child)//時(shí)間復(fù)雜度O(log2(N))
	{
		size_t parent = (child - 1) / 2;
		while (child > 0)
		{
			if (_a[child] > _a[parent])
			{
				swap(_a[child], _a[parent]);
				child = parent;
				parent = (child - 1) / 2;
			}
			else
				break;
		}
		
	}
private:
	vector _a;
};

測(cè)試函數(shù)

void test()
{
	int a[10] = { 5, 10, 34, 24, 2, 4, 17, 23, 12, 9 };
	Heap h2(a, 10);
	h2.Push(3);
	h2.Push(4);
	h2.Print();
	h2.Push(100);
	h2.Print();
	h2.Pop();
	h2.Print();
	h2.Pop();
	h2.Print();

}
int main()
{
	test();
	getchar();
	return 0;
}

    上面實(shí)現(xiàn)了最大堆,最小堆方法同最大堆,但是再在類中重新一遍則會(huì)使程序的可維護(hù)性降低,所以我們用仿函數(shù)來(lái)實(shí)現(xiàn)。

 

仿函數(shù)

仿函數(shù)就是使一個(gè)類使用看上去像一個(gè)函數(shù),其實(shí)現(xiàn)就是類中實(shí)現(xiàn)一個(gè)operator().這個(gè)類就有了類似函數(shù)的行為。

struct Free
{
     void operator()(void *ptr)
     {
        free( ptr);
     }
};
void Testsharedptr()
{
    int *p1=(int*)malloc(sizeof(int)*10);
    shared_ptr<int>sp1(p1,Free());//在使用完后自動(dòng)釋放p1
}

用仿函數(shù)實(shí)現(xiàn)最大堆與最小堆

template
struct Less
{
	bool operator()(const T&left, const T&right)
	{
		return left < right;
	}
};
template
struct Greater
{
	bool operator()(const T&left, const T&right)
	{
		return left>right;
	}
};

template>
class Heap
{
public:
	Heap()//無(wú)參類型的構(gòu)造函數(shù)
	{}
	Heap(T *a, size_t size)//有參類型的構(gòu)造函數(shù)
	{
		assert(a);
	
		for (size_t i = 0; i < size; i++)
		{
			_a.push_back(a[i]);
		}
		//建堆,用 向下調(diào)整 算法
		//對(duì)于一個(gè)完全二叉樹,其結(jié)點(diǎn)的父子關(guān)系與數(shù)組的下標(biāo)的關(guān)系:
		//左子下標(biāo)= 父結(jié)點(diǎn)下標(biāo)*2+1
		//右子下標(biāo)=父結(jié)點(diǎn)下標(biāo)*2+2
		for (int j = (_a.size() - 2) / 2; j >= 0; j--)//找到倒數(shù)第一個(gè)非葉子結(jié)點(diǎn)(最后一個(gè)元素一定是非葉子結(jié)點(diǎn)的子節(jié)點(diǎn))
		{
			_AdjustDown(j);
		}
	}
public:
	void Push(const T &x)//在最后加入一個(gè)節(jié)點(diǎn)
	{
		_a.push_back(x);
		_AdjustUp(_a.size() - 1);//向上調(diào)整,傳入最后一個(gè)元素下標(biāo)(調(diào)整x的位置)
	}
	void Pop()//把最大的刪掉(根)
	{
		assert(!_a.empty());

		swap(_a[0], _a[_a.size() - 1]);//把根節(jié)點(diǎn)和最后一個(gè)節(jié)點(diǎn)交換
		_a.pop_back();//刪掉最后一個(gè)節(jié)點(diǎn)
		_AdjustDown(0);//向下調(diào)整
	}
	void Print()
	{
		for (size_t i = 0; i < _a.size(); i++)
		{
			cout << _a[i]<<" ";
		}
		cout << endl;
	}
	size_t size()
	{
		return _a.size();
	}
	bool empty()
	{
		return _a.empty();
	}
protected:
	void _AdjustDown(size_t parent)//時(shí)間復(fù)雜度O(log2(N))
	{
		size_t child = parent * 2 + 1; //找到其左孩子
		compare com;
		while (child < _a.size())
		{
			if ((child + 1) < _a.size() && com(_a[child+1],_a[child]))
			{
				++child;
			}
			if (com(_a[child],_a[parent]))//
			{
				swap(_a[child], _a[parent]);
				parent = child;//父和子調(diào)整后要繼續(xù)向下調(diào)整交換后的子節(jié)點(diǎn)
				child = parent * 2 + 1;//現(xiàn)在這個(gè)調(diào)整后的節(jié)點(diǎn)的子節(jié)點(diǎn)
			}
			else
				break;
		}
	}
	void _AdjustUp(size_t child)//時(shí)間復(fù)雜度O(log2(N))
	{
		size_t parent = (child - 1) / 2;
		compare com;
		while (child > 0)
		{
			if (com(_a[child] , _a[parent]))
			{
				swap(_a[child], _a[parent]);
				child = parent;
				parent = (child - 1) / 2;
			}
			else
				break;
		}
		
	}
private:
	vector _a;
};

    這樣就實(shí)現(xiàn)了最大堆與最小堆,通過(guò)仿函數(shù),程序的可維護(hù)性好于在類中寫一個(gè)最大堆寫法和最小堆寫法。

測(cè)試

void test1()
{
	int a[10] = { 5, 10, 34, 24, 2, 4, 17, 23, 12, 9 };
	int b[10] = { 5, 10, 34, 24, 2, 4, 17, 23, 12, 9 };
	Heap> h2(a, 10);//最小堆
	h2.Push(3);
	h2.Push(4);
	h2.Print();
	h2.Push(100);
	h2.Print();
	h2.Pop();
	h2.Print();
	h2.Pop();
	h2.Print();
	
	Heaph3(b);//最大堆
        h3.Push(3);
	h3.Push(4);
	h3.Print();
	h3.Push(100);
	h3.Print();
	h3.Pop();
	h3.Print();
	h3.Pop();
	h3.Print();
}

堆的應(yīng)用:

1.堆排序

分析:由于在大堆或者小堆排完后,只能保證每個(gè)小樹為大堆或者小堆,故還需要進(jìn)行排序才能使數(shù)組元素完全依次排列。

      所以我們先用大堆排列后,使堆頂元素為大元素,再依次將堆頂元素和未交換的最后一個(gè)元素交換,交換完成后再將除了大的元素之外的元素進(jìn)行交換

/******************************
堆排序
盡建立一個(gè)大堆,每次把堆頂?shù)脑睾妥詈笠粋€(gè)還未交換的元素交換
*/
void AdjustDown(int *a, int parent, int size)
{
	int child = parent * 2 + 1;
	
	while (child < size)
	{
		if (a[child] a[parent])
		{
			swap(a[child], a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
			break;

	}
}
void HeapSort(int *a, int n)
{
	assert(a);

	for (int i = (n - 2) / 2; i >= 0; i--)
	{
		AdjustDown(a,i,n);//大堆向下調(diào)整
	}
	
	for (int i = 0; i < n; i++)
	{
		swap(a[n - 1 - i],a[0]);
		AdjustDown(a, 0, n - 1 - i);
	}

}

2.求N個(gè)元素中前K個(gè)最大的數(shù)

    分析:建立一個(gè)小堆存放前K個(gè)元素,小堆的堆頂元素為前K個(gè)中最小的值,剩下N-K個(gè)數(shù)中依次和堆頂元素相比較,若大于堆頂元素,入堆,每入堆一個(gè)元素再小堆排序一次,使堆頂元素總為這K個(gè)元素的最小值;若小于堆頂元素,則繼續(xù)下一個(gè)數(shù)比較。當(dāng)N-K個(gè)元素全比較完,堆中這K個(gè)元素就是最大的前K個(gè)數(shù)。

/*
從N個(gè)數(shù)中找到K個(gè)最大的數(shù)
建立一個(gè)小堆,大小為K,每次從第N-K的數(shù)據(jù)中取出一個(gè)和堆頂元素比較
*/
const int N = 1000000;
const int K = 100;

void AdjustDown(int *a, int parent, int size)
{
	int child = parent * 2 + 1;
	while (child  a[child + 1])
		{
			child++;
		}
		
		if (a[child] < a[parent])
		{
			swap(a[child], a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
			break;
	}
}
void GetTopK(int *a)
{
	assert(K < N);
	int top[100];

	for (size_t i = 0; i < 100; i++)
	{
		top[i] = a[i];
	}
	//建一個(gè)小堆,向下調(diào)整
	for (int i = (K - 2) / 2; i>=0; i--)
	{
		AdjustDown(top, i, K);
	}
	//從剩下的N-K的數(shù)據(jù)中拿數(shù)據(jù)和堆頂元素比較,若小于堆頂元素,則入堆
	for (size_t j = K; j < N ; j++)
	{
		if (a[j]            
            
                        
網(wǎng)站題目:堆的結(jié)構(gòu)分析與應(yīng)用
文章路徑:http://weahome.cn/article/jsgjhe.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部