Problem 1 K smallest In Unsorted Array

Method 1: Sorting

  • (体会下考点不再这里)==》 面试里提一个即可,不要浪费时间

High Level:

  • 把数组从小到大排列

  • 前k个元素就是最小的k个元素

TC& SC

  • O(nlogn), O(sort)

public class Solution {
    public int[] kSmallest(int[] array, int k) {
        Arrays.sort(array);
        int[] result = new int[k];
        for (int i = 0; i< k; i++) {
            result[i] = array[i];
        }
        return result;
    }
}

Method 1b: 用heap sort,不用API

  • 我要用Heap Sort,不用API

    • step 1: 开个heap

    • step 2: 把所有的元素都放在heap里,再拿出来一遍

  • 这个方法不是heap sort,heap sort的最大的优势

    • 不仅是可以和mergeSort一样能达到nlogn的时间

    • 而且人家是space O(1)

class solution {
    public int[] kSmallest(int[] array, int k) {
        heapify(array); // 把你给我的array变成一个heap 
            // 你heap完没有按顺序啊,所以我每次只能拿出来在最前面的那个
            // 所以后面每一步,我需要从第0个,一个个percolateDown,确保从第0个到第k-1个都是排好序的
        for (int i = 0 ; i < array.length; i++) {
            swap(array, 0, array.length - 1 - i);//这一步意味着倒数第i个元素已经完成了排序
            // 接下来调整0到array.length - 1 - i
            percolateDown(array, 0, array.length - 1 - i);
        }
        int[] result = new int[k];
        for (int i = 0; i < k; i++) {
            result[i] = array[i];
        }
    }
    private void heapify(int[] arr) {
        for (int i = arr.length/ 2 - i ; i >= 0; i--) {
            percolateDown(arr, i, arr.length);
        }
    }
    private void swap (int[] arr, int i , int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    private void percolateDown(int[] array, int index, int size) {
        while ( 2* index + 2 <= size) {
            int leftChild = 2 * index + 1;
            int rightChild = 2 * index + 2;
            int candidateChild = leftChild;
            if (rightChild <= size && array[rightChild] > array[leftChild]) {
                candidateChild = rightChild;
            }
            if (array[index] > array[candidateChild]){
                swap(array, index, candidateChild);
            } else{
                break;
            }
            index = candidateChild;
        }
    }
}

TC : O(n) + O(klogn) + O(k) ==> O(nlogn)

SC: O(1)

Method 2 MinHeap(两种)

方法论:当题目求的topK和Kth和你的heap比较的优先级一致的时候,全放进去,poll K次

  • TC: O(nlogn)

  • SC: O(n)

public int[] kSmallest(int[] array, int k) {
    PriorityQueue<Integer> minHeap = new ArrayQueue<>();
    for (int num: array) {
        minHeap.offer(num);
    }
    int[] result = new int[Math.min(minHeap.size()),k];
    for(int i = 0; i < result.length; i++) {
        result[i] = minHeap.poll();
    }
    return result;
}

  • 自己实现heapify且直接poll up前k个

  • 和前面不一样,前面那个是sort好,再解决,这个是每次都poll直到第k次,所以时间会变成O(klog n)

public class Solution {
  public int[] kSmallest(int[] array, int k) {
    // Write your solution here

    // sanity check
    if (array == null || array.length == 0 || k <=0) {
      return new int[]{};
    }

    // build the minheap
    heapify(array, 0, array.length - 1);
    
    // poll k smallest element out
    int[] result = new int[k];
    for (int i = 0; i < k; i++) {
      result[i] = array[0];
      swap(array, 0, array.length - i - 1);
      percolateDown(array, 0, array.length - i - 1);// 下一次少了一个
    }
    return result;
  }
  private void swap(int[] array, int left, int right) {
    int temp = array[left];
    array[left] = array[right];
    array[right] = temp;
  }
  
  private void heapify(int[] array, int left, int right) {
    int size = right - left + 1;
    for (int i = size /2 - 1; i >= 0; i--) {
      percolateDown(array, i, size);
    }
  }
  private void percolateDown(int[] array, int index, int size) {
    while (index <= size / 2 -  1) {
      int leftIndex = index * 2 + 1;
      int rightIndex = index * 2 + 2;
      int candIndex = leftIndex;
      if (rightIndex < size && array[rightIndex] < array[leftIndex]) {
        candIndex = rightIndex;
      }
      if (array[candIndex] < array[index]) {
        swap(array, candIndex, index);
      } else {
        break;
      }
      index = candIndex;
    }
  }
}

TC: O(n + k logn)

SC: O(1)

Method 3: MaxHeap(两种)

方法论:当题目求的topK和Kth和你的Heap比较的优先级不一样

  • 你的heap里装的是:到目前为止topK candidate

    • 手头先攥k个,这些是候选人

    • 然后再撸那些没看过的元素,如果你看到更优先,我们得淘汰手头最不优先的

public class KSmallest{
    
    public int[] kSmallest(int[] array, int k) {
        // sanity check
        if (array.length == 0 || k == 0) {
            return new int[]{0};
        }
        // build the pq
        //PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(k , new Comparator<Integer>() {
            //@Override
            //public int compare(Integer o1, Integer o2) {
                //if (o1.equals(o2) {
                    //return 0;
                //}
                //return o1 > o2 ? -1: 1;
            //}
        //})        
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collection.reverseOrder());
        
        // offer pq in and poll when pq.size() > k
        //for (int i = 0; i < array.length; i++) {
            //if (i < k) {
               // maxHeap.offer(array[i]);
            //}
            //else if (array[i] < maxHeap.peek()) {
                //maxHeap.poll();
                //maxHeap.offer(array[i]);
            //}
        //}    
        for (int i = 0; i < array.length; i++) {
            maxHeap.offer(array[i]);
            if (maxHeap.size() > k) {
                maxHeap.poll();
            }
        }  
        
        // post processing return all result
        
        int[] result = new int[k];
        for (int i = k -1; i >= 0; i--) {
            result[i] = maxHeap.poll();
        }
        return result;
    }
}

这个方法能用heapify优化么?

  • 这个方法是把前k个元素放到heap里,是k size heap

  • heapify应该是O(k),不应该是O(n)---> 我们只应该heapify前k个元素)

  • ---> 本质上实现一个带range的heapify

public int[] kSmallest(int[] array, int k) {
    heapify(array, 0 , k -1);
    // step 1: build the similuated heap in [0, k- 1]
    
    // step 2: try to find the good candidate if array[new] < array[0]
        // swap, percolatedDown
    for (int i = k; i < array.length; i++) {
        if (array[i] < array[0]) { // new candidate is better the worst one
            swap(array, 0, i);
            percolateDown(array, 0, i);
        }
    }
    // step 3:
        // get the result out from small to large 
        // 你应该每次都是从0th拿出来,但是还是需要percolateDown
    int[] result = new int[k];
    for (i = k - 1; i >= 0; i--) { //注意第一个已经先拿出来,所以一共是k个
        result[i] = array[0];
        swap(array, 0, i);
        percolateDown(array, 0, i);
    }
    return result;
}

private void swap(int[] array, int i, int j) {
    int temp = int[i];
    int[i] = int[j];
    int[j] = temp; 
}
private void percolateDown(int[] array, int index, int size) {
    while (index * 2 + 2 < size) {
        int leftChild = index * 2 + 1;
        int rightChild = index * 2 +2;
        int candidate = leftChild;
        if (rightChild < size && array[rightChild] > array[leftChild]) {
            candidate = rightChild;
        }
        if (array[candiate] > array[index]) {
            swap(array, candidate, index);
        }
        else {
            break;
        }
        index = candidate;
    }
}
private void heapify(int[] array, int left, int right) {
    int size = right - left + 1;
    for (int i = size / 2 - 1; i >= 0; i--) { [left, left + size/ 2 - 1]
        percolateDown(arr, i, size);
    }
}

TC: O(k + (n - k) log k)

SC: O(1)

  • 拓展:percolate and heapify只针对left = 0的代码

    • 如果你想要拓展,必须保证left对应第一个元素,采用offset或者[left, right]

  • This one also works for online algorithm

  • 体现出实时性,如果不需要读完完整的input就可以实时的知道,到目前为止的结果,这就是online算法。不然我们就称之为offline算法

Last updated