【每日算法0228】 排序(中等)

99 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 20天,点击查看活动详情

题目

剑指 Offer 40. 最小的k个数

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

示例:

输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]

分析

简单粗暴两步完成

  • 数组排序
  • 数组截取

实现

function getLeastNumbers(arr: number[], k: number): number[] {
    arr.sort((a, b) => a - b)
​
    return arr.slice(0, k)
};

题目

剑指 Offer 41. 数据流中的中位数

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

示例:

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

  • void addNum(int num) - 从数据流中添加一个整数到数据结构中。
  • double findMedian() - 返回目前所有元素的中位数。
输入:
["MedianFinder","addNum","addNum","findMedian","addNum","findMedian"]
[[],[1],[2],[],[3],[]]
输出:[null,null,null,1.50000,null,2.00000]

分析一

先简单粗暴的做出来试试

由于该数据结构没有要求需要记录数据进入的顺序,故我们可以在内部将数据进行排序

那么这道题的求解可以分解为

  • 添加元素,并进行排序

  • 求中位数,分两种情况(偶数和奇数数组)

    • 偶数数组:数组长度 / 2 为较大的中位数索引,减1得出较小中位数索引,可得
    (this.list[Math.floor(len / 2)] + this.list[Math.floor(len / 2) - 1]) / 2
    
    • 奇数数组:数组长度 / 2 取整数部分就是中位数索引,可以直接求得
    this.list[Math.floor(len / 2)]
    

实现

class MedianFinder {
    list: number[]
    constructor() {
        this.list = []
    }
​
    addNum(num: number): void {
        this.list.push(num)
        this.list.sort((a, b) => a - b)
    }
​
    findMedian(): number {
        let len = this.list.length
        // console.log(len)
        if (len % 2 == 0) {
            let index = Math.floor(len / 2)
​
            return (this.list[index] + this.list[index - 1]) / 2
        } else {
            let index = Math.floor(len / 2)
​
            return this.list[index]
        }
​
    }
}

补充

JS堆的实现

在计算机科学中,堆是一种数据结构,它是一种特殊的树形结构,通常用来实现优先队列。堆具有以下特性:

  1. 堆是一棵完全二叉树,即除了最后一层,其他层的节点都是满的,最后一层的节点从左到右排列。
  2. 堆中每个节点都必须满足堆属性,即父节点的键值必须大于或等于(或小于或等于)其子节点的键值。
  3. 堆通常分为最大堆和最小堆。在最大堆中,父节点的键值大于或等于其子节点的键值;在最小堆中,父节点的键值小于或等于其子节点的键值。

堆的主要应用是实现优先队列,其中每个元素都有一个关键字,优先级高的元素先出队。堆的时间复杂度通常是O(log n),因此在处理大量数据时非常高效。堆还可以用于排序算法,例如堆排序。

最大堆代码

class MaxHeap {
  constructor() {
    this.heap = [];
  }
​
  // 获取父节点索引
  getParentIndex(index) {
    return Math.floor((index - 1) / 2);
  }
​
  // 获取左子节点索引
  getLeftChildIndex(index) {
    return index * 2 + 1;
  }
​
  // 获取右子节点索引
  getRightChildIndex(index) {
    return index * 2 + 2;
  }
​
  // 上移操作,用于插入节点后调整堆
  shiftUp() {
    let index = this.heap.length - 1;
    while (index > 0) {
      let parentIndex = this.getParentIndex(index);
      if (this.heap[index] > this.heap[parentIndex]) {
        [this.heap[index], this.heap[parentIndex]] = [this.heap[parentIndex], this.heap[index]];
        index = parentIndex;
      } else {
        break;
      }
    }
  }
​
  // 下移操作,用于删除节点后调整堆
  shiftDown() {
    let index = 0;
    while (this.getLeftChildIndex(index) < this.heap.length) {
      let maxChildIndex = this.getLeftChildIndex(index);
      let rightChildIndex = this.getRightChildIndex(index);
      if (rightChildIndex < this.heap.length && this.heap[rightChildIndex] > this.heap[maxChildIndex]) {
        maxChildIndex = rightChildIndex;
      }
      if (this.heap[index] < this.heap[maxChildIndex]) {
        [this.heap[index], this.heap[maxChildIndex]] = [this.heap[maxChildIndex], this.heap[index]];
        index = maxChildIndex;
      } else {
        break;
      }
    }
  }
​
  // 插入节点
  insert(value) {
    this.heap.push(value);
    this.shiftUp();
  }
​
  // 删除堆顶节点
  delete() {
    if (this.heap.length === 0) {
      return null;
    }
    if (this.heap.length === 1) {
      return this.heap.pop();
    }
    let deletedValue = this.heap[0];
    this.heap[0] = this.heap.pop();
    this.shiftDown();
    return deletedValue;
  }
}
​

以上代码中,我们定义了一个 MaxHeap 类,它包含了以下方法:

  • getParentIndex(index):获取父节点索引。
  • getLeftChildIndex(index):获取左子节点索引。
  • getRightChildIndex(index):获取右子节点索引。
  • shiftUp():上移操作,用于插入节点后调整堆。
  • shiftDown():下移操作,用于删除节点后调整堆。
  • insert(value):插入节点。
  • delete():删除堆顶节点。

在这个示例中,我们实现了最大堆。如果需要实现最小堆,只需要修改 shiftUp()shiftDown() 方法中的比较符号即可。