数据结构杂谈(三)堆排序

用数组存储一个堆,看成一个近似的完全二叉树。堆有自己的尺寸(不一定与数组长度相同)。

堆的性质:父结点比儿子结点大是最大堆;父节点比儿子节点小是最小堆。

本文以最大堆为例。

对于一个结点i,其父结点标号是i/2向下取整,左子结点是2*i,右子结点是2*i+1。

堆的高度是log(n)。

堆排序的思想

堆排序的本质是淘汰赛。分组比赛最终得到冠军。

一个典型的淘汰赛

要实现堆排序,需要以下步骤。

  1. 对于不符合堆性质的地方,需要进行重建堆
  2. 对于初始的情况,需要初始建堆
  3. 用1和2实现堆排序堆排序

重建堆

对于不符合堆性质(最大堆:父结点比儿子结点大)的地方,需要进行重建堆。

重建堆的思想:

  1. 找到当前结点i、左子结点、右子结点中的最大值
  2. 将最大的结点交换到当前i的位置
  3. 由于交换之后可能破坏子树,需要对交换的子节点递归调用重建堆算法
  4. 如果没有发生交换,则为递归出口

c++实现

void restoreHeap(MyArray &arr, int curr, int size)
{
    int leftSon = curr * 2 + 1;//元素标号从0开始,这里再加1
    int rightSon = curr * 2 + 2;
    int maxLoc = curr;
    if (leftSon<size)
        maxLoc = (arr[maxLoc]>arr[leftSon]) ? maxLoc : leftSon;
    if (rightSon<size)
        maxLoc = (arr[maxLoc]>arr[rightSon]) ? maxLoc : rightSon;
    if (maxLoc != curr)
    {
        arr.swap(curr, maxLoc);
        restoreHeap(arr, maxLoc, size);
    }
}

算法分析

重建堆的时间复杂度是O(n)

初始建堆

进行排序时,数组元素顺序进入堆中,未保证堆的性质,需要对堆进行初始化。

初始建堆的思想

从最后一个内结点向前不断重建堆,自底向上的重建。

c++实现

void initHeap(MyArray &arr, int size)
{
    for (int i = size / 2; i >= 0; i--)
    {
        restoreHeap(arr, i, size);
    }
}

算法分析

初始建堆的时间复杂度是O(n)

堆排序

对一个最大堆,最大元素在根结点的位置,将根与堆的最后一个元素交换,并将堆的尺寸减1,然后重建堆,直到排好序。

c++实现

void heapSort(MyArray& arr)
{
    int size = arr.len();
    initHeap(arr, size);
    while (size>0)
    {
        arr.swap(size-1, 0);
        --size;
        restoreHeap(arr, 0, size);
    }
}

算法分析

堆排序的时间复杂度是O(nlgn)

*关于MyArray类,请移步至我的上一篇博客http://www.cnblogs.com/yatesxu/p/5369976.html

时间: 2024-10-14 00:35:45

数据结构杂谈(三)堆排序的相关文章

数据结构(三):非线性逻辑结构-特殊的二叉树结构:堆、哈夫曼树、二叉搜索树、平衡二叉搜索树、红黑树、线索二叉树

在上一篇数据结构的博文<数据结构(三):非线性逻辑结构-二叉树>中已经对二叉树的概念.遍历等基本的概念和操作进行了介绍.本篇博文主要介绍几个特殊的二叉树,堆.哈夫曼树.二叉搜索树.平衡二叉搜索树.红黑树.线索二叉树,它们在解决实际问题中有着非常重要的应用.本文主要从概念和一些基本操作上进行分类和总结. 一.概念总揽 (1) 堆 堆(heap order)是一种特殊的表,如果将它看做是一颗完全二叉树的层次序列,那么它具有如下的性质:每个节点的值都不大于其孩子的值,或每个节点的值都不小于其孩子的值

3. 蛤蟆的数据结构进阶三静态查询之折半查询

3. 蛤蟆的数据结构进阶三静态查询之折半查询 本篇名言:"但是话不行,要紧的是做. --鲁迅" 继续来看静态查询的折半查询. 欢迎转载,转载请标明出处:http://blog.csdn.net/notbaron/article/details/47211637 1.  折半查找 折半查找要求查找表用顺序存储结构存放且各数据元素按关键字有序(升序或隆序)排列,也就是说折半查找只适用于对有序顺序表进行查找. 折半查找的基本思想是:首先以整个查找表作为查找范围,用查找条件中给定值k与中间位置

一步一步学习数据结构(三)栈的顺序存储结构实现代码

//栈这种逻辑结构的实现与一般线性表的实现类似,有两种存储方式:顺序存储和链式存储 //先学习顺序存储 //1. #include<stdio.h> #include<stdlib.h> #define null NULL #define SIZE 100 typedef int dataType ; typedef struct { dataType data[SIZE]; int top; }cStack,*cStackPointer; //初始化栈空间 void initSt

Java杂谈三之判断素数以及穷举素数

首先确认下什么是素数,又称质数 百度的定义解答: 质数(prime number)又称素数,有无限个.一个大于1的自然数,如果除了1和它本身 外,不能被其他自然数整除(除0以外)的数称之为素数(质数) 明确了素数的定义后,下面用java来判断素数以及穷举2-999以内的素数,还是一样,主 要看的是对一个数进行判断或者对一组数进行判断的思想,至于怎样判断的,百度的定义已经给了很好的解答思路. JavaPrimeNubmer:类名 enumPrimeNumber():穷举2-999以内的所有素数 m

【算法与数据结构】图说堆排序

1.堆   一棵完全二叉树 大顶堆:所有非叶子节点元素均不小于其左右子树根节点的值 小顶堆:所有非叶子节点元素均不大于其左右子树根节点的值 2. 初始化堆 ①一组无序元素R[0, 1, ..., n - 1], 先按照顺序将该组无序元素构造为一棵完全二叉树 ②从该二叉树的第一个非叶子结点开始调整,然后调整前一个结点(一定是非叶子结点),依次直到调整完根节点 ③上一步一遍完成后,再来一遍,直到该完全二叉树符合一个堆的定义为止 测试数据:R[] = {16, 7, 3, 20, 17, 8}, 本组

数据结构(三)——基于顺序存储结构的线性表

数据结构(三)--基于顺序存储结构的线性表 一.基于顺序存储结构的线性表实现 1.顺序存储的定义 线性表的顺序存储结构是用一段地址连续的存储单元依次存储线性表中的数据元素. 2.顺序存储结构的操作 使用一维数组实现顺序存储结构. template <typename T> class SeqList:public List<T> { protected: T* m_array;//顺序存储空间 int m_length;//当前线性表的长度 }; 一维顺序存储结构可以是原生数组或是

我理解的数据结构(三)—— 队列(Queue)

我理解的数据结构(三)-- 队列(Queue) 一.队列 队列是一种线性结构 相比数组,队列对应的操作是数组的子集 只能从一端(队尾)添加元素,只能从另一端(队首)取出元素 队列是一种先进先出的数据结构(FIFO) 二.数组队列与循环队列 1. 数组队列 如果你有看过我之前的文章不要小看了数组或者栈,你就会发现,自己封装一个数组队列是如此的轻松加愉快! (1)先定义一个接口,接口中定义队列需要实现的方法 public interface Queue<E> { int getSize(); bo

数据结构杂谈(一)浅谈基本排序算法

0.基本概念 记录:待排序的项目 关键词:决定排序结果 稳定性:相同关键词的记录保持原来的相对次序 1.1插入排序(Insertion Sort) 算法思想 一种简单直观的排序算法,工作原理是通过构建有序序列:对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入. 算法描述 具体算法描述如下: 从第一个元素开始,该元素可以认为已经被排序 取出下一个元素,在已经排序的元素序列中从后向前扫描 如果该元素(已排序)大于新元素,将该元素移到下一位置 重复步骤3,直到找到已排序的元素小于或者等于

数据结构(三):非线性逻辑结构-二叉树

接着上一次对非线性逻辑数据结构树的内容,开启对二叉树的深入复习和总结.首先还是先回顾一下几个重要的概念: 一.回顾 1. 满二叉树与完全二叉树 满二叉树指的是除了叶子节点外所有的节点都有两个子节点.这样可以很容易的计算出满二叉树的深度,要掌握满二叉树的一些性质. 完全二叉树则是从满二叉树继承而来,指的所有的节点按照从上到下,从左到右的层次顺序依次排列所构成的二叉树称之为完全二叉树.所以可以想象,对于深度为h的完全二叉树,前h-1层可以构成深度为h-1的满二叉树,而对于第h层则是从左到右连续排列的