当前位置: 首页 > news >正文

常见的数据结构---队列、树与堆的深入剖析

目录

一、队列

二、树

三、堆


在现代计算机科学与工程领域,队列是三种极其重要的基础数据结构,它们各自具有独特的特点和应用。在日常开发中,合理选择和使用这些数据结构可以显著提高程序的效率和可维护性。它们不仅奠定了算法设计的理论基础,还在系统开发、数据处理和实际应用中扮演着不可或缺的角色。

一、队列

队列(Queue)是一种特殊的线性数据结构,它遵循先进先出(First In First Out, FIFO)的原则。这意味着最早进入队列的元素将是最先被移除的元素。在队列中,插入操作通常发生在队尾(rear),而删除操作则发生在队头(front)。这种特性使得队列非常适合用来模拟现实生活中的排队现象,如银行或超市收银台前的顾客排队。

1. 队列的定义与特点

定义:队列是一种特殊的线性表,只允许在一端(称为队尾)插入数据,在另一端(称为队头)删除数据。

特点:

先进先出:队列中最早加入的元素最先被移除。

单向操作:数据只能从队尾插入,从队头移除。

受限性:与栈类似,操作位置有限,不支持随机访问。

2. 队列的基本操作

  1. 入队(Enqueue)在队尾插入一个元素。

  2. 出队(Dequeue)从队头移除一个元素。

  3. 获取队头元素(Front)查看队头元素,但不移除。

  4. 检查是否为空(IsEmpty)判断队列中是否还有数据。

  5. 检查是否已满(IsFull)(对于静态实现的队列)判断队列是否达到容量限制。

3. 队列的分类

3.1 普通队列

最简单的队列形式。

操作规则

入队:在队尾插入数据。

出队:从队头移除数据。

#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100typedef struct {int data[MAX_SIZE];int front;  // 指向队头int rear;   // 指向队尾
} Queue;// 初始化队列
void initQueue(Queue *q) {q->front = 0;q->rear = 0;
}// 判断队列是否为空
int isEmpty(Queue *q) {return q->front == q->rear;
}// 判断队列是否已满
int isFull(Queue *q) {return q->rear == MAX_SIZE;
}// 入队操作
void enqueue(Queue *q, int value) {if (isFull(q)) {printf("Queue is full\n");return;}q->data[q->rear++] = value;
}// 出队操作
int dequeue(Queue *q) {if (isEmpty(q)) {printf("Queue is empty\n");return -1;}return q->data[q->front++];
}int main() {Queue q;initQueue(&q);enqueue(&q, 10);enqueue(&q, 20);printf("Dequeued: %d\n", dequeue(&q));printf("Dequeued: %d\n", dequeue(&q));return 0;
}

3.2 循环队列

为了解决普通队列中由于数据出队导致的空间浪费问题,采用环状的存储方式。

特点:通过逻辑上的首尾相连使队列能高效利用存储空间。

实现:利用取模运算控制队列的首尾相连。

#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 5typedef struct {int data[MAX_SIZE];int front;  // 指向队头int rear;   // 指向队尾
} CircularQueue;// 初始化循环队列
void initCircularQueue(CircularQueue *q) {q->front = q->rear = 0;
}// 判断循环队列是否为空
int isCircularQueueEmpty(CircularQueue *q) {return q->front == q->rear;
}// 判断循环队列是否已满
int isCircularQueueFull(CircularQueue *q) {return (q->rear + 1) % MAX_SIZE == q->front;
}// 入队操作
void circularEnqueue(CircularQueue *q, int value) {if (isCircularQueueFull(q)) {printf("Circular Queue is full\n");return;}q->data[q->rear] = value;q->rear = (q->rear + 1) % MAX_SIZE;
}// 出队操作
int circularDequeue(CircularQueue *q) {if (isCircularQueueEmpty(q)) {printf("Circular Queue is empty\n");return -1;}int value = q->data[q->front];q->front = (q->front + 1) % MAX_SIZE;return value;
}int main() {CircularQueue q;initCircularQueue(&q);circularEnqueue(&q, 10);circularEnqueue(&q, 20);printf("Dequeued: %d\n", circularDequeue(&q));printf("Dequeued: %d\n", circularDequeue(&q));return 0;
}

3.3 双端队列(Deque, Double-Ended Queue)

支持在队列的两端进行入队和出队操作。

分类

输入受限双端队列:只能从一端入队。

输出受限双端队列:只能从一端出队。

#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100typedef struct {int data[MAX_SIZE];int front;  // 指向队头int rear;   // 指向队尾
} Deque;// 初始化双端队列
void initDeque(Deque *dq) {dq->front = dq->rear = 0;
}// 判断是否为空
int isDequeEmpty(Deque *dq) {return dq->front == dq->rear;
}// 判断是否已满
int isDequeFull(Deque *dq) {return (dq->rear + 1) % MAX_SIZE == dq->front;
}// 从队头插入
void enqueueFront(Deque *dq, int value) {if (isDequeFull(dq)) {printf("Deque is full\n");return;}dq->front = (dq->front - 1 + MAX_SIZE) % MAX_SIZE;dq->data[dq->front] = value;
}// 从队尾插入
void enqueueRear(Deque *dq, int value) {if (isDequeFull(dq)) {printf("Deque is full\n");return;}dq->data[dq->rear] = value;dq->rear = (dq->rear + 1) % MAX_SIZE;
}// 从队头删除
int dequeueFront(Deque *dq) {if (isDequeEmpty(dq)) {printf("Deque is empty\n");return -1;}int value = dq->data[dq->front];dq->front = (dq->front + 1) % MAX_SIZE;return value;
}// 从队尾删除
int dequeueRear(Deque *dq) {if (isDequeEmpty(dq)) {printf("Deque is empty\n");return -1;}dq->rear = (dq->rear - 1 + MAX_SIZE) % MAX_SIZE;return dq->data[dq->rear];
}int main() {Deque dq;initDeque(&dq);enqueueRear(&dq, 10);enqueueRear(&dq, 20);enqueueFront(&dq, 5);printf("Dequeued from front: %d\n", dequeueFront(&dq));printf("Dequeued from rear: %d\n", dequeueRear(&dq));return 0;
}

3.4 优先队列(Priority Queue)

队列中的每个元素附加一个优先级,根据优先级决定元素的出队顺序。

实现方式:常用堆(Heap)结构实现。

#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100typedef struct {int data[MAX_SIZE];int size;  // 当前优先队列大小
} PriorityQueue;// 初始化优先队列
void initPriorityQueue(PriorityQueue *pq) {pq->size = 0;
}// 插入元素到优先队列
void enqueuePriorityQueue(PriorityQueue *pq, int value) {if (pq->size >= MAX_SIZE) {printf("Priority Queue is full\n");return;}pq->data[pq->size++] = value;int i = pq->size - 1;// 上浮操作while (i > 0 && pq->data[i] < pq->data[(i - 1) / 2]) {int temp = pq->data[i];pq->data[i] = pq->data[(i - 1) / 2];pq->data[(i - 1) / 2] = temp;i = (i - 1) / 2;}
}// 移除优先队列中的最小元素
int dequeuePriorityQueue(PriorityQueue *pq) {if (pq->size == 0) {printf("Priority Queue is empty\n");return -1;}int min = pq->data[0];pq->data[0] = pq->data[--pq->size];int i = 0;// 下沉操作while (2 * i + 1 < pq->size) {int smallest = 2 * i + 1;if (smallest + 1 < pq->size && pq->data[smallest + 1] < pq->data[smallest]) {smallest++;}if (pq->data[i] <= pq->data[smallest]) break;int temp = pq->data[i];pq->data[i] = pq->data[smallest];pq->data[smallest] = temp;i = smallest;}return min;
}int main() {PriorityQueue pq;initPriorityQueue(&pq);enqueuePriorityQueue(&pq, 15);enqueuePriorityQueue(&pq, 10);enqueuePriorityQueue(&pq, 5);printf("Dequeued: %d\n", dequeuePriorityQueue(&pq));printf("Dequeued: %d\n", dequeuePriorityQueue(&pq));return 0;
}

4. 队列的实现方式

4.1 顺序队列(基于数组)

优点:实现简单。

缺点:如果不使用循环队列,出队会导致存储空间浪费。数组大小固定,可能导致空间浪费或溢出。

#include <stdio.h>
#define MAX_SIZE 100typedef struct {int data[MAX_SIZE];int front;int rear;
} Queue;void initQueue(Queue *q) {q->front = q->rear = 0;
}int isEmpty(Queue *q) {return q->front == q->rear;
}int isFull(Queue *q) {return (q->rear + 1) % MAX_SIZE == q->front;
}void enqueue(Queue *q, int value) {if (isFull(q)) {printf("Queue is full\n");return;}q->data[q->rear] = value;q->rear = (q->rear + 1) % MAX_SIZE;
}int dequeue(Queue *q) {if (isEmpty(q)) {printf("Queue is empty\n");return -1;}int value = q->data[q->front];q->front = (q->front + 1) % MAX_SIZE;return value;
}int main() {Queue q;initQueue(&q);enqueue(&q, 10);enqueue(&q, 20);printf("Dequeued: %d\n", dequeue(&q));printf("Dequeued: %d\n", dequeue(&q));return 0;
}

4.2 链式队列(基于链表)

优点:动态分配存储空间,无需提前固定大小。

缺点:需要额外的指针开销。

#include <stdio.h>
#include <stdlib.h>typedef struct Node {int data;struct Node *next;
} Node;typedef struct {Node *front;Node *rear;
} Queue;void initQueue(Queue *q) {q->front = q->rear = NULL;
}int isEmpty(Queue *q) {return q->front == NULL;
}void enqueue(Queue *q, int value) {Node *newNode = (Node *)malloc(sizeof(Node));newNode->data = value;newNode->next = NULL;if (q->rear) {q->rear->next = newNode;} else {q->front = newNode;}q->rear = newNode;
}int dequeue(Queue *q) {if (isEmpty(q)) {printf("Queue is empty\n");return -1;}Node *temp = q->front;int value = temp->data;q->front = temp->next;if (q->front == NULL) {q->rear = NULL;}free(temp);return value;
}int main() {Queue q;initQueue(&q);enqueue(&q, 10);enqueue(&q, 20);printf("Dequeued: %d\n", dequeue(&q));printf("Dequeued: %d\n", dequeue(&q));return 0;
}

4.3 循环队列

原理:利用取模运算实现首尾相连。

操作公式

入队位置:(rear+1)%capacity

出队位置:(front+1)%capacity

优点:充分利用存储空间。

缺点:实现稍微复杂。

#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 5typedef struct {int data[MAX_SIZE];int front;  // 指向队头int rear;   // 指向队尾
} CircularQueue;// 初始化循环队列
void initCircularQueue(CircularQueue *q) {q->front = q->rear = 0;
}// 判断循环队列是否为空
int isCircularQueueEmpty(CircularQueue *q) {return q->front == q->rear;
}// 判断循环队列是否已满
int isCircularQueueFull(CircularQueue *q) {return (q->rear + 1) % MAX_SIZE == q->front;
}// 入队操作
void circularEnqueue(CircularQueue *q, int value) {if (isCircularQueueFull(q)) {printf("Circular Queue is full\n");return;}q->data[q->rear] = value;q->rear = (q->rear + 1) % MAX_SIZE;
}// 出队操作
int circularDequeue(CircularQueue *q) {if (isCircularQueueEmpty(q)) {printf("Circular Queue is empty\n");return -1;}int value = q->data[q->front];q->front = (q->front + 1) % MAX_SIZE;return value;
}int main() {CircularQueue q;initCircularQueue(&q);circularEnqueue(&q, 10);circularEnqueue(&q, 20);printf("Dequeued: %d\n", circularDequeue(&q));printf("Dequeued: %d\n", circularDequeue(&q));return 0;
}

5. 队列的典型应用场景

  1. 广度优先搜索(BFS):在图算法中,使用队列存储当前层次的节点。

  2. 任务调度:在操作系统中,用队列管理任务执行顺序,如打印任务、进程调度等。

  3. 数据缓冲:在网络通信中,队列用于暂存数据以待后续处理。

  4. 生产者-消费者模型:队列作为中间缓冲区,连接生产者与消费者。

  5. 消息队列:在分布式系统中,用于异步通信和任务分发。

6. 队列的优势与局限性

优势

简单易用:先进先出的操作模式清晰且自然。

操作高效:逻辑简单,易于实现和使用。插入和删除操作的时间复杂度为 O(1)O(1)O(1)。

应用广泛:从算法设计到工程实践,队列都有重要作用。

 局限性

操作受限:只能在两端进行操作,不支持随机访问。

空间利用问题:普通队列在频繁出队时可能造成空间浪费。

普通队列效率低:出队后空间不可复用。

7、小结

队列作为一种基础数据结构,因其先进先出的特性,在数据的有序处理、任务调度、广度搜索等场景中发挥了重要作用。
通过对队列的分类、操作及实现方式的深入理解,开发者能够灵活运用队列解决各种复杂问题,并优化程序性能。

顺序队列适用于固定大小的简单队列场景,但会导致存储空间浪费。链式队列通过动态分配解决空间问题,适合大小不确定的队列,但需额外的内存指针。循环队列通逻辑上的首尾相连实现存储空间的高效利用,是一种改进的顺序队列方式。

二、树

树是一种 非线性数据结构,以分层的方式存储数据。它由一个根节点(Root)和多个子节点(Child)组成,每个节点可能有多个子节点,但只有一个父节点(Parent)。树特别适用于分层组织和快速查找的场景。

1. 树的定义与特点

定义

树是由节点组成的结构,具有以下特点:

树中只有一个根节点。除根节点外的每个节点有且仅有一个父节点。节点之间通过边连接形成一个无环的层次结构。

树的特点

递归定义:树是一个根节点和若干子树的集合,每棵子树本质上也是一棵树。

无环性:从根节点到任意节点仅有一条路径。

层次结构:树具有明显的分层结构,数据之间存在父子关系。

节点关系父节点:直接指向当前节点的节点。子节点:被当前节点指向的节点。兄弟节点:具有同一个父节点的节点。叶子节点:没有子节点的节点。根节点:没有父节点的节点。

2. 树的基本概念

根节点(Root):没有父节点的节点。

父节点(Parent):直接指向其他节点的节点。

子节点(Child):被某个节点直接指向的节点。

叶子节点(Leaf):没有子节点的节点。

度(Degree):节点的子节点个数称为该节点的度,树的最大度称为树的度。

层次(Level):树中节点的层次从根节点开始,根节点为第 1 层,其子节点为第 2 层,以此类推。

高度(Height):节点高度:从当前节点到叶子节点的最长路径上的边数。树高度:根节点的高度。

深度(Depth):节点的深度是从根节点到当前节点的路径长度。

路径(Path):从一个节点到另一个节点经过的所有节点和边。

森林(Forest):由多棵互不相交的树组成的集合。

3. 树的分类

按节点连接关系分类

普通树:节点可以有任意数量的子节点。

// 树的节点定义
typedef struct TreeNode {int value;struct TreeNode **children; // 子节点指针数组int childCount;             // 子节点数
} TreeNode;

二叉树:每个节点最多有两个子节点,分别是左子节点和右子节点。
特殊的二叉树

// 定义二叉树节点
typedef struct TreeNode {int value;struct TreeNode* left;struct TreeNode* right;
} TreeNode;

搜索二叉树(BST):左子树的所有节点小于根节点,右子树的所有节点大于根节点。

// 插入
TreeNode* insertBST(TreeNode* root, int value) {if (!root) return createTreeNode(value);if (value < root->value)root->left = insertBST(root->left, value);else if (value > root->value)root->right = insertBST(root->right, value);return root;
}// 搜索
TreeNode* searchBST(TreeNode* root, int value) {if (!root || root->value == value) return root;if (value < root->value)return searchBST(root->left, value);return searchBST(root->right, value);
}

平衡二叉树:左右子树高度差不超过 1。
满二叉树:所有层的节点数都达到最大值。
完全二叉树:所有层均满,最后一层的节点从左到右连续排列。
多叉树:每个节点可以有多个子节点,例如 N 叉树。
Trie 树:一种特殊的多叉树,用于快速存储和检索字符串。
红黑树、AVL树:自平衡二叉搜索树,用于保证操作的效率。

#include <stdio.h>
#include <stdlib.h>// AVL 树节点定义
typedef struct AVLNode {int value;int height;struct AVLNode* left;struct AVLNode* right;
} AVLNode;// 获取节点高度
int getHeight(AVLNode* node) {return node ? node->height : 0;
}// 计算平衡因子
int getBalanceFactor(AVLNode* node) {return getHeight(node->left) - getHeight(node->right);
}// 创建新节点
AVLNode* createAVLNode(int value) {AVLNode* node = (AVLNode*)malloc(sizeof(AVLNode));node->value = value;node->height = 1;node->left = NULL;node->right = NULL;return node;
}// 右旋
AVLNode* rightRotate(AVLNode* y) {AVLNode* x = y->left;AVLNode* T = x->right;x->right = y;y->left = T;y->height = 1 + (getHeight(y->left) > getHeight(y->right) ? getHeight(y->left) : getHeight(y->right));x->height = 1 + (getHeight(x->left) > getHeight(x->right) ? getHeight(x->left) : getHeight(x->right));return x;
}// 左旋
AVLNode* leftRotate(AVLNode* x) {AVLNode* y = x->right;AVLNode* T = y->left;y->left = x;x->right = T;x->height = 1 + (getHeight(x->left) > getHeight(x->right) ? getHeight(x->left) : getHeight(x->right));y->height = 1 + (getHeight(y->left) > getHeight(y->right) ? getHeight(y->left) : getHeight(y->right));return y;
}// 插入节点
AVLNode* insertAVL(AVLNode* root, int value) {if (!root) return createAVLNode(value);if (value < root->value)root->left = insertAVL(root->left, value);else if (value > root->value)root->right = insertAVL(root->right, value);elsereturn root;root->height = 1 + (getHeight(root->left) > getHeight(root->right) ? getHeight(root->left) : getHeight(root->right));int balance = getBalanceFactor(root);// 左左if (balance > 1 && value < root->left->value)return rightRotate(root);// 右右if (balance < -1 && value > root->right->value)return leftRotate(root);// 左右if (balance > 1 && value > root->left->value) {root->left = leftRotate(root->left);return rightRotate(root);}// 右左if (balance < -1 && value < root->right->value) {root->right = rightRotate(root->right);return leftRotate(root);}return root;
}

3.2 按数据组织方式分类

堆(Heap):完全二叉树,满足堆序性质。

并查集树:用于解决连通性问题,支持合并和查找操作。

4. 树的基本操作

遍历:深度优先遍历(DFS):前序遍历:根 → 左 → 右。中序遍历:左 → 根 → 右。后序遍历:左 → 右 → 根。广度优先遍历(BFS):层次遍历:按层次从上到下,从左到右依次访问节点。

插入:根据树的性质插入节点,例如在二叉搜索树中插入时需保持排序规则。

删除:移除指定节点后需调整树结构以保持性质,例如删除二叉搜索树节点时需替换为合适的继承节点。

查找:按树的规则查找特定节点,如二叉搜索树的查找效率为 O(log⁡n)O(\log n)O(logn)。

高度计算:递归或迭代计算节点的高度。

5. 树的存储方式

链式存储

每个节点通过指针指向其子节点和父节点。常见于动态树结构,如链表表示的二叉树。

typedef struct TreeNode {int value;struct TreeNode *left;struct TreeNode *right;
} TreeNode;

顺序存储:使用数组存储完全二叉树。左子节点的索引为 2i+1,右子节点的索引为 2i+2。

#include <stdio.h>
#include <stdlib.h>#define MAX_SIZE 100 // 最大节点数typedef struct CompleteBinaryTree {int data[MAX_SIZE]; // 数组存储完全二叉树的节点值int size;           // 当前节点数
} CompleteBinaryTree;// 初始化完全二叉树
void initTree(CompleteBinaryTree* tree) {tree->size = 0;
}// 插入节点
void insertNode(CompleteBinaryTree* tree, int value) {if (tree->size >= MAX_SIZE) {printf("Tree is full. Cannot insert more nodes.\n");return;}tree->data[tree->size] = value;tree->size++;
}// 获取左子节点索引
int getLeftChildIndex(int index) {return 2 * index + 1;
}// 获取右子节点索引
int getRightChildIndex(int index) {return 2 * index + 2;
}// 打印树的层序遍历
void printTree(CompleteBinaryTree* tree) {printf("Tree: ");for (int i = 0; i < tree->size; i++) {printf("%d ", tree->data[i]);}printf("\n");
}// 打印节点的左右子节点
void printChildren(CompleteBinaryTree* tree, int index) {if (index >= tree->size) {printf("Invalid index\n");return;}printf("Node %d: ", tree->data[index]);int leftIndex = getLeftChildIndex(index);int rightIndex = getRightChildIndex(index);if (leftIndex < tree->size)printf("Left Child = %d ", tree->data[leftIndex]);elseprintf("Left Child = NULL ");if (rightIndex < tree->size)printf("Right Child = %d ", tree->data[rightIndex]);elseprintf("Right Child = NULL ");printf("\n");
}int main() {CompleteBinaryTree tree;initTree(&tree);// 插入节点insertNode(&tree, 1);insertNode(&tree, 2);insertNode(&tree, 3);insertNode(&tree, 4);insertNode(&tree, 5);insertNode(&tree, 6);// 打印树printTree(&tree);// 打印某些节点的子节点printChildren(&tree, 0); // 根节点printChildren(&tree, 1); // 第二层左节点printChildren(&tree, 2); // 第二层右节点return 0;
}

6. 树的应用场景

  1. 二叉树:二叉搜索树(BST):高效查找、插入、删除。堆:优先队列的实现。
  2. Trie 树:快速字符串匹配、前缀查询。
  3. 红黑树、AVL 树:数据库索引、动态集合操作。
  4. 决策树:机器学习中的分类和回归算法。
  5. 表达式树:表达式求值。
  6. 文件系统:文件和目录的层次管理。

7. 树的优势与局限性

优势
  1. 自然表示分层结构,适合数据的分级存储。
  2. 支持高效的查找、插入和删除操作。
  3. 可扩展性强,灵活适应不同场景。
局限性
  1. 链式存储需要额外的指针开销。
  2. 树结构的实现较为复杂,维护特性(如平衡)需付出额外代价。
  3. 不适合随机访问,性能劣于数组。

8. 小结

树作为一种基础数据结构,在理论研究和实际工程中都具有重要地位,广泛应用于文件系统、数据库索引、路由表等场景。通过不同种类的树(如二叉树、AVL树、红黑树等),我们可以根据需求选择适合的结构来优化数据操作效率。掌握树的基本知识和实现方法,是深入学习算法与数据结构的重要一步。

二叉树及其变种:解决高效数据存储与操作问题。Trie 树:适合字符串处理场景。红黑树、AVL 树:优化动态数据的平衡性。掌握树的基本原理、特性及其适用场景,是深入理解算法与系统设计的关键。

三、堆

1、堆的定义与特性

堆(Heap)是一种特殊的基于树的抽象数据类型(特殊的完全二叉树),堆通常用于实现优先队列,在这种结构中,最高优先级的元素总是位于根节点。

堆满足以下特性:对于任意给定的节点i,如果Pi的父节点,那么P的键值(或元素)要么大于等于(最大堆, max-heap),要么小于等于(最小堆, min-heap)i的键值。

大顶堆(Max Heap):任意节点的值都不小于其子节点的值,堆顶为最大值。
小顶堆(Min Heap):任意节点的值都不大于其子节点的值,堆顶为最小值。

完全二叉树 (Complete Binary Tree): 堆通常以完全二叉树的形式实现,这意味着除了最底层外,所有层都是满的,并且最底层的节点尽可能靠左排列。

存储结构:通常使用数组来存储堆,逻辑上的父子节点关系可以通过索引计算:
父节点索引:i
左子节点索引:2i + 1
右子节点索引:2i + 2
若需要访问父节点:(i - 1) / 2

2、堆的基本操作

  1. 插入(Insert):在堆尾添加新元素。将新元素“上浮”(Bubble Up),以保持堆的性质。

  2. 删除堆顶元素(Remove Top/Pop):将堆顶元素替换为最后一个元素。删除最后一个元素。将新堆顶“下沉”(Bubble Down),以保持堆的性质。

  3. 堆化(Heapify):将无序数组调整为堆结构。从最后一个非叶节点开始,对每个节点执行下沉操作。

  4. 堆排序(Heap Sort):利用堆性质进行排序。大顶堆适用于升序排序,小顶堆适用于降序排序。

  5. 获取最大/最小元素 (Get-Max/Get-Min): 对于最大堆或最小堆,根节点就是具有最大或最小键值的元素。

3、应用场景

优先队列 (Priority Queue): 堆是最常用的优先队列实现方式之一。在优先队列中,每次取出的都是具有最高优先级的元素。

堆排序 (HeapSort): 一种高效的排序算法,时间复杂度为O(n log n)。

选择问题 (Selection Problem): 如寻找数组中的第k大或第k小元素。

图算法: 例如Dijkstra算法和Prim算法,其中需要频繁访问或更新最小权重的边。

内存管理: 某些操作系统使用堆来管理动态内存分配。

4、代码实现

1. 最大堆(大顶堆)
#include <stdio.h>
#include <stdlib.h>#define MAX_SIZE 100typedef struct {int data[MAX_SIZE];int size;
} MaxHeap;// 初始化堆
void initHeap(MaxHeap* heap) {heap->size = 0;
}// 上浮操作
void bubbleUp(MaxHeap* heap, int index) {while (index > 0) {int parent = (index - 1) / 2;if (heap->data[index] > heap->data[parent]) {// 交换int temp = heap->data[index];heap->data[index] = heap->data[parent];heap->data[parent] = temp;index = parent;} else {break;}}
}// 插入元素
void insert(MaxHeap* heap, int value) {if (heap->size >= MAX_SIZE) {printf("Heap is full!\n");return;}heap->data[heap->size] = value;bubbleUp(heap, heap->size);heap->size++;
}// 下沉操作
void bubbleDown(MaxHeap* heap, int index) {while (index * 2 + 1 < heap->size) {int leftChild = index * 2 + 1;int rightChild = index * 2 + 2;int largerChild = leftChild;if (rightChild < heap->size && heap->data[rightChild] > heap->data[leftChild]) {largerChild = rightChild;}if (heap->data[index] < heap->data[largerChild]) {// 交换int temp = heap->data[index];heap->data[index] = heap->data[largerChild];heap->data[largerChild] = temp;index = largerChild;} else {break;}}
}// 删除堆顶元素
int removeTop(MaxHeap* heap) {if (heap->size == 0) {printf("Heap is empty!\n");return -1;}int top = heap->data[0];heap->data[0] = heap->data[--heap->size];bubbleDown(heap, 0);return top;
}// 打印堆
void printHeap(MaxHeap* heap) {for (int i = 0; i < heap->size; i++) {printf("%d ", heap->data[i]);}printf("\n");
}int main() {MaxHeap heap;initHeap(&heap);insert(&heap, 10);insert(&heap, 20);insert(&heap, 15);insert(&heap, 30);insert(&heap, 25);printf("Heap elements: ");printHeap(&heap);printf("Removed top: %d\n", removeTop(&heap));printf("Heap after removal: ");printHeap(&heap);return 0;
}

2. 堆排序

void heapSort(int arr[], int n) {// 构建大顶堆for (int i = n / 2 - 1; i >= 0; i--) {bubbleDown(arr, n, i);}// 逐步将堆顶移到数组末尾,缩小堆的范围for (int i = n - 1; i > 0; i--) {int temp = arr[0];arr[0] = arr[i];arr[i] = temp;bubbleDown(arr, i, 0); // 对新的堆顶进行下沉}
}

5、小结

堆是一种功能强大的数据结构,适用于优先级处理和高效排序。它利用了完全二叉树的特性,结合数组实现,简化了存储和计算,同时保持了良好的时间复杂度,是许多算法和系统开发中的核心工具。

总结来说,队列、树和堆都是非常重要的基础数据结构,各自在不同的场景中发挥着不可替代的作用。队列适合处理按顺序排队的任务,树则为数据的组织和快速检索提供了有效的解决方案,而堆则常用于需要优先级排序的场合。通过深入理解它们的实现与应用,开发者可以根据实际需求选择最适合的结构,提高程序的性能与可靠性。掌握这些数据结构,不仅能提升代码的效率,还能为进一步学习复杂算法打下坚实的基础。

相关文章:

常见的数据结构---队列、树与堆的深入剖析

目录 一、队列 二、树 三、堆 在现代计算机科学与工程领域&#xff0c;队列、树和堆是三种极其重要的基础数据结构&#xff0c;它们各自具有独特的特点和应用。在日常开发中&#xff0c;合理选择和使用这些数据结构可以显著提高程序的效率和可维护性。它们不仅奠定了算法设计…...

leetcode--螺旋矩阵

LCR 146.螺旋遍历二维数组 给定一个二维数组 array&#xff0c;请返回「螺旋遍历」该数组的结果。 螺旋遍历&#xff1a;从左上角开始&#xff0c;按照 向右、向下、向左、向上 的顺序 依次 提取元素&#xff0c;然后再进入内部一层重复相同的步骤&#xff0c;直到提取完所有元…...

JavaScript(JS)的对象

目录 1.array 数组对象 2.String 字符串对象 3.JSON 对象&#xff08;数据载体&#xff0c;进行数据传输&#xff09; 4.BOM 浏览器对象 5.DOM 文档对象&#xff08;了解&#xff09; 1.array 数组对象 定义方式1&#xff1a;var 变量名 new Array(元素列表); 定义方式…...

基于BM1684的AI边缘服务器-模型转换,大模型一体机

介绍 我们属于SoC模式&#xff0c;即我们在x86主机上基于tpu-nntc和libsophon完成模型的编译量化与程序的交叉编译&#xff0c;部署时将编译好的程序拷贝至SoC平台&#xff08;1684开发板/SE微服务器/SM模组&#xff09;中执行。 注&#xff1a;以下都是在Ubuntu20.04系统上操…...

git推送多个仓库

在 Git 中&#xff0c;可以通过添加多个远程仓库来实现一次 git push 推送到多个仓库&#xff0c;比如同时推送到 Gitee 和 GitHub。以下是详细的设置步骤&#xff1a; 1. 添加多个远程仓库 假设你的项目已经有一个远程仓库&#xff08;例如 GitHub&#xff09;&#xff0c;你…...

Matlab mex- setup报错—错误使用 mex,未检测到支持的编译器...

错误日志&#xff1a; 在使用mex编译时报错提示&#xff1a;错误使用 mex&#xff0c;未检测到支持的编译器。您可以安装免费提供的 MinGW-w64 C/C 编译器&#xff1b;请参阅安装 MinGW-w64 编译器。有关更多选项&#xff0c;请访问https://www.mathworks.com/support/compile…...

PostgreSQL认证培训需要什么条件

PostgreSQL认证培训通常没有严格的前置条件&#xff0c;但以下几点可以帮助你更好地准备和通过认证考试&#xff1a; 1、基础知识&#xff1a;具备基本的数据库知识和经验&#xff0c;特别是对SQL有一定的了解。如果你Oracle、MySQL等基础知识&#xff0c;对对你学习PostgreSQ…...

Oracle—系统包使用

文章目录 系统包dbms_redefinition 系统包 dbms_redefinition 功能介绍&#xff1a;该包体可以实现将Oracle库下的表在线改为分区结构或者重新定义&#xff1b; 说明&#xff1a;在检查表是否可以重定义和开始重定义的过程中&#xff0c;按照表是否存在主键&#xff0c;参数 o…...

【排序用法】.NET开源 ORM 框架 SqlSugar 系列

&#x1f4a5; .NET开源 ORM 框架 SqlSugar 系列 &#x1f389;&#x1f389;&#x1f389; 【开篇】.NET开源 ORM 框架 SqlSugar 系列【入门必看】.NET开源 ORM 框架 SqlSugar 系列【实体配置】.NET开源 ORM 框架 SqlSugar 系列【Db First】.NET开源 ORM 框架 SqlSugar 系列…...

【SpringBoot】整合篇

1、log4j2 第一步&#xff0c;导入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions><!-- 去掉springboot默认配置 --> <exclusion> <…...

写入json和读取json文件

/// <summary> ///写入文件 /// </summary> /// <param name"Stns"></param> /// <returns></returns> public ActionResult WriteJsonFile(string Stns) { strin…...

Vuex的理解及使用场景

Vuex 是 Vue.js 应用中一个专门为状态管理而设计的库&#xff0c;它基于 Fluts 和 Redux 的模式。Vuex 提供了一种集中式存储管理所有组件的状态&#xff0c;并以相应的规则保证状态以一种可预测的方式发生变化。以下是 Vuex 的理解及使用场景&#xff1a; Vuex 的理解 核心概…...

PostGis学习笔记

– 文本方式查看几何数据 SELECT ST_AsText(geom)FROM nyc_streets WHERE name ‘Avenue O’; – 计算紧邻的街区 SELECT name,ST_GeometryType(geom) FROM nyc_streets WHERE ST_DWithin( geom,ST_GeomFromText(‘LINESTRING(586782 4504202,586864 4504216)’,26918),0.1); …...

Qt 窗口类型、窗口标志和窗口属性

一、窗口类型 Qt 窗口标志枚举类型用于指定小部件的各种窗口系统属性。其中一些标志取决于底层窗口管理器是否支持它们。以下是窗口类型: Qt::QWidget:这是 QWidget 的默认类型。如果它们有父级,这种类型的部件是子部件,如果没有父控件,则为独立窗口。Qt::Window:通常具…...

相机学习笔记——工业相机的基本参数

0、相机分类 图像颜色不同可以分为黑白相机和彩色相机&#xff1a;相同分辨率下&#xff0c;黑白工业相机相比彩色工业相机精度更高&#xff0c;检测图像边缘时&#xff0c;黑白工业相机成像效果更好。 芯片类型不同可以分为CCD相机和CMOS相机&#xff1a;CCD工业相机具有体积小…...

MATLAB - ROS2 ros2genmsg 生成自定义消息(msg/srv...)

系列文章目录 前言 语法 ros2genmsg(folderpath)ros2genmsg(folderpath,Name=Value) 一、说明 ros2genmsg(folderpath) 通过读取指定文件夹路径下的 ROS 2 自定义信息和服务定义来生成 ROS 2 自定义信息。函数文件夹必须包含一个或多个 ROS 2 软件包。这些软件包包含 .msg 文…...

【Git 操作】-- 将 fork master 分支的最新commit更新到自己的仓库

目录 1.举例 2. 配置上游仓库&#xff08;Upstream&#xff09; 3. 获取上游仓库的更新 4. 切换到你自己的 master 分支 5. 合并上游仓库的 master 分支 6. 解决冲突&#xff08;如果有的话&#xff09; 7. 推送更新到你自己的 GitHub 仓库 1.举例 当我们从 github 的 h…...

[高等数学学习记录] 泰勒公式

1 知识点 1.1 要求 为简化计算, 通常用多项式近似表达复杂函数: 设函数 f ( x ) f(x) f(x) 在含有 x 0 x_0 x0​ 的开区间内具有 ( n 1 ) (n1) (n1) 阶导数, 试找出一个关于 ( x − x 0 ) (x-x_0) (x−x0​) 的 n n n 次多项式 p n ( x ) p_n(x) pn​(x) 近似表达 f…...

我的创作纪念日—128天的坚持|分享|成长

&#x1f4ab;《博主介绍》&#xff1a;✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ &#x1f4ab;《擅长领域》&#xff1a;✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux&#xff0c;也在扩展大数据方向的知识面✌️…...

万字长文解读深度学习——多模态模型BLIP2

&#x1f33a;历史文章列表&#x1f33a; 深度学习——优化算法、激活函数、归一化、正则化 深度学习——权重初始化、评估指标、梯度消失和梯度爆炸 深度学习——前向传播与反向传播、神经网络&#xff08;前馈神经网络与反馈神经网络&#xff09;、常见算法概要汇总 万字长…...

selinux与防火墙

selinux 什么是selinux SELinux 是 Security-Enhanced Linux 的缩写&#xff0c;意思是安全强化的 linux 。 SELinux 主要由美国国家安全局&#xff08; NSA &#xff09;开发&#xff0c;当初开发的目的是为了避免资源的误用。 系统资源都是通过程序进行访问的&#xff0…...

java基础概念47-ArrayList、LinkList和迭代器

一、ArrayList集合 1-1、ArrayList的两种添加信息的方式 1-2、ArrayList集合底层逻辑 1、利用空参创建的集合&#xff0c;在底层创建一个默认长度为0的数组 2、添加第一个元素时&#xff0c;底层会创建一个新的长度为10的数组 3、存满时&#xff0c;会扩容1.5倍。 4、如果…...

Delphi 12.2.1 idhttpserver的使用方法

Delphi 12.2.1 idhttpserver的使用方法 1&#xff09;CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);事件 该事件和IDTCPSERVER的EXECUTE()事件一样&#xff0c;都是“线程方法”&#xff0c;即事件是在子线程里…...

【golang】单元测试,以及出现undefined时的解决方案

单元测试 要对某一方法进行测试时&#xff0c;例如如下这一简单减法函数&#xff0c;选中函数名后右键->转到->测试 1&#xff09;Empty test file 就是一个空文件&#xff0c;我们可以自己写测试的逻辑 但是直接点绿色箭头运行会出问题&#xff1a; 找不到包。我们要在…...

jmeter 压测常用静默参数解释应用

简介&#xff1a; JMeter静默压测&#xff08;即无界面压测&#xff09;是一种常用的性能测试方法&#xff0c;用于模拟多个用户同时访问系统并测量系统的响应时间和吞吐量等关键性能指标。在JMeter静默压测中&#xff0c;常用的压测参数及其解释如下&#xff1a; 一、基本…...

【开源】A059-基于SpringBoot的社区养老服务系统的设计与实现

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看项目链接获取⬇️&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600个选题ex…...

《智能体雏形开发(高阶实操)》开发计划概述

智能体雏形开发计划 通过本计划,逐步完成一个可以真实运行的智能体雏形。 最终完成一个**“用户日志文件生成日报,日报再进一步汇总成周报”**的任务驱动型智能体雏形 第一阶段:基础准备与环境搭建 1. 学习基础知识 了解智能体的概念、类型和技术框架。学习大模型(如阿里…...

QT学习笔记-QStringList,QTimer

QStringList-存储和管理一系列的字符串 在Qt框架中&#xff0c;QStringList 是一个模板类 QList<QString> 的特化&#xff0c;专门用于处理 QString 对象&#xff08;即Qt中的字符串&#xff09;的列表。当你看到这样的声明&#xff1a; QStringList m_rec_topicList; …...

如何使用brew安装phpredis扩展?

如何使用brew安装phpredis扩展&#xff1f; phpredis扩展是一个用于PHP语言的Redis客户端扩展&#xff0c;它提供了一组PHP函数&#xff0c;用于与Redis服务器进行交互。 1、cd到php某一版本的bin下 /usr/local/opt/php8.1/bin 2、下载 phpredis git clone https://githu…...

游戏引擎学习第25天

Git: https://gitee.com/mrxiao_com/2d_game 今天的计划 总结和复述&#xff1a; 这段时间的工作已经接近尾声&#xff0c;虽然每次编程的时间只有一个小时&#xff0c;但每一天的进展都带来不少收获。尽管看起来似乎花费了很多时间&#xff0c;实际上这些日积月累的时间并未…...