IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 28.讲堆和堆排序:为什么说堆排序没有快速排序快 -> 正文阅读

[数据结构与算法]28.讲堆和堆排序:为什么说堆排序没有快速排序快

堆排序是原地,时间复杂度为O(nlogn)。快速排序也是如此,为什么实际软件开发快速排序性能要比堆排序要好?

1. 如何理解“堆”?

特点:

  • 堆是一个完全二叉树;
  • 每个节点大于等于或小于等于其左右子树的值。前者叫大顶堆,后者叫小顶堆

2.如何实现一个堆?

完全二叉树可以用数组实现堆,比较省内存,因为不用存指针,直接根据索引计算。
节点索引i,其左节点为2i+1,右节点为2i+2。
在这里插入图片描述

2.1 往堆中插入一个元素

插入一个新元素后,需要继续满足堆的两个要求,为此需要堆化(heapify),堆化有上到下,下到上两种方法。

2.1.1 从下到上堆化

案例:
在这里插入图片描述

过程:
在这里插入图片描述

public class Heap {
  private int[] a; // 数组,从下标1开始存储数据
  private int n;  // 堆可以存储的最大数据个数
  private int count; // 堆中已经存储的数据个数

  public Heap(int capacity) {
    a = new int[capacity + 1];
    n = capacity;
    count = 0;
  }

  public void insert(int data) {
    if (count >= n) return; // 堆满了
    ++count;
    a[count] = data;
    int i = count;
    while (i/2 > 0 && a[i] > a[i/2]) { // 自下往上堆化
      swap(a, i, i/2); // swap()函数作用:交换下标为i和i/2的两个元素
      i = i/2;
    }
  }
 }

我的版本:

public class Class28_Heap {

  int[] arr;
  // 存储最大个数
  int n;
  // 当前存储的个数
  int size;

  public Class28_Heap(int capacity) {
    this.arr = new int[capacity];
    this.n = capacity;
  }

  /**
   * 构建大顶堆
   *
   * @param value
   */
  public void insert(int value) {
    if (size > n) {
      return;
    }

    size++;
    arr[size - 1] = value;
    int i = size - 1;
    while (i >= 0 && arr[i] > arr[(i - 1) / 2]) {
      swap(arr, i, (i - 1) / 2);
      i = (i - 1) / 2;
    }
  }


  private static void swap(int[] arr, int i, int j) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
  }

  public static void main(String[] args) {
    Class28_Heap heap = new Class28_Heap(10);
    heap.insert(10);
    heap.insert(8);
    heap.insert(9);
    heap.insert(2);
    heap.insert(5);
    heap.insert(13);
    System.out.println("done ");

  }
}

2.2 删除堆顶元素

2.2.1 从上往下堆化

思路1问题:
在这里插入图片描述

改进:
在这里插入图片描述

public void removeMax() {
  if (count == 0) return -1; // 堆中没有数据
  a[1] = a[count];
  --count;
  heapify(a, count, 1);
}

private void heapify(int[] a, int n, int i) { // 自上往下堆化
  while (true) {
    int maxPos = i;
    if (i*2 <= n && a[i] < a[i*2]) maxPos = i*2;
    if (i*2+1 <= n && a[maxPos] < a[i*2+1]) maxPos = i*2+1;
    if (maxPos == i) break;
    swap(a, i, maxPos);
    i = maxPos;
  }
}

我的版本:

public void removeMax() {
    if (size == 0) {
      return;
    }
    arr[0] = arr[size - 1];
    // 该位置的值需要移除
    arr[size - 1] = 0;
    size--;
    heapify(arr, size, 0);
  }

  private void heapify(int[] arr, int size, int i) {
    while (true) {
      int maxPos = i;
      if ((i * 2 + 1) < n && arr[i] < arr[(i * 2 + 1)]) {
        maxPos = 2 * i + 1;
      }
      if ((i * 2 + 2) < n && arr[maxPos] < arr[(i * 2 + 2)]) {
        maxPos = 2 * i + 2;
      }
      // 加入i节点的值比左右节点都大,就可以终止了
      if (maxPos == i) {
        break;
      }
      swap(arr, maxPos, i);
      // 继续往下堆化
      i = maxPos;
    }
  }

3. 如何基于堆实现排序?

堆排序是原地排序,时间复杂度非常稳定O(nlog n)。

3.1 建堆

两种实现思路:

  • 参考往堆中插入数据,从前往后处理数组,每插入一个数据,从下往上堆化;
  • 从后往前处理数组,从上往下堆化。
    举第二种例叶:子节点只能自己跟自己比较,所以可以忽略处理。
    在这里插入图片描述在这里插入图片描述
private static void buildHeap(int[] a, int n) {
  for (int i = n/2; i >= 1; --i) {
    heapify(a, n, i);
  }
}

private static void heapify(int[] a, int n, int i) {
  while (true) {
    int maxPos = i;
    if (i*2 <= n && a[i] < a[i*2]) maxPos = i*2;
    if (i*2+1 <= n && a[maxPos] < a[i*2+1]) maxPos = i*2+1;
    if (maxPos == i) break;
    swap(a, i, maxPos);
    i = maxPos;
  }
}

我的版本:

 private static void heapify(int[] arr, int size, int i) {
    while (true) {
      int maxPos = i;
      if ((i * 2 + 1) < size && arr[i] < arr[(i * 2 + 1)]) {
        maxPos = 2 * i + 1;
      }
      if ((i * 2 + 2) < size && arr[maxPos] < arr[(i * 2 + 2)]) {
        maxPos = 2 * i + 2;
      }
      // 加入i节点的值比左右节点都大,就可以终止了
      if (maxPos == i) {
        break;
      }
      swap(arr, maxPos, i);
      // 继续往下堆化
      i = maxPos;
    }
  }

  public void buildHeap(int[] arr) {
    int n = arr.length;
    for (int i = ((n - 1) - 1) / 2; i >= 0; i--) {
      heapify(arr, n, i);
    }
  }

3.1.1 建堆的时间复杂度

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
h=logn,带入公式:
在这里插入图片描述

3.2 排序

建堆完成,堆顶是最大元素,与组后一个元素交换位置,将剩下n-1个元素重新建堆,以此类推,直到组后一个元素。得到数组就是有序的。
在这里插入图片描述

// n表示数据的个数,数组a中的数据从下标1到n的位置。
public void sort(int[] a, int n) {
  buildHeap(a, n);
  int k = n;
  while (k > 1) {
    swap(a, 1, k);
    --k;
    heapify(a, k, 1);
  }
}

我的版本:

 public static void sort(int[] arr) {
    buildHeap(arr);
    int k = arr.length - 1;
    while (k >= 0) {
      swap(arr, 0, k);
      k--;
      heapify(arr, k + 1, 0);
    }
  }

总结:堆排序是原地,时间复杂度O(nlog n),不稳定的排序算法。

4. 解答开篇

  • 堆排序数据访问的方式没有快速排序友好,对于快速排序来说,数据是顺序访问的。而对于堆排序来说,数据是跳着访问的,对CPU缓存是不友好的。
  • 对于同样的数据,在排序过程中,堆排序算法的数据交换次数要多于快速排序。
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-06-25 18:20:58  更:2022-06-25 18:22:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 1:53:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码