堆排序(附小顶堆源码)

二叉树

        满二叉树:同时满足两个条件

                1.要么有两个孩子,要么没有孩子

                2.叶子节点在同一层

        规律:

                 如果是第n层,那么该层的节点数一定是2的n-1次方

                总共有多少节点:2的n次方-1

完全二叉树

        就是满二叉树从右往左,从下往上删,不是中间删除,就是完全二叉树

        满二叉树一定是完全二叉树

        完全二叉树不一定是满二叉树

        为什么有完全二叉树,因为他虽然是树结构,但并不希望用树结构去描述它(用数组即可描述)

        因为完全二叉树除了最后一行,其他的都是有顺序的,可以用下标表示,即有树结构的优点,也有数组的优点。

                只要搞定堆,就能搞定完全二叉树

        因为堆是特殊的完全二叉树:有序的完全二叉树,即堆

        它的有序不一样,是要求父子之间的有序(兄弟或其他节点之间不管)

父大于子:最大堆(大顶堆)

父小于子:最小堆(小顶堆)

用数组去描述堆

父节点下标为n

左孩子:2n+1

右孩子:2n+2

所以

已知左孩子下标为m,父节点的下标为:(m-1)/2

已知右孩子下标为m,父节点的下标为:(m-2)/2

已知孩子下标为m,父节点的下标一定为:(m-1)/2【因为左孩子一定是奇数,右孩子一定是偶数】因为能算出来,所以就不需要指针了

时间复杂度是log2n,不是特别稳定

头文件:

#pragma once
//小顶堆
#include<iostream>
using namespace std;

//不需要写节点来搞指针,因为做的是数组
template<class T>
class myHeap
{
public:
	myHeap() 
	{	
		pRoot = NULL;
		len = maxLen = 0; 
	}
	~myHeap()
	{
		if (pRoot)
		{
			delete[]pRoot;
		}; 
		pRoot = NULL; len = maxLen = 0;
	}
	//数组需要什么函数,它就需要什么函数
	//插入
	void insertNode(const T& data);
	//删除
	void deleteNode(const T& data);
	//删除堆顶 这个比较特殊
	T pop();
	//遍历
	void travel();
	//直接用数组的方式来构建堆
	void initHeap(T* arr, int size);

public:
	T*		pRoot;	//指向根节点的指针
	size_t	len;	//元素个数
	size_t	maxLen;	//容量
};

//插入
template<class T>
void myHeap<T>::insertNode(const T& data)
{
	//要往上做插入排序,因为小顶堆,子大于父
	//一开始直接进来,然后比较,覆盖
	//1.插入进来
	if (maxLen<=len)//需不需要申请内存
	{
		maxLen = maxLen+(((maxLen >> 1) > 1) ? maxLen >> 1 : 1);
		//开内存
		T* pNew = new T[maxLen];
		if (pRoot)
		{
			memcpy(pNew, pRoot, sizeof(T) * len);
			delete[] pRoot;
		}
		pRoot = pNew;
	}
	//上面开内存是可以省略的


#if 0 //下面可以优化



	pRoot[len++] = data;
	//2.循环和父节点比较,如果冲突,交换,不冲突,循环结束(和插入排序很像)
	if (len==1)//根节点进来直接结束
	{
		return;
	}
	int currentIdx = len - 1;//插入数的下标
	int parentIdx = (currentIdx - 1) / 2;

	while (1)
	{
		if (currentIdx<=0)//没有父节点
		{
			break;
		}
		parentIdx = (currentIdx - 1) / 2;
		if (pRoot[parentIdx]<pRoot[currentIdx])//不冲突则循环结束
		{
			break;
		}
		//交换
		temp = pRoot[currentIdx];
		pRoot[parentIdx]; = pRoot[currentIdx];
		pRoot[currentIdx] = temp;
		//上移
		currentIdx = parentIdx;
	}
#else	//优化部分
	if (len==0)
	{
		pRoot[len++] = data;
		return;
	}

	int currentIdx = len;//插入数的下标
	int parentIdx = (currentIdx - 1) / 2;
	//数据先放进来
	pRoot[currentIdx] = data;

	while (1)
	{
		if (currentIdx <= 0)//没有父节点
		{
			break;
		}
		parentIdx = (currentIdx - 1) / 2;
		if (pRoot[parentIdx] < pRoot[currentIdx])//不冲突则循环结束
		{
			break;
		}
		//冲突,父节点覆盖子节点
		pRoot[currentIdx] = pRoot[parentIdx];
		currentIdx = parentIdx;
	}
	//新数据覆盖回来
	pRoot[currentIdx] = data;
	//个数的增加
	len++;
#endif // 0
}

//删除
template<class T>
void myHeap<T>::deleteNode(const T& data)
{

}

//删除堆顶 这个比较特殊
template<class T>
T myHeap<T>::pop()
{
	if (len==0)
	{
		cout << "堆是空的"<<endl;
		return (T)0;
	}
	if (len == 1)//只有一个
	{
		len--;
		return pRoot[0];
	}
	//临时保存堆顶元素
	T temp = pRoot[0];
	//最后一个覆盖堆顶
	pRoot[0] = pRoot[len - 1];
	
	int currentIdx = 0;//从堆顶开始
	int minChild;// 最小的孩子
	while (1)
	{
		if ((currentIdx * 2 + 1) > (len - 1)||(currentIdx * 2 + 2) > (len - 1))//数组结束
		{
			break;
		}
		// 找到最小的孩子 
		minChild = currentIdx * 2 + 1;//假定左孩子比较小
		if (pRoot[minChild]>pRoot[minChild+1])//如果左比右大,右小
		{
			minChild++;
		}
		if (pRoot[len-1]<pRoot[minChild])
		{
			break;
		}
		//  进行交换(覆盖)
		pRoot[currentIdx] = pRoot[minChild];
		currentIdx = minChild;
	}
	//临时保存的堆顶元素覆盖回来
	pRoot[currentIdx] = pRoot[len-1];

	//结束
	len--;
	return temp;


}

//遍历
template<class T>
void myHeap<T>::travel()
{
	cout << "heap:";
	for (int i = 0; i < len; i++)
	{
		cout << pRoot[i] << " ";
	}
	cout<< endl;
}

//直接用数组的方式来构建堆
template<class T>
void  myHeap<T>::initHeap(T* arr, int size)
{
	//开内存
	maxLen= size;
	len = 0;
	pRoot = new T[size];
	//数据进入
	pRoot[len++] = arr[0];
	int currentIdx = len;//插入数的下标
	int parentIdx = (currentIdx - 1) / 2;
	for (int i = 1; i < size; i++)
	{
		currentIdx = len;
		parentIdx = (currentIdx - 1) / 2;

		//数据先放进来
		pRoot[currentIdx] = arr[i];

		while (1)
		{
			if (currentIdx <= 0)//没有父节点
			{
				break;
			}
			parentIdx = (currentIdx - 1) / 2;
			if (pRoot[parentIdx] < pRoot[currentIdx])//不冲突则循环结束
			{
				break;
			}
			//冲突,父节点覆盖子节点
			pRoot[currentIdx] = pRoot[parentIdx];
			currentIdx = parentIdx;
		}
		//新数据覆盖回来
		pRoot[currentIdx] = arr[i];
		//个数的增加
		len++;
		travel();
	}
}

主文件:

#include"SmallDui.h"
int main()
{
	int arr[9] = { 1992,636,54,87,542,88 ,77,8888,932};

	myHeap<int> h;
#if 0



	for (int i = 0; i < 9; i++)
	{
		h.insertNode(arr[i]);
		h.travel();
	}
#else

	h.initHeap(arr, 9);
	//堆排序,删除出来是有序的
	for (int i = 0; i < 9; i++)
	{
		cout<<h.pop() << endl;
		h.travel();
	}

#endif // 0
	while (1)
	{

	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/q244645787/article/details/128331393