二叉堆(以大根堆为例):

堆的数据结构:

堆一般使用数组存储。当堆中有n个元素的时,可以将这些元素存放在数组heap的前n个单元里,其中堆的根节点中元素存放在heap[1]中。结点之间的关系有两种情况:

  • 如果根节点在数组中的位置是1,那么第i个位置的左右节点下标分别为2i、2i+1,父节点下标为 i/2。
  • 如果根节点在数组中的位置是0,那么第i个位置的左右节点下标分别为2i+1、2i+2,父节点下标为 (i-1)/2。
struct Heap
{
public:
	//EType为自定义类型
	int *heap; //存放数据的空间,下标从1开始存储数据,下标为0的作为工作空间,存储临时数据。
	int HeapSize; //HeapSize是堆元素的个数
	int MaxSize; //MaxSize是堆中最多存放元素数量
	Heap(int maxsize)  //init
	{
		MaxSize = maxsize;
		HeapSize = 0;
		heap = new int[maxsize + 1];
	}
	~Heap() //free memory
	{
		delete heap;
	}
};

初始化堆:

int Maxsize = 20;
Heap *H = new Heap(Maxsize);

交换堆中元素:

//把堆中的a,b位置的值互换
void swap(Heap &H, int a, int b)
{
	//临时存储child位置的值
	int temp = H.heap[a];
	//把index的值赋给child的位置
	H.heap[a] = H.heap[b];
	//把原来的child位置的数值赋值给index位置
	H.heap[b] = temp;
}

向上调整:

递归实现:

void HeapUp(Heap &H, int index)
{
	//注意由于数值是从下标为1开始,当index = 1的时候,已经是根节点了
	if (index > 1)
	{
		//求出父亲的节点
		int parent = index / 2;
		//获取相应位置的数值
		int parentValue = H.heap[parent];
		int indexValue = H.heap[index];
		//如果父亲节点比index的数值小,就交换二者的数值
		if (parentValue < indexValue)
		{
			//交换数值
			swap(H, parent, index);
			//递归调用
			HeapUp(H, parent);
		}
	}
}

非递归实现:

void HeapUp(Heap &H, int index)
{
	int parent = 0;

	while (index > 1)
	{
		//获取index的父节点的下标
		parent = index / 2;
		//获得父节点的值
		int parentValue = H.heap[parent];
		//获得index位置的值
		int indexValue = H.heap[index];
		//如果父节点的值小就交换
		if (parentValue < indexValue)
		{
			swap(H, parent, index);
		}
		index = parent;
	}
}

向下调整:

递归实现:

void HeapDown(Heap &H, int index)
{
	int n = H.HeapSize;

	//记录最大的那个儿子节点的位置
	int child = -1;

	if (2 * index > n)
	{
		//2*index>n说明该节点没有左右儿子节点了,那么就返回
		return;
	}
	else if (2 * index == n)
	{
		//如果只有一个儿子(左儿子节点)
		child = 2 * index;
	}
	else if (2 * index < n)
	{
		//如果左右儿子都存在
		//定义左儿子节点
		child = 2 * index;
		//如果左儿子小于右儿子的数值,取右儿子的下标
		if (H.heap[child] < H.heap[child+1])
		{
			child++;
		}
	}

	if (H.heap[child] > H.heap[index])
	{
		//交换堆中的child,和index位置的值
		swap(H, child, index);

		//完成交换后递归调用,继续下降
		HeapDown(H, child);
	}
}

非递归实现:

void HeapDown(Heap &H, int index)
{
	int child = 0;//存储左儿子的位置

	int temp = H.heap[index];
	int n = H.HeapSize;

	//如果有儿子的话
	while (2 * index <= n)
	{
		//获取左儿子的位置
		child = 2 * index;
		//如果有右儿子且右儿子数值比左儿子大
		if (child < n&&H.heap[child + 1]>H.heap[child])
		{
			child = child + 1;
		}
		//如果数值最大的儿子比temp的值大
		if (H.heap[child] > temp)
		{
			swap(H, index, child);
			index = child;
		}
		else
			break;
	}
}

插入元素:

void Insert(Heap &H, int value)
{
	//如果堆没满,堆数据数量加一,最后一个位置放入新数据
	if (H.HeapSize < H.MaxSize)
	{
		H.HeapSize++;
		H.heap[H.HeapSize] = value;
		//最后一个数据上升到合适位置
		HeapUp(H, H.HeapSize);
	}
	else
		return;
}

删除元素:

oid Delete(Heap &H, int index)
{
	//把最后的一个叶子的数值赋值给index位置
	H.heap[index] = H.heap[H.HeapSize];
	//堆元素个数减一
	H.HeapSize--;
	//index节点下沉到合适位置
	HeapDown(H, index);
}

调整堆:

void Adjust(Heap &H)
{
	//从第Heapsize/2个元素到第1个元素逐个HeapDown
	int n = H.HeapSize / 2;
	while (n >= 1)
	{
		HeapDown(H, n);
		--n;
	}
}

堆排序:

void HeapSort(Heap &H, int a[], int length)
{
	for (int i = 0; i < length; ++i)
		Insert(H, a[i]);
	while (H.HeapSize > 0)
	{
		cout << H.heap[1] << endl;
		Delete(H, 1);
	}
}

d叉堆:

d叉堆与二叉堆的区别是d叉堆是完全d叉树。

操作二叉堆d叉堆
insert O(logN) O(log(d)N)
delete O(logN) O(d*log(d)N)
merge O(N*logN) O(N*log(d)N)

 

posted @ 2023-02-05 00:34  PassCo2  阅读(119)  评论(0)    收藏  举报