C语言实现基于最大堆和最小堆的堆排序算法示例_C 语言

堆定义
堆实际上是一棵完全二叉树,其任何一非叶节点满足性质:
Key[i]<=key[2i+1]&&Key[i]<=key[2i+2](小顶堆)或者:Key[i]>=Key[2i+1]&&key>=key[2i+2](大顶堆)
即任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。

堆排序的思想
利用大顶堆(小顶堆)堆顶记录的是最大关键字(最小关键字)这一特性,使得每次从无序中选择最大记录(最小记录)变得简单。

  • 最大堆:所有节点的子节点比其自身小的堆。
  • 最小堆:所有节点的子节点比其自身大的堆。

这里以最大堆为基础,其基本思想为:

1.将初始待排序关键字序列(R1,R2....Rn)构建成大顶堆,此堆为初始的无序区;
2.将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,......Rn-1)和新的有序区(Rn),且满足R[1,2...n-1]<=R[n];
3.由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,......Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2....Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。

C语言实现
1.基于最大堆实现升序排序

// 初始化堆
void initHeap(int a[], int len) {
 // 从完全二叉树最后一个非子节点开始
 // 在数组中第一个元素的索引是0
 // 第n个元素的左孩子为2n+1,右孩子为2n+2,
 // 最后一个非子节点位置在(n - 1) / 2
 for (int i = (len - 1) / 2; i >= 0; --i) {
  adjustMaxHeap(a, len, i);
 }
}

void adjustMaxHeap(int a[], int len, int parentNodeIndex) {
 // 若只有一个元素,那么只能是堆顶元素,也没有必要再排序了
 if (len <= 1) {
  return;
 }

 // 记录比父节点大的左孩子或者右孩子的索引
 int targetIndex = -1;

 // 获取左、右孩子的索引
 int leftChildIndex = 2 * parentNodeIndex + 1;
 int rightChildIndex = 2 * parentNodeIndex + 2;

 // 没有左孩子
 if (leftChildIndex >= len) {
  return;
 }

 // 有左孩子,但是没有右孩子
 if (rightChildIndex >= len) {
  targetIndex = leftChildIndex;
 }
 // 有左孩子和右孩子
 else {
  // 取左、右孩子两者中最大的一个
  targetIndex = a[leftChildIndex] > a[rightChildIndex] ? leftChildIndex : rightChildIndex;
 }

 // 只有孩子比父节点的值还要大,才需要交换
 if (a[targetIndex] > a[parentNodeIndex]) {
  int temp = a[targetIndex];

  a[targetIndex] = a[parentNodeIndex];
  a[parentNodeIndex] = temp;

  // 交换完成后,有可能会导致a[targetIndex]结点所形成的子树不满足堆的条件,
  // 若不满足堆的条件,则调整之使之也成为堆
  adjustMaxHeap(a, len, targetIndex);
 }
}

void heapSort(int a[], int len) {
 if (len <= 1) {
  return;
 }

 // 初始堆成无序最大堆
 initHeap(a, len);

 for (int i = len - 1; i > 0; --i) {
  // 将当前堆顶元素与最后一个元素交换,保证这一趟所查找到的堆顶元素与最后一个元素交换
  // 注意:这里所说的最后不是a[len - 1],而是每一趟的范围中最后一个元素
  // 为什么要加上>0判断?每次不是说堆顶一定是最大值吗?没错,每一趟调整后,堆顶是最大值的
  // 但是,由于len的范围不断地缩小,导致某些特殊的序列出现异常
  // 比如说,5, 3, 8, 6, 4序列,当调整i=1时,已经调整为3,4,5,6,8序列,已经有序了
  // 但是导致了a[i]与a[0]交换,由于变成了4,3,5,6,8反而变成无序了!
  if (a[0] > a[i]) {
   int temp = a[0];
   a[0] = a[i];
   a[i] = temp;
  }

  // 范围变成为:
  // 0...len-1
  // 0...len-1-1
  // 0...1 // 结束
  // 其中,0是堆顶,每次都是找出在指定的范围内比堆顶还大的元素,然后与堆顶元素交换
  adjustMaxHeap(a, i - 1, 0);
 }
}

2.基于最小堆实现降序排序

// 初始化堆
void initHeap(int a[], int len) {
 // 从完全二叉树最后一个非子节点开始
 // 在数组中第一个元素的索引是0
 // 第n个元素的左孩子为2n+1,右孩子为2n+2,
 // 最后一个非子节点位置在(n - 1) / 2
 for (int i = (len - 1) / 2; i >= 0; --i) {
  adjustMinHeap(a, len, i);
 }
}

void adjustMinHeap(int a[], int len, int parentNodeIndex) {
 // 若只有一个元素,那么只能是堆顶元素,也没有必要再排序了
 if (len <= 1) {
  return;
 }

 // 记录比父节点大的左孩子或者右孩子的索引
 int targetIndex = -1;

 // 获取左、右孩子的索引
 int leftChildIndex = 2 * parentNodeIndex + 1;
 int rightChildIndex = 2 * parentNodeIndex + 2;

 // 没有左孩子
 if (leftChildIndex >= len) {
  return;
 }

 // 有左孩子,但是没有右孩子
 if (rightChildIndex >= len) {
  targetIndex = leftChildIndex;
 }
 // 有左孩子和右孩子
 else {
  // 取左、右孩子两者中最上的一个
  targetIndex = a[leftChildIndex] < a[rightChildIndex] ? leftChildIndex : rightChildIndex;
 }

 // 只有孩子比父节点的值还要小,才需要交换
 if (a[targetIndex] < a[parentNodeIndex]) {
  int temp = a[targetIndex];

  a[targetIndex] = a[parentNodeIndex];
  a[parentNodeIndex] = temp;

  // 交换完成后,有可能会导致a[targetIndex]结点所形成的子树不满足堆的条件,
  // 若不满足堆的条件,则调整之使之也成为堆
  adjustMinHeap(a, len, targetIndex);
 }
}

void heapSort(int a[], int len) {
 if (len <= 1) {
  return;
 }

 // 初始堆成无序最小堆
 initHeap(a, len);

 for (int i = len - 1; i > 0; --i) {
  // 将当前堆顶元素与最后一个元素交换,保证这一趟所查找到的堆顶元素与最后一个元素交换
  // 注意:这里所说的最后不是a[len - 1],而是每一趟的范围中最后一个元素
  // 为什么要加上>0判断?每次不是说堆顶一定是最小值吗?没错,每一趟调整后,堆顶是最小值的
  // 但是,由于len的范围不断地缩小,导致某些特殊的序列出现异常
  // 比如说,5, 3, 8, 6, 4序列,当调整i=1时,已经调整为3,4,5,6,8序列,已经有序了
  // 但是导致了a[i]与a[0]交换,由于变成了4,3,5,6,8反而变成无序了!
  if (a[0] < a[i]) {
   int temp = a[0];
   a[0] = a[i];
   a[i] = temp;
  }

  // 范围变成为:
  // 0...len-1
  // 0...len-1-1
  // 0...1 // 结束
  // 其中,0是堆顶,每次都是找出在指定的范围内比堆顶还小的元素,然后与堆顶元素交换
  adjustMinHeap(a, i - 1, 0);
 }
}

3.C语言版测试

大家可以测试一下:

// int a[] = {5, 3, 8, 6, 4};
int a[] = {89,-7,999,-89,7,0,-888,7,-7};
heapSort(a, sizeof(a) / sizeof(int));

for (int i = 0; i < sizeof(a) / sizeof(int); ++i) {
  NSLog(@"%d", a[i]);
}

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索c语言
, 排序
, 堆排序
排序算法
堆排序算法c语言、最小堆排序算法、c语言最小生成树算法、c语言最小公倍数算法、c语言堆排序,以便于您获取更多的相关知识。

时间: 2024-10-02 14:27:37

C语言实现基于最大堆和最小堆的堆排序算法示例_C 语言的相关文章

C语言编写基于TCP和UDP协议的Socket通信程序示例_C 语言

Tcp多线程服务器和客户端程序服务器程序: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #define PORT 8082 #define BUFSIZE 512 char

VC实现五子棋游戏的一个算法示例_C 语言

本文讲述了VC实现五子棋游戏的一个算法示例,该算法采用极大极小剪枝博弈算法,感兴趣的读者可以对程序中不完善的部分进行修改与完善. 该设计主要包括:数据结构.估值函数.胜负判断.搜索算法 程序运行界面如下: 具体实现步骤如下: 1.数据结构 //记录每步棋,可以建立链表用来进行悔棋.后退(本程序没有实现) struct Step { int x,y; //棋子坐标 int ball; //表示下子方{BLACK,WHITE} }; //记录棋盘情况,用于搜索过程 class CBoardSitua

C++快速幂与大数取模算法示例_C 语言

一.快速幂 其实就是求(a^b)% p ,(其中a,b,p都比较大在int范围内)这类问题. 首先要知道取余的公式: (a*b)%p=(a%p*b%p)%p . 那么幂不就是乘机的累积吗,由此给出代码: int fast(int a,int b,int p) { long long a1=a,t=1; while(b>0) { if(b&1) /如果幂b是奇数多乘一次,因为后边会除2变偶数,(7/2=3) t=(t%p)*(a1%p)%p; a1=(a1%p)*(a1%p)%p; b/=2;

c语言实现冒泡排序、希尔排序等多种算法示例_C 语言

实现以下排序 插入排序O(n^2) 冒泡排序 O(n^2) 选择排序 O(n^2) 快速排序 O(n log n) 堆排序 O(n log n) 归并排序 O(n log n) 希尔排序 O(n^1.25) 1.插入排序 O(n^2) 一般来说,插入排序都采用in-place在数组上实现.具体算法描述如下:⒈ 从第一个元素开始,该元素可以认为已经被排序⒉ 取出下一个元素,在已经排序的元素序列中从后向前扫描⒊ 如果该元素(已排序)大于新元素,将该元素移到下一位置⒋ 重复步骤3,直到找到已排序的元素

基于C++实现的各种内部排序算法汇总_C 语言

提起排序算法相信大家都不陌生,或许很多人已经把它们记得滚瓜烂熟,甚至随时可以写出来.是的,这些都是最基本的算法.这里就把各种内部排序算法总结归纳了一下,包括插入排序(直接插入排序,折半插入排序,希尔排序).交换排序(冒泡排序,快速排序).选择排序(简单选择排序,堆排序).2-路归并排序.(另:至于堆排序算法,前面已经有一篇文章针对堆排序的算法实现做了详细的描述) C++实现代码如下: /*******************************************************

C/C++实现八大排序算法汇总_C 语言

概述排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存. 我们这里说说八大排序就是内部排序. 当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序.堆排序或归并排序. 快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短: 1. 插入排序-直接插入排序(Straight Insertion Sort) 基本思想: 将一个记录插入到已

C语言实现选择排序、直接插入排序、冒泡排序的示例_C 语言

选择排序选择排序是一种简单直观的排序算法,其核心思想是:遍历数组,从未排序的序列中找到最小元素,将其放到已排序序列的末尾. 时间复杂度:O(n^2) 稳定性 :不稳定 /* * @brief selection sort */ void selection_sort(int a[], int n) { int i, j, min, tmp; for (i = 0; i < n - 1; ++i) { min = i; for (j = i+1; j < n; ++j) { if (a[j] &

C语言实现选择排序、冒泡排序和快速排序的代码示例_C 语言

选择和冒泡 #include<stdio.h> void maopao(int a[],int len){ int i,j,temp; for(i = 0;i < len - 1 ; i ++){//从第一个到倒数第二个 for (j = 0 ; j < len - 1 - i ; j ++)//排在后的是已经排序的 { if (a[j] > a[j + 1])//大的数换到后面去 { temp = a[j]; a[j] = a[j + 1]; a [j + 1] = tem

VC++实现选择排序算法简单示例_C 语言

本文以一个非常简单的实例说明VC++选择排序算法的实现方法,对n个记录进行n-1趟简单选择排序,在无序区中选取最小记录. 具体实现代码如下: #include<iostream> using namespace std; //简单选择排序 void SelectSort(int r[ ], int n) { int i; int j; int index; int temp; for (i=0; i<n-1; i++) //对n个记录进行n-1趟简单选择排序 { index=i; for