树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成的具有层次关系的集合。
在该节点中表示出其孩子结点即可(优点,找孩子结点方标,找双亲结点不方便)
在该结点中表示出双亲结点即可(找双亲结点方便,找孩子结点不方便)
//孩子表示法
typedef struct int DType;
typedef struct node{
DType data;
struct node* right;
struct node* left;
}node;
一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。
二叉树的特点:
特殊的二叉树:
二叉树可以使用两种存储结构:顺序结构和链式结构。
顺序存储:
当二叉树满足完全二叉树时采用顺序存储,它一般采用数组进行存储,,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储。(二叉树在物理存储上是一个数组,在逻辑上是一个二叉树)
链式存储:
二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链和三叉链。
如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
(完整代码自取–>包含小堆,大堆实现)
让完全二叉树满足堆特点,需要将二叉树的数组进行调整,进行双亲结点和孩子结点进行比较,然后进行交换。
在这里要注意的几点:
void adjust(DType array[], int size, int parent){
//默认让child标记左孩子,因为parent可能不存在右孩子
int child = parent * 2 + 1;
while (child<size&&array[child]<array[parent]){
//右孩子存在的情况:
if (child<size&&array[child + 1]<array[child]) //要注意&&两边的先后次序,如果比较左右孩子放在前面,会造成访问越界
{
child += 1;//让child标记右孩子
}
//检测parent是否满足情况
if (array[child]<array[parent]){
swap(&array[child], &array[parent]);
//较大的双亲和孩子交换后,可能会导致子树不满足
parent = child;
child = parent * 2 + 1;
}
else{
//如果双亲比孩子节点还小
return;
}
}
}
//初始化
heap* heapInit(heap* hp, DType* array, int size){
assert(hp);
int lastnotleaf = 0;
hp->array = (DType*)malloc(sizeof(DType)*size);
if (NULL == hp->array){
return NULL;
}
hp->capacity = size;
//memcpy(hp->array, array, sizeof(sizeof(DType)*size));//以字节为准进行拷贝,大小为(sizeof(DType)*capa)
for (int i = 0; i < size; i++){
hp->array[i] = array[i];
}
hp->size = size;
//找到非叶子结点
lastnotleaf = ((size - 1) - 1) / 2;
//从该结点位置逐个往前直到根
for (int root = lastnotleaf; root >= 0; root--){
adjust(hp->array, size, root);
}
return hp;
}
在进行对的插入时,我们要进行一下几个步骤:
//插入
//插入
void heapInsert(heap* hp, DType data){
//首先确定堆是否已满
if (hp->size == hp->capacity){
//已满先进行扩容
hp->array = (DType*)realloc(hp->array,sizeof(DType)*(hp->capacity)*2);
if (NULL == hp->array){
assert(0);
return;
}
hp->capacity *= 2;
}
//先将data插入堆中
hp->array[hp->size++] = data;
//然后对插入后的孩子节点与其父节点进行比较,调换
adjustup(hp->array, hp->size);
}
void adjustup(DType* array,int size){
int child = size-1;
int parent = (child - 1) / 2;
while (child){
if (array[child] < array[parent])
{
swap(&array[child], &array[parent]);
child = parent;
parent = (child - 1) / 2;
}
else return;
}
}
堆的删除进行的步骤:
//删除
void heapdel(heap* hp){
//判断堆是否为空
if (heapempty(hp)) return;
//交换堆顶和堆尾元素
swap(&hp->array[0], &hp->array[hp->size - 1]);
//有效个数-1
hp->size -= 1;
//对现在的二叉树进行重新排序,再次成堆
adjust(hp->array, hp->size, 0);
}
//获取堆顶元素
DType heaptopdata(heap* hp){
assert(!heapempty(hp));
return hp->array[0];
}
//检测是否为空
int heapempty(heap* hp){
assert(hp);
return 0 == hp->size;
}
//获取堆里有效元素的个数
int heapsize(heap* hp){
assert(hp);
return hp->size;
}
//对的销毁
void heapdestory(heap* hp){
assert(hp);
if (hp->array){
free(hp->array);
hp->array = NULL;
hp->capacity = 0;
hp->size = 0;
}
}
void swap(DType* left, DType* right){
int temp = *left;
*left = *right;
*right = temp;
}
1.建堆(在排序中升序选择–>大堆, 降序选择–>小堆)
2.排序–>利用堆删除的思想进行排序
例如下图:我们所求升序,因此在建堆的时候先建成大堆,保证堆顶值最大,然后一次将堆顶元素与堆为元素进行交换,使得最大元素在物理存储上即在数组的最后一位,然后对剩下的重新进行向下调整,使得每次都能将剩下的最大数值放到剩下此时空间的末位,最后得到升序数组(的到降序数组与其原理类似)。
降序:利用的是小堆
int less(DType left, DType right){
//大堆
return left < right;
}
//堆排序-->降序
void sort(heap* hp){
if (heapempty(hp)) return;
int size = hp->size-1;
int count = 0;
while (size){
if (hp->array[0] < hp->array[size]){
heapdel(hp);
count++;
}
else{
size--;
}
}
hp->size += count;
}
升序:利用的是大堆
int less(DType left, DType right){
//大堆
return left > right;
}
//堆排序-->降序
void sort(heap* hp){
if (heapempty(hp)) return;
int size = hp->size-1;
int count = 0;
while (size){
if (hp->array[0] > hp->array[size]){
heapdel(hp);
count++;
}
else{
size--;
}
}
hp->size += count;
}
1.方法一–排序(O(NlogN)),但是在数据量比较大的情况下使用不了。
2.方法二–用堆处理
步骤一:用前K个元素建堆(求前K个最大的建小堆;求前K个最小的建大堆)
步骤二:(求最大的前K个)使用剩余的N-K元素一次与堆顶进行比较,如果比堆顶大,则元素进行替换,然后再进行堆的重新排序(因为我们此事件的是小堆,每次将N-K的元素与堆顶进行比较的时候,都相当与与队中最小的比较,也就是剔除堆中最小的元素)
输出前K个最大值:
//TOP-K
void TOP_K(heap* hp, int k, int* array,int size){
//建前K给元素的小堆
heapInit(hp,array,k,less);
int dex = k;
while (dex<size){
if (array[dex]>hp->array[0]){
hp->array[0] = array[dex];
adjust(hp,0);
dex++;
}
else{
dex++;
}
}
}
输出前K个最小值:
void TOP_K(heap* hp, int k, int* array,int size){
//建前K给元素的大堆
heapInit(hp,array,k,less);
int dex = k;
while (dex<size){
if (array[dex]《hp->array[0]){
hp->array[0] = array[dex];
adjust(hp,0);
dex++;
}
else{
dex++;
}
}
}