堆排序——C语言

概念:

堆是一种完全二叉树的数据结构。堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。

性质

大顶堆:每个结点的值都大于或等于该结点的左右孩子的结点值;

小顶堆:每个结点的值都小于或等于该结点的左右孩子的结点值。

 将给给定无序列构造成一个大顶堆;

1、从最后一个非叶子结点开始调整,也就是下面的7;判断是否满足大顶堆的性质,满足则跳过,不满足则调整。从上往下,从左往右调整。

2、找到倒数第二非叶子结点,判断是否满足大顶堆性质,满足则跳过,很显然倒数第二个非叶子结点(也就是下面的22)满足则跳过,同理倒数第三过也是如此。判断下一个叶子结点8,不满足大顶堆性质,则需要和其左孩子结点进行交换。

3、第一遍调整完后,再回到最后一个非叶子结点进行判断,直到所有非叶子结点都满足大顶堆的性质才结束;

 

 将给给定无序列构造成一个小顶堆;

将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素比较交换,得到第二大元素。如此反复进行交换、重建、交换。

1、从最后一个非叶子结点判断是否满足小顶堆性质,满足则只需要其叶子结点与堆顶元素进行比较交换,不需要调整;不满足需要调整;

2、倒数第二个非叶子结点不满足小顶堆性质,但因顶堆元素比它大则不调整;将顶堆元素末尾元素进行比较,大则交换,小则于其上一个元素比较,同理。这里17于3进行交换;

 3、因倒数第二个非叶子结点不满足小顶堆性质,所有将3和16进行交换后顶堆元素再进行比较交换;

4、顶堆结点不满足小顶堆性质,所有将3和7进行交换; 

 

5、 最终实现小顶堆排序

 

 heapsort.c

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 
  4 void swap(int *a, int *b)
  5 {
  6     int temp = *b;
  7     *b = *a;
  8     *a = temp;
  9 }
 10 
 11 void max_heapify(int arr[], int start, int end)
 12 {
 13 
 14     // 建立父节点指向和子节点指向
 15     int dad = start;
 16     int son = dad * 2 + 1;
 17     while (son <= end)
 18     {
 19             //若子节点指向在范围内才做比较
 20         if (son + 1 <= end && arr[son] < arr[son + 1])

 21            //先比较两个子结点大小,选择最大的
 22             son++;
 23         if (arr[dad] > arr[son])
 24             //如果父节点大于子结点代表调整完后,直接跳出函数
 25                 return;
 26         else
 27         {
 28                 //否则交换父子内容在继续子结点和孙节点比较
 29             swap(&arr[dad], &arr[son]);
 30             dad = son;
 31             son = dad * 2 + 1;
 32         }
 33     }
 34 }
 35 
 36 void heap_sort(int arr[], int len)
 37 {
 38     int i;
 39    //初始化,i从最后一个父节点开始调整
 40     for (i = len / 2 - 1; i >= 0; i--)
 41         max_heapify(arr, i, len - 1);
 42    //先将第一个元素和已安排好元素前一位做交换,再重新调整,直到排序>    完整
 43     for (i = len - 1; i > 0; i--)
 44     {
 45         swap(&arr[0], &arr[i]);
 46         max_heapify(arr, 0, i - 1);
 47     }
 48 }
 49 
 50 int main()
 51 {
 52     int arr[] = { 90,70,80,60,10,40,20,50,30};
 53     int len = (int) sizeof(arr) / sizeof(*arr);
 54     heap_sort(arr, len);
 55     int i;
 56     for (i = 0; i < len; i++)
 57         printf("%d ", arr[i]);
 58     printf("\n");
 59     return 0;
 60 }
 

 

 

<think>好的,我现在需要处理用户的问题:“改写成数据结构用——C语言描述的”。首先,我需要理解用户的具体需求。用户可能已经有一份关于数据结构的内容,但它是用另一种语言或伪代码描述的,现在希望将其转换为C语言的具体实现。或者,用户可能希望了解如何将数据结构的理论概念用C语言代码来表达。 接下来,我需要考虑数据结构的基本组成部分,比如线性结构(数组、链表、栈、队列)、树形结构(二叉树、堆、AVL树)、图结构以及相关的算法(排序、查找、遍历)。用户可能需要具体的代码示例,或者更偏向于方法论,即如何将抽象的数据结构转化为C语言实现。 然后,我应该检查用户提供的系统级指令,确保回答符合格式要求。所有行内数学表达式需要用$...$,独立公式用$$...$$,并且使用中文回答。同时,回答需要结构清晰,逐步引导用户解决问题,并确保内容准确可靠。 用户可能对C语言的语法和数据结构的具体实现不太熟悉,因此需要详细解释每个步骤,包括结构体定义、指针操作、内存管理等关键点。例如,链表节点的结构体如何定义,如何实现插入、删除操作,栈的数组实现和链表实现的区别等。 另外,需要注意C语言中没有类的概念,因此数据结构的操作通常通过函数来实现,这些函数接受结构体指针作为参数。例如,链表的插入操作可能需要一个函数接收头节点指针、插入位置和值。 还需要考虑错误处理和边界条件,比如内存分配失败时的处理,避免内存泄漏等问题。这可能涉及到动态内存分配函数如malloc和free的使用,以及如何正确释放数据结构占用的内存。 可能还需要比较不同实现方式的优缺点,比如数组实现的栈与链表实现的栈在性能、灵活性上的差异,帮助用户根据实际需求选择合适的实现方式。 最后,总结关键点,确保用户能够理解如何将数据结构的理论知识转化为具体的C语言代码,并可能提供进一步的资源或示例代码供用户参考。</think>将数据结构用C语言描述需要将抽象的数据结构概念转化为具体的C语言语法实现。以下是关键步骤和示例: $$数据结构 = 逻辑结构 + 存储结构 + 运算实现$$ 一、基础元素转换原则 1. 结点结构体化:使用struct定义数据结构节点 ```c typedef struct Node { // 链表结点 int data; struct Node* next; } Node; ``` 2. 运算函数化:通过独立函数实现数据结构操作 ```c void insert(Node** head, int data) { Node* newNode = (Node*)malloc(sizeof(Node)); newNode->data = data; newNode->next = *head; *head = newNode; } ``` 二、典型数据结构实现示例 1. 线性表(链表实现) ```c typedef struct List { Node* head; int length; } List; void initList(List* list) { list->head = NULL; list->length = 0; } ``` 2. 栈(数组实现) ```c #define MAX_SIZE 100 typedef struct Stack { int data[MAX_SIZE]; int top; } Stack; void push(Stack* s, int value) { if(s->top < MAX_SIZE-1) { s->data[++s->top] = value; } } ``` 3. 二叉树 ```c typedef struct TreeNode { int data; struct TreeNode* left; struct TreeNode* right; } TreeNode; TreeNode* createNode(int value) { TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode)); node->data = value; node->left = node->right = NULL; return node; } ``` 三、实现注意事项 1. 内存管理:使用malloc/free动态管理内存 2. 指针操作:注意指针的间接引用(->操作符) 3. 参数传递:修改结构体内容时需传递指针 4. 时间复杂度:保持操作的时间复杂度$O(1)$或$O(n)$等理论特性 四、抽象数据类型(ADT)映射 将ADT的三个要素对应实现: 1. 数据对象:通过结构体定义 2. 数据关系:使用指针或数组索引建立 3. 基本操作:用函数封装实现 建议开发流程: 1. 定义数据结构体 2. 实现初始化函数 3. 编写核心操作函数 4. 添加销毁/释放函数 5. 编写测试用例验证 示例完整栈实现: ```c #include <stdio.h> #include <stdlib.h> typedef struct { int* items; int top; int capacity; } Stack; Stack* createStack(int capacity) { Stack* s = (Stack*)malloc(sizeof(Stack)); s->capacity = capacity; s->top = -1; s->items = (int*)malloc(capacity * sizeof(int)); return s; } int isFull(Stack* s) { return s->top == s->capacity - 1; } void push(Stack* s, int item) { if (!isFull(s)) { s->items[++s->top] = item; } } int pop(Stack* s) { if (s->top >= 0) { return s->items[s->top--]; } return -1; // 错误值 } ``` 这种实现方式完整呈现了栈的后进先出(LIFO)特性,通过结构体封装状态,函数操作保证数据完整性。实际开发中还需考虑动态扩容、错误处理等细节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值