【面试知识点】史上最全面讲解----堆排序

简介: 【面试知识点】史上最全面讲解----堆排序

堆排序的过程

 

堆父节点和子节点

 

  1. parent(3) = (i - 1) / 2;
  2. c1(5) = 2 * i + 1;
  3. c2(6) = 2 * i + 2;

 

建立大顶推和小顶推

 

建堆的过程,我们需要知道一个函数heapify()【这里的heapify不代表最终版本】

 

public static void heapify(int[] tree, int n, int i) {
            if (i >= n) {
      return;
    }
    int max = i;
    int c1 = 2 * i + 1;
    int c2 = 2 * i + 2;
    if (c1 < n && tree[c1] > tree[max]) {
      max = c1;
    }
    if (c2 < n && tree[c2] > tree[max]) {
      max = c2;
    }
    if (max != i) {
      duiSwap(tree, max, i);
    }
  }

 

 

这个函数用来进行3、5、6中,将最大的数与3交换

 

 

 

我们对于每一个结点利用heapify,这样,我们是否就得到了一个完整的堆?

我们往下看看,当3与6交换完后,我们再对结点2进行heapify(),2与7进行交换,然后对结点4进行heapify,6与4进行交换,最后1与7交换,我们看看最后的结果

我们考虑下,关于堆的定义:是一个完全二叉树、父节点全部大于等于或者全部小于等于子节点

我们看下图,可以看出明明5和3是4的子节点,为什么4还小于6呢?

同理,5和2也是1的子节点,为什么1还小于5呢?

 

这说明了我们对于堆的建立出现了错误,难道是我们的heapify出现了错误,我们从头开始分析一下:

首先,当我们将3和6进行交换时,这时候我们仅仅改变的是3-5-6之间的关系,也就是合理的,继而我们以2为父节点,改变2-5-7之间的关系,也是合理的,

当我们对以节点4进行heapify时,我们会将6和4的位置进行调换

这时候,聪明的你有没有看出什么错误导致的我们的建堆错误?

答案是:当我们对结点4进行heapify时,将4转变至子节点,破坏了下面这个小堆

也就是,对于交换后,我们原来排序好的堆,就失去了稳定性,有什么办法能让我们原来排序的堆也是顺序的嘛?

聪明的你可能想到了,我们再一次进行heapify不就可以了,对,我们只需要对交换完之后的节点再一次使用heapify,也就是对于上图中进行heapify,得到如下

按照上面的想法,我们对节点1再一次进行heapify

这时候聪明的你,能不能想到我们的heapify少了点什么?

对,我们一开始的heapify少了对改变的子节点进行heapify,我们怎么实现呢?

这里只需要递归就可以了,也就是对改变的子节点进行heapify

 

public static void heapify(int[] tree, int n, int i) {
    if (i >= n) {
      return;
    }
    int max = i;
    int c1 = 2 * i + 1;
    int c2 = 2 * i + 2;
    if (c1 < n && tree[c1] > tree[max]) {
      max = c1;
    }
    if (c2 < n && tree[c2] > tree[max]) {
      max = c2;
    }
    if (max != i) {
      duiSwap(tree, max, i);
      heapify(tree, n, max);
    }
  }

 

这时候我们的堆就建好了,我们可以进行下一步操作,堆排序

 

进行堆排序

 

对于堆排序,我们使用方法是:

每次将root与末尾的进行交换,然后对root进行heapify

我们看一下图示:

依次进行这种操作,我们最后得到的数组就是从小到大排序好的

 

 

注意:我们进行堆排序的时候,我们每次重新对节点使用heapify时,我们已经排序好了一个7,所以下一次进行heapify时,n(也就是heapify的范围)就可以直接除去一个7,后面也是这样

 

 

源代码

 

public static void duiSwap(int[] tree, int i, int j) {
    int x = tree[i];
    tree[i] = tree[j];
    tree[j] = x;
  }
  public static void heapify(int[] tree, int n, int i) {
            if (i >= n) {
      return;
    }
    int max = i;
    int c1 = 2 * i + 1;
    int c2 = 2 * i + 2;
    if (c1 < n && tree[c1] > tree[max]) {
      max = c1;
    }
    if (c2 < n && tree[c2] > tree[max]) {
      max = c2;
    }
    if (max != i) {
      duiSwap(tree, max, i);
      heapify(tree, n, max);
    }
  }
  public static void bulidDui(int[] tree, int n) {
    int last = n - 1;
    int parent = (last - 1) / 2;
    for (int i = parent; i >= 0; i--) {
      heapify(tree, n, i);
    }
  }
  public static void duiSort(int[] tree, int n) {
    bulidDui(tree, n);
    for (int i = n - 1; i >= 0; i--) {
      duiSwap(tree, i, 0);
      heapify(tree, i, 0);
    }
  }
  public static void main(String[] args) {
    int[] tree = new int[] { 2, 5, 3, 1, 10 };
    int n = tree.length;
    duiSort(tree, n);
    for (int i = 0; i < n; i++) {
      System.out.println(tree[i]);
    }
  }

 

相关文章
|
1月前
|
存储 分布式计算 大数据
HBase分布式数据库关键技术与实战:面试经验与必备知识点解析
【4月更文挑战第9天】本文深入剖析了HBase的核心技术,包括数据模型、分布式架构、访问模式和一致性保证,并探讨了其实战应用,如大规模数据存储、实时数据分析及与Hadoop、Spark集成。同时,分享了面试经验,对比了HBase与其他数据库的差异,提出了应对挑战的解决方案,展望了HBase的未来趋势。通过Java API代码示例,帮助读者巩固理解。全面了解和掌握HBase,能为面试和实际工作中的大数据处理提供坚实基础。
83 3
|
1月前
|
SQL 分布式计算 监控
Sqoop数据迁移工具使用与优化技巧:面试经验与必备知识点解析
【4月更文挑战第9天】本文深入解析Sqoop的使用、优化及面试策略。内容涵盖Sqoop基础,包括安装配置、命令行操作、与Hadoop生态集成和连接器配置。讨论数据迁移优化技巧,如数据切分、压缩编码、转换过滤及性能监控。此外,还涉及面试中对Sqoop与其他ETL工具的对比、实际项目挑战及未来发展趋势的讨论。通过代码示例展示了从MySQL到HDFS的数据迁移。本文旨在帮助读者在面试中展现Sqoop技术实力。
118 2
|
1月前
|
监控 负载均衡 Cloud Native
ZooKeeper分布式协调服务详解:面试经验与必备知识点解析
【4月更文挑战第9天】本文深入剖析ZooKeeper分布式协调服务原理,涵盖核心概念如Server、Client、ZNode、ACL、Watcher,以及ZAB协议在一致性、会话管理、Leader选举中的作用。讨论ZooKeeper数据模型、操作、会话管理、集群部署与管理、性能调优和监控。同时,文章探讨了ZooKeeper在分布式锁、队列、服务注册与发现等场景的应用,并在面试方面分析了与其它服务的区别、实战挑战及解决方案。附带Java客户端实现分布式锁的代码示例,助力提升面试表现。
420 2
|
1月前
|
数据采集 消息中间件 监控
Flume数据采集系统设计与配置实战:面试经验与必备知识点解析
【4月更文挑战第9天】本文深入探讨Apache Flume的数据采集系统设计,涵盖Flume Agent、Source、Channel、Sink的核心概念及其配置实战。通过实例展示了文件日志收集、网络数据接收、命令行实时数据捕获等场景。此外,还讨论了Flume与同类工具的对比、实际项目挑战及解决方案,以及未来发展趋势。提供配置示例帮助理解Flume在数据集成、日志收集中的应用,为面试准备提供扎实的理论与实践支持。
68 1
|
1月前
|
XML 分布式计算 监控
Oozie工作流管理系统设计与实践:面试经验与必备知识点解析
【4月更文挑战第9天】本文详述了Oozie工作流管理系统的核心概念,包括安装配置、Workflow XML、Action、Coordinator和Bundle XML定义。此外,讨论了工作流设计实践,如监控调试、自动化运维,并对比了Oozie与其他工作流工具的差异。文中还分享了面试经验及解决实际项目挑战的方法,同时展望了Oozie的未来发展趋势。通过学习,读者能提升Oozie技术能力,为面试做好充分准备。
36 0
|
1月前
|
SQL 存储 分布式计算
Hive数据仓库设计与优化策略:面试经验与必备知识点解析
本文深入探讨了Hive数据仓库设计原则(分区、分桶、存储格式选择)与优化策略(SQL优化、内置优化器、统计信息、配置参数调整),并分享了面试经验及常见问题,如Hive与RDBMS的区别、实际项目应用和与其他组件的集成。通过代码样例,帮助读者掌握Hive核心技术,为面试做好充分准备。
|
1月前
|
机器学习/深度学习 SQL 分布式计算
Spark核心原理与应用场景解析:面试经验与必备知识点解析
本文深入探讨Spark核心原理(RDD、DAG、内存计算、容错机制)和生态系统(Spark SQL、MLlib、Streaming),并分析其在大规模数据处理、机器学习及实时流处理中的应用。通过代码示例展示DataFrame操作,帮助读者准备面试,同时强调结合个人经验、行业趋势和技术发展以展现全面的技术实力。
|
1月前
|
消息中间件 监控 大数据
Kafka消息队列架构与应用场景探讨:面试经验与必备知识点解析
【4月更文挑战第9天】本文详尽探讨了Kafka的消息队列架构,包括Broker、Producer、Consumer、Topic和Partition等核心概念,以及消息生产和消费流程。此外,还介绍了Kafka在微服务、实时数据处理、数据管道和数据仓库等场景的应用。针对面试,文章解析了Kafka与传统消息队列的区别、实际项目挑战及解决方案,并展望了Kafka的未来发展趋势。附带Java Producer和Consumer的代码示例,帮助读者巩固技术理解,为面试做好准备。
46 0
|
1月前
|
监控 Java 应用服务中间件
Spring Boot 源码面试知识点
【5月更文挑战第12天】Spring Boot 是一个强大且广泛使用的框架,旨在简化 Spring 应用程序的开发过程。深入了解 Spring Boot 的源码,有助于开发者更好地使用和定制这个框架。以下是一些关键的知识点:
43 6
|
1月前
|
Java 程序员
Java this关键字详解(3种用法),Java程序员面试必备的知识点
Java this关键字详解(3种用法),Java程序员面试必备的知识点