【程序员笔试面试必会——排序③】高频笔试题、知识点

一、小范围排序

题目:

  已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离要小于k,并且k相对于数组来说比较小。请选择一个合适的排序算法针对这个数据进行排序。

给定一个int数组A,同时给定A的大小n和题意中的k,请返回排序后的数组。

测试样例:

  [2, 1, 4, 3, 6, 5, 8, 7, 10, 9], 10, 2
  返回:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

思路:

  首先看一下时间复杂度O(N)的算法,如:计数排序、基数排序,但因为我们不知道数组的范围,所以这些算法就不去考虑了。

  然后看一下时间复杂度O(N^2)的算法,如:冒泡排序、选择排序,这两个排序算法是无论要排序的序列是什么顺序,时间复杂度都是严格的O(N^2)。插入排序,这里可以做到很好的程度,因为插入排序的过程与原始顺序有关,每个移动距离不超过k,对本题来说,插入排序的时间复杂度是不会高于O(N*k)。

  最后看一下时间复杂度O(N*logN)的算法,如:快速排序,快速排序与原始数据顺序也是无关的,快速排序是随机选一个数,以这个数对整个数组进行划分,划分出来的两个部分再分别进行递归。归并排序,也是与原始数据顺序无关的,归并排序是把所有的组都打散,然后小组合大组,大组再合更大的组,最后使整个数组有序。

  然而这道题的答案是,改进后的堆排序。整个数组的最小值肯定是在 位置0~位置k-1 这个范围里的,首先将 位置0~位置k-1 这k个数组成一个小根堆,这个小根堆的堆顶肯定是整个数组的最小值,把堆顶弹出放在数组的 位置0 上,再将原序列 位置k 上的数放在小根堆的堆顶,对小根堆进行调整,数组第二小的数会作为小根堆的堆顶出现,再把堆顶放在数组的 位置1 上,反复操作,如此弹出堆顶的顺序就是数组排好序的顺序。因为每次调整顺序都是在大小为k的小根堆里调整的,所以每次调整的代价是O(k),总共N个数,所以整个的时间复杂度是O(N*logk)。

Python代码:

# -*- coding:utf-8 -*-

class ScaleSort:

    def sortElement(self, A, len_A, k):
        i = 0
        min_heap = A[0: k]
        for n in A[k:]:
            # 修复小根堆
            for j in xrange(k / 2 + 1, -1, -1):
                self.min_heap_fix(min_heap, j, k)
            A[i], min_heap[0] = min_heap[0], n
            i += 1

        for _ in xrange(k):
            for j in xrange(len(min_heap) + 1, -1, -1):
                self.min_heap_fix(min_heap, j, len(min_heap))
            A[i] = min_heap.pop(0)
            i += 1

        return A

    def min_heap_fix(self, A, i, n):
        """
        :param A: 小根堆(一维数组)
        :param i: 预修复的子树根节点
        :param n: 小根堆总的元素数量
        """
        j = i * 2 + 1  # i的左子节点下标
        # 当i的左子节点存在时
        while j < n:
            # 当i的右子节点存在,且大于i的左子节点
            if j + 1 < n and A[j+1] < A[j]:
                j += 1
            # 当i的左右子节点都大于i时,修复小根堆结束
            if A[j] >= A[i]:
                break
            # 当i的子节点小于i时,交换节点
            A[i], A[j] = A[j], A[i]
            i = j  # 将i移向于i交换的节点
            j = i * 2 + 1  # i的左子节点下标


l = [2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
print ScaleSort().sortElement(l, len(l), 6)

 

 

 

 

posted @ 2018-01-21 21:40  WangC.W  阅读(723)  评论(0编辑  收藏  举报