(叠甲:如有侵权请联系,内容都是自己学习的总结,一定不全面,仅当互相交流(轻点骂)我也只是站在巨人肩膀上的一个小卡拉米,已老实,求放过)
注:本文章代码使用语言为C/C++
什么是数据结构?
数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。
数据结构主要分为逻辑结构和物理结构。逻辑结构反映数据元素之间的逻辑关系,如集合、线性结构、树形结构、图形结构等。物理结构则是数据结构在计算机中的存储形式,包括顺序存储结构和链式存储结构。
数据结构的种类有哪些?
顺序表、链表、栈、队列、二叉树、堆、平衡二叉树(AVL树、红黑树)、有向图、无向图、加权图、哈希表等——本文章暂时主要介绍平衡二叉树之前的几种。
顺序表
顺序表是一种物理地址连续的储存单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
1.静态顺序表:使用定长数组存储元素
#define N 10
typedef int SLDataType;
typedef struct SeqList
{
SLDataType arry[N]; //定长数组
size_t size; //有效数据的个数
}SeqList;
2.动态顺序表:使用动态开辟的数组存储
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* arry; //定长数组
size_t size; //有效数据的个数
size_t capacity; //容量大小
}SeqList;
在现实的生活中,基本都是使用动态顺序表,根据实际情况进行空间的分配。静态顺序表使用的较少,静态顺序表的定长如果不合理的话,要么就造成了空间的浪费要么就不够用,这是一个不好掌控的过程,一下是以动态顺序表实现的接口实现。
SeqList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#define Initsize 4
typedef int SLDatatype ;
typedef struct Seqlist
{
SLDatatype* arr;
int size;
int capacity;
}SL;
//初始化动态顺序表
void SlInit(SL*);
//检查容量
void CheckCapacity(SL* a);
//尾插
void SLPushBack(SL*, SLDatatype);
//尾删
void SLDeleteBack(SL* a);
//头插
void SLPushFront(SL* a, SLDatatype);
//头删
void SLDeleteFront(SL* a);
//打印
void SLprintf(SL* a);
//在任意位置插入
void SLInsert(SL*,int, SLDatatype);
//在任意位置删除
void SLErase(SL*, int);
//查找
int SLFind(SL*, SLDatatype);
//修改
void SLModify(SL*, int, SLDatatype);
//销毁动态顺序表
void SlDestroy(SL*);
SeqList.c
#include"seqlist.h"
#define _CRT_SECURE_NO_WARNINGS 1
void SlInit(SL* a)
{
a->capacity = 0;
a->size = 0;
a->arr = NULL;
}
void CheckCapacity(SL* a)
{
SLDatatype* tem = NULL;
if (a->capacity == a->size)
{
int newcapacity = a->capacity == 0 ? 4 : a->capacity * 2;
a->capacity = newcapacity;
tem = (SLDatatype*)realloc(a->arr, a->capacity * sizeof(SLDatatype));
if (tem==NULL)
{
perror(realloc);
exit(-1);
}
a->arr = tem;
}
}
void SLPushBack(SL*a, SLDatatype b)
{
CheckCapacity(a);
*(a->arr + a->size) = b;
a->size++;
}
void SLDeleteBack(SL* a)
{
assert(a->arr);
a->size--;
}
void SLPushFront(SL* a, SLDatatype b)
{
CheckCapacity(a);
int i = 0;
for (i = a->size; i>0; i--)
{
*(a->arr +i) = *(a->arr +i-1);
}
*(a->arr) = b;
a->size++;
}
void SLDeleteFront(SL* a)
{
assert(a->arr);
int i = 0;
for (i = a->size; i >= 0; i--)
{
*(a->arr + i) = *(a->arr + i+1);
}
a->size--;
}
void SLprintf(SL* a)
{
int i = 0;
for (i = 0; i < a->size; i++)
{
printf("%d ", *(a->arr + i));
}
printf("\n");
}
void SLInsert(SL*a, int pos, SLDatatype x)
{
CheckCapacity(a);
int i = 0;
for (i = a->size; i >=pos; i--)
{
*(a->arr +i) = *(a->arr + i-1);
}
*(a->arr + pos- 1) = x;
a->size++;
}
void SLErase(SL*a, int pos)
{
int i = 0;
for (i = pos; i < a->size; i++)
{
*(a->arr + i - 1) = * (a->arr + i);
}
a->size--;
}
int SLFind(SL*a, SLDatatype x)
{
int i = 0;
int flag = 0;
for (i = 0; i < a->size; i++)
{
if (*(a->arr + i )== x)
{
printf("找到了,该值的位置是%d\n",i+1);
flag = 1;
}
}
if (flag == 0)
{
printf("没找到");
}
return 0;
}
void SLModify(SL*a, int pos, SLDatatype x)
{
*(a->arr + pos - 1) = x;
}
void SlDestroy(SL* a)
{
free(a->arr);
a->arr = NULL;
a->capacity = 0;
a->size = 0;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"seqlist.h"
int main()
{
SL sl;
SlInit(&sl);
SLPushFront(&sl,1);
SLPushFront(&sl, 2);
SLPushFront(&sl, 3);
SLPushFront(&sl, 4);
SLPushFront(&sl, 5);
SLprintf(&sl);
SLModify(&sl, 3, 8);
SLprintf(&sl);
return 0;
}
链表
链表是一种物理存储结构上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。由于链表有带头/不带头、循环/不循环、单向/双向的3种可选项,组合起来就有8种结果。
无头单项非循环链表,结构简单,一般不会单独用来存储数据,实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。
带头双线循环链表,结构最复杂,一般用在单独存储数据,实际中使用的链表数据结构,都是带头双向循环表。
无头/单向/非循环链表的增删改查实现
SList.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int SLTDataType;
typedef struct SlitNode
{
SLTDataType data;
struct SLTNode* next;
}SLTNode;
//初始化
void SLPInit(SLTNode* phead);
//打印
void SLTPrint(SLTNode*phead);
//头插
void SLPushFront(SLTNode** phead, SLTDataType x);
//尾插
void SLPushBack(SLTNode** phead, SLTDataType x);
//头删
void DestroyFront(SLTNode** phead);
//尾删
void DestroyBack(SLTNode** phead);
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
//修改
void SLTChange(SLTNode* phead, SLTDataType x, SLTDataType y);
//销毁
void SLTDestroy(SLTNode** phead);
//中间位置插入
void SLTIsert(SLTNode* phead, SLTDataType x , SLTDataType y);
SList.c
#include"slist.h"
//初始化
void SLPInit(SLTNode* phead)
{
phead->data = 0;
phead->next = NULL;
}
void SLTPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
SLTNode* newnode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode==NULL)
{
perror("malloc faild:");
return NULL;
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void SLPushFront(SLTNode** phead, SLTDataType x)
{
SLTNode* cur = newnode(x);
if (*phead == NULL)
{
*phead = cur;
}
else
{
cur->next = *phead;
*phead = cur;
}
}
void SLPushBack(SLTNode** phead, SLTDataType x)
{
SLTNode* cur = newnode(x);
if (*phead == NULL)
{
*phead = cur;
}
else
{
SLTNode* tial = *phead;
while (tial->next!=NULL)
{
tial = tial->next;
}
tial->next = cur;
}
}
void DestroyFront(SLTNode ** phead)
{
assert(*phead);
if ((*phead)->next == NULL)
{
free(*phead);
*phead = NULL;
}
else
{
SLTNode* cur = *phead;
*phead = (*phead)->next;
free(cur);
cur = NULL;
}
}
void DestroyBack(SLTNode** phead)
{
assert(*phead);
if ((*phead)->next == NULL)
{
free(*phead);
*phead = NULL;
}
else
{
SLTNode* tail = *phead;
SLTNode* cur = NULL;
while (tail->next)
{
cur = tail;
tail = tail->next;
}
free(tail);
tail = NULL;
cur->next = NULL;
}
}
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
assert(phead);
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void SLTChange(SLTNode* phead, SLTDataType x, SLTDataType y)
{
assert(phead);
SLTNode* node = SLTFind(phead, x);
node->data = y;
}
void SLTDestroy(SLTNode** phead)
{
assert(*phead);
SLTNode* cur = *phead;
while (cur)
{
cur= (cur)->next;
free(*phead);
*phead = cur;
}
free(cur);
free(*phead);
*phead = NULL;
}
//中间位置插入
void SLTIsert(SLTNode* phead, SLTDataType x, SLTDataType y)
{
SLTNode* cur = NULL;
SLTNode* node = SLTFind(phead, x);
SLTNode* new = newnode(y);
cur = node->next;
node->next = new;
new->next = cur;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"slist.h"
int main()
{
SLTNode* list = NULL;
SLPushFront(&list,1);
SLPushFront(&list,2);
SLPushFront(&list,3);
SLPushFront(&list,4);
SLTPrint(list);
SLTIsert(list,4, 5);
SLTPrint(list);
return 0;
}
有头/双向/循环链表的增删改查实现
DList.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int Typedata;
//typedef int SLTDataType;
//typedef struct SlitNode
//{
// SLTDataType data;
// struct SLTNode* next;
//}SLTNode;
typedef struct DList
{
Typedata data;
struct DList* phead;
struct DList* next;
}DList;
//初始化
DList* DLiIniti();
//新节点
DList* creatnewnode(Typedata x);
//头插
void DLPFront(DList*head , Typedata x);
//尾插
void DLPBack(DList* head, Typedata x);
//尾删
void DLPushBack(DList* head);
//头删
void DLPushFront(DList* head);
//查找
DList* DLPFind(DList* head, Typedata pos);
//打印
void Printf(DList* head);
//双链表在pos位置前面进行插入
void PosFront(DList* head, Typedata x, Typedata pos);
//双链表在pos位置进行删除
void PosPush(DList* head, Typedata pos);
//双链表的销毁
void DLPDestory(DList** head);
DList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"dlist.h"
//初始化
DList* DLiIniti()
{
DList* head = (DList*)malloc(sizeof(DList));
assert(head);
head->data = 0;
head->phead = head;
head->next = head;
return head;
}
//打印
void Printf(DList* head)
{
assert(head);
DList* cur = head;
cur = cur->next;
while (cur != head)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
//新节点
DList* creatnewnode(Typedata x)
{
DList* newnode = (DList*)malloc(sizeof(DList));
newnode->data = x;
newnode->phead = NULL;
newnode->next = NULL;
return newnode;
}
//头插
void DLPFront(DList* head, Typedata x)
{
DList* newnode = creatnewnode(x);
/*if (head->next = head)
{
DList* newnode = creatnewnode(x);
head->next = newnode;
head->phead = newnode;
newnode->next = head;
newnode->phead = head;
}
else
{*/
DList* tem = head->next;
head->next = newnode;
newnode->phead = head;
newnode->next = tem;
tem->phead = newnode;
///*}*/
}
//尾插
void DLPBack(DList* head, Typedata x)
{
DList* newnode = creatnewnode(x);
//head->next = newnode;
//head->phead = newnode;
//newnode->next = head;
//newnode->phead = head;
DList* tem = head->phead;
tem->next = newnode;
newnode->phead = tem;
head->phead = newnode;
newnode->next = head;
}
//尾删
void DLPushBack(DList* head)
{
assert(head);
assert(head->next != head);
DList* tail= head->phead;
DList* pretail = tail->phead;
free(tail);
tail = NULL;
head->phead = pretail;
pretail->next = head;
}
//头删
void DLPushFront(DList* head)
{
assert(head);
assert(head->next != head);
if (head->next->next == head)
{
free(head->next);
head->next = NULL;
head->next = head;
head->phead = head;
}
DList* tem = head->next;
head->next = head->next->next;
tem->next->phead = head;
free(tem);
tem = NULL;
}
//查找
DList* DLPFind(DList* head ,Typedata pos)
{
assert(head);
DList* cur = head->next;
while (cur!=head)
{
if (cur->data == pos)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
//双链表在pos位置前面进行插入
void PosFront(DList* head, Typedata x, Typedata pos)
{
assert(head);
DList* tem = DLPFind(head, pos);
if (tem == NULL)
{
return;
}
DList* newnode = creatnewnode(x);
DList* cur = tem->phead;
cur->next = newnode;
newnode->phead = cur;
newnode->next = tem;
}
//双链表在pos位置进行删除
void PosPush(DList* head, Typedata pos)
{
assert(head);
DList* tem = DLPFind(head, pos);
if (tem == NULL)
{
return;
}
DList* cur = tem->phead;
cur->next = tem->next;
tem->next->phead = cur;
free(tem);
tem = NULL;
}
//双链表的销毁
void DLPDestory(DList** head)
{
DList* cur = (*head)->next ;
while (cur != *head)
{
DList* tem = cur;
cur = cur->next;
free(tem);
tem = NULL;
}
free(*head);
*head = NULL;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"dlist.h"
int main()
{
DList* head = DLiIniti();
DLPFront(head,1);
DLPFront(head, 2);
DLPFront(head, 3);
DLPFront(head, 4);
DLPBack(head, 5);
DLPBack(head, 6);
DLPBack(head, 7);
Printf(head);
PosFront(head, 8, 5);
Printf(head);
PosPush(head, 8);
Printf(head);
DLPDestory(&head);
return 0;
}
栈
一种特殊的线性表,只允许在固定的一端进行插入和删除元素操作,进行数据插入和删除操作的一段称为栈顶,另外一段称为栈底。栈中的元素遵循后进先出的原则。
压栈:栈的插入操作叫做进栈,入数据在栈顶。
出栈:栈的删除叫做出栈,出数据也在栈顶。
栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些,因为数组在尾上插入数据的代价比较小。如果使用数组来实现的话,栈简直就是顺序表puls,也分静态和动态,动态可增长的栈使用的较多。
静态栈
typedef int STDataType;
#define N 10
typedef struct Stack
{
STData a[N];
size_t top; //栈顶
}Stack;
动态栈
typedef int STDataType;
#define N 10
typedef struct Stack
{
STData a[N];
size_t top; //栈顶
size_t capacity; //容量
}Stack;
动态增长栈的增删改查实现
Stack.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int STDataType;
typedef struct stack
{
STDataType* a;
int top;//顶栈
int capacity;//容量
}Stack;
//初始化栈
void StackInit(Stack* ps);
//判断是否有空间
void StackCheck(Stack* ps);
//入栈
void StackPush(Stack* ps, STDataType data);
//判断是否是空
bool StackEmpty(Stack* ps);
//出栈
void StackPop(Stack* ps);
//获取栈顶元素
STDataType StackTop(Stack* ps);
//销毁栈
void StackDestory(Stack* ps);
Stack.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"stack.h"
//初始化栈
void StackInit(Stack* ps)
{
assert(ps);
ps->a = NULL;
ps->capacity = 0;
ps->top = 0;
}
//判断是否有空间
void StackCheck(Stack* ps)
{
assert(ps);
if (ps->top == ps->capacity)
{
if (ps->capacity == 0)
{
int newcapacity = 4;
STDataType* tem = NULL;
tem = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
if (tem == NULL)
{
perror("realloc fail\n");
}
ps->a = tem;
ps->capacity = newcapacity;
}
else
{
int newcapacity = 2*ps->capacity;
STDataType* tem = NULL;
tem = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
if (tem == NULL)
{
perror("realloc fail\n");
}
ps->a = tem;
ps->capacity = newcapacity;
}
}
}
//入栈
void StackPush(Stack* ps, STDataType data)
{
StackCheck(ps);
ps->a[ps->top] = data;
++ps->top;
}
//判断是否是空
bool StackEmpty(Stack* ps)
{
assert(ps);
return ps->top == 0;
}
//出栈
void StackPop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));
--ps->top;
}
//获取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->a[ps->top-1];
}
//销毁栈
void StackDestory(Stack* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = 0;
ps->top = 0;
}
//确定栈中有效数据个数
int StackSize(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->top;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"stack.h"
int main()
{
Stack ps;
StackInit(&ps);
StackPush(&ps, 1);
StackPush(&ps, 2);
StackPush(&ps, 3);
StackPush(&ps, 4);
StackPush(&ps, 5);
printf("%d\n",StackTop(&ps));
StackPop(&ps);
printf("%d\n", StackTop(&ps));
StackPop(&ps);
StackPush(&ps, 6);
StackPush(&ps, 7);
StackPush(&ps, 8);
printf("%d\n", StackTop(&ps));
StackPop(&ps);
printf("%d\n", StackTop(&ps));
StackPop(&ps);
printf("%d\n", StackTop(&ps));
StackPop(&ps);
printf("%d\n", StackTop(&ps));
StackPop(&ps);
printf("%d\n", StackTop(&ps));
StackPop(&ps);
printf("%d\n", StackTop(&ps));
StackPop(&ps);
return 0;
}
队列
只允许在 一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出的原则,进行插入操作的一端称为队尾——入队,进行出数据的操纵一端称为队头——出队。
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为使用数组的结构,出队列在链表上会更高。
一下是使用单向/非循环/有头链表实现的队列
Queue.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//单链表
typedef int QDataType;
typedef struct QListNode
{
struct QListNode* next;
QDataType data;
}QNode;
//队头,队尾
typedef struct Queue
{
QNode* front;
QNode* rear;
}Queue;
//初始化队列
void QueueInit(Queue* q);
//判断队列是否为空
bool QueueEmpty(Queue* q);
//对头出队列
void QueuePop(Queue* q);
//获取队头的数据
QDataType QueueFront(Queue* q);
//获取队尾的数据
QDataType QueueRear(Queue* q);
//获取队列有效个数
int QueueSize(Queue* q);
//销毁队列
void QueueDestory(Queue* q);
Queue.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"
//队列初始化
void QueueInit(Queue* q)
{
assert(q);
q->front = NULL;
q->rear = NULL;
}
//队尾入队
void QueuePush(Queue* q, QDataType x)
{
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail\n");
return ;
}
newnode->data = x;
newnode->next = NULL;
if (q->rear == NULL)
{
assert(q->front == NULL);
q->rear = newnode;
q->front = newnode;
}
else
{
q->rear->next = newnode;
q->rear = newnode;
}
}
//判断队列是否为空
bool QueueEmpty(Queue* q)
{
assert(q);
return q->front==NULL;
}
//对头出队列
void QueuePop(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
if (q->front->next == NULL)
{
free(q->front);
q->front = NULL;
q->rear = NULL;
}
QNode* next = q->front->next;
free(q->front);
q->front = next;
}
//获取对头的数据
QDataType QueueFront(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->front->data;
}
//获取队尾的数据
QDataType QueueRear(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
return q->rear->data;
}
//获取队列有效个数
int QueueSize(Queue* q)
{
assert(q);
assert(!QueueEmpty(q));
size_t size = 0;
QNode* cur = q->front;
while (cur)
{
++size;
cur = cur->next;
}
return size;
}
void QueueDestory(Queue* q)
{
while (q->front)
{
QueuePop(q);
}
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"
int main()
{
Queue q;
QueueInit(&q);
QueuePush(&q, 1);
QueuePush(&q, 2);
QueuePush(&q, 3);
QueuePush(&q, 4);
QueuePush(&q, 5);
printf("%d\n", QueueFront(&q));
printf("%d\n", QueueSize(&q));
QueuePop(&q);
printf("%d\n", QueueFront(&q));
printf("%d\n", QueueSize(&q));
QueuePop(&q);
printf("%d\n", QueueRear(&q));
printf("%d\n", QueueSize(&q));
QueuePop(&q);
printf("%d\n", QueueRear(&q));
QueuePop(&q);
printf("%d\n", QueueRear(&q));
return 0;
}
补充:实际中还有一种队列叫循环队列,同样的,可以使用数组实现,也可以使用循环链表实现。
二叉树
一颗二叉树是结点的一个有限集合,该集合可能为空,或者由一个根节点加上两颗称为左子树和右子树的二叉树组成。
二叉树不存在度大于2的结点,二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树。对于任意的二叉树都是由空树、只有根节点、只有左子树、只有右子树、左右子树均在的情况复合而成。
特殊的二叉树
满二叉树:一个二叉树,如果每一层的结点数字、都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是,则它就是满二叉树。
完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树引出来的,对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点——对应时称之为完全二叉树。要注意的是满二叉树是一种特殊的完全二叉树。
二叉树的性质
1.若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有个结点。
2.若规定根节点的层数为1,则深度为h的二叉树的最大结点数为.
3.对任何一颗二叉树,如果度为0其叶结点个数为,度为2的分支结点为
,则有
=
4.若规定根结点的层数为1,具有n个结点的满二叉树的深度为。
5.对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有结点从0开始编号,则对与序号为i的结点有:
1)若i>0;i位置结点的双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点;
2)若2i+1<n,左孩子序号:2i+1,2i+1>=n则无左孩子;
3)若2i+2<n,右孩子序号;2i+2,2i+2>=n则无右孩子;
二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。
1.顺序存储,顺序储存就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费,而且现实使用中只有堆才会使用数组来存储。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。
2.链式存储
二叉树的链式存储结构是指,用链表来表示一颗二叉树,即用链来指示元素的逻辑关系。通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址。链式结构又分为二叉链和三叉链,初级一般都是二叉链,后面的高阶数据结构,如红黑树会用到三叉链。
二叉树的顺序结构
现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意这里的堆和操作系统虚拟进程地址空间中的堆是两码事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。
堆
如果一个关键码的集合K= {},把所有元素按照完全二叉树的顺序存储方式在一个一维数组中,并满足
且
或者
且
(i = 0,1,.....,)则称为小堆(大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做小堆或是小根堆。
堆的性质:
1.堆中某一个结点的值总是不大于或不小于其父结点的值;
2.堆总是一颗完全二叉树;
堆的增删改查实现
注:堆的向上向下调整算法是由前提的,左右子树必须要是一个堆!
Heap.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int HPDataType;
typedef struct Heap
{
HPDataType* a;
int size;
int capacity;
}Heap;
//初始化
void HeapInit(Heap* hp);
//向下调整
void JudgeDown(Heap* hp, int size,int root);
//向上调整
void Judgeup(Heap* hp, int size);
//插入
void HeapPush(Heap* hp, HPDataType x);
//销毁
void HeapDestory(Heap* hp);
//取顶部数据
HPDataType HeapTop(Heap* hp);
//堆的删除
void HeapPop(Heap* hp);
//堆的数据个数
int HeapSize(Heap* hp);
//堆的判空
bool HeapEmty(Heap* hp);
//数据的交换
void Sweap(HPDataType* a, HPDataType* b);
Heap.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"
//初始化
void HeapInit(Heap* hp)
{
assert(hp);
hp->a = NULL;
hp->capacity = 0;
hp->size = 0;
}
//数据的交换
void Sweap(HPDataType* a, HPDataType* b)
{
assert(a);
assert(b);
int tem = *a;
*a = *b;
*b = tem;
}
//向下调整(建立小堆)
void JudgeDown(Heap* hp, int size, int root)
{
assert(hp);
int parent = root;
int child = parent * 2 + 1;
while (child<size)
{
if ((child + 1<size) && (hp->a[child + 1] < hp->a[child]))
{
child++;
}
//如果孩子小于父亲的话就换
if (hp->a[child] < hp->a[parent])
{
Sweap(&hp->a[child], &hp->a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
//向上调整(建小堆)
void Judgeup(Heap* hp, int size)
{
assert(hp);
int child = size - 1;
int parent = (child - 1) / 2;
while (child>0)
{
if (hp->a[child] > hp->a[parent])
{
Sweap(&hp->a[child], &hp->a[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
//插入
void HeapPush(Heap* hp, HPDataType x)
{
assert(hp);
if (hp->capacity == hp->size)
{
if (hp->capacity == 0)
{
int newcapacity = 4;
HPDataType* tem = (HPDataType*)realloc(hp->a, newcapacity * sizeof(HPDataType));
if (tem == NULL)
{
perror("realloc fail\n");
return;
}
hp->a = tem;
hp->capacity = newcapacity;
}
else
{
int newcapacity = 2 * hp->capacity;
HPDataType* tem = (HPDataType*)realloc(hp->a, newcapacity * sizeof(HPDataType));
if (tem == NULL)
{
perror("realloc fail\n");
return;
}
hp->a = tem;
hp->capacity = newcapacity;
}
}
hp->a[hp->size++] = x;
Judgeup(hp, hp->size);
}
//销毁
void HeapDestory(Heap* hp)
{
assert(hp);
free(hp->a);
hp->a = NULL;
hp->capacity = 0;
hp->size = 0;
}
//堆的判空
bool HeapEmty(Heap* hp)
{
assert(hp);
return hp->size == 0;
}
//取顶部数据
HPDataType HeapTop(Heap* hp)
{
assert(hp);
assert(!HeapEmty(hp));
return hp->a[0];
}
//堆的删除
void HeapPop(Heap* hp)
{
assert(hp);
assert(!HeapEmty(hp));
Sweap(&(hp->a[0]), &(hp->a[hp->size - 1]));
hp->size--;
JudgeDown(hp, hp->size, 0);
}
//堆的数据个数
int HeapSize(Heap* hp)
{
assert(hp);
assert(!HeapEmty(hp));
return hp->size;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"
int main()
{
Heap hp;
HeapInit(&hp);
HeapPush(&hp, 1);
HeapPush(&hp, 55);
HeapPush(&hp, 66);
HeapPush(&hp, 12);
HeapPush(&hp, 7);
for (int i = 0; i < hp.size; i++)
{
printf("%d ", hp.a[i]);
}
//printf("%d\n",hp);
HeapPop(&hp);
return 0;
}
结束