《剑指Offer》查找和排序

2019-02-09  本文已影响28人  4v3r9

快速排序

在各种基于关键码比较的内排序算法中,快速排序是实践中平均速度最快的算法之一。算法的基本思想是划分,即按照某种标准把考虑的记录划分为“小记录”和“大记录”,并通过递归不断划分,最终得到一个排序的序列。其基本过程是:

快速排序算法有许多不同的实现方法,下面主要介绍一种针对顺序表的实现。

def qsort_rec(lst, l, r):
    if l >= r:return

    i = l
    j = r
    pivot = lst[l]
    while i<j:
        while i<j and lst[j] >= pivot:
            j -= 1
        if i <j:
            lst[i] = lst[j]
            i+=1

        while i<j and lst[i] <= pivot:
            i +=1
        if i < j:
            lst[j] = lst[i]
            j -=1
    lst[i] = pivot
    qsort_rec(lst, l, i-1)
    qsort_rec(lst, i+1, r)


def quick_sort(lst):
    qsort_rec(lst, 0, len(lst)-1)


origin = [3,4,6,72,1,-1,6,9,17]
quick_sort(origin)
print(origin)

复杂度分析

根据二叉树理论,所有n个节点的二叉树的平均高度是 O(logn)。因此,快速排序的时间复杂度是 O(nlogn)

虽然不知道解释器每层递归花费的具体空间量,但应该认为它是常量,与递归的深度和函数调用次数无关。因此, 分析前面快速排序的空间复杂度,最重要的问题就是递归的深度,最坏情况是 O(n)

快速排序算法不会因为原序列接近有序而更高效(适得其反),因此它不具有适应性。

另一种简单实现:

def quick_sort(lst):
    def qsort(lst, begin, end):
        if begin >= end:
            return
        pivot = lst[begin]
        i = begin
        for j in range(begin+1, end+1):
            if lst[j] < pivot:
                i+=1
                lst[i], lst[j] = lst[j], lst[i]

       lst[begin],lst[i] = lst[i], lst[begin]
       qsort(lst, begin, i-1)
       qsort(lst, i+1, end)

    qsort(lst,0, len(lst)-1)

tlist = [4,2,1,7,0,9,3]
quick_sort(tlist)
print(tlist)

面试题

旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

运行时间:962ms, 占用内存:5724k


def minNumberInRotateArray(rotateArray):
    # write code here
    if not rotateArray:
        return 0
    ind1 = 0
    ind2 = len(rotateArray) -1
    indMid = ind1
    while rotateArray[ind1] >= rotateArray[ind2]:
        if ind2 - ind1 == 1:
            indMid = ind2
            break
        indMid = (ind1 + ind2)//2
        if rotateArray[indMid] >= rotateArray[ind1]:
            ind1 = indMid
        elif rotateArray[indMid] <= rotateArray[ind2]:
            ind2 = indMid

    return rotateArray[indMid]


tlist = [3,4,5,1,2]
res = minNumberInRotateArray(tlist)
print(res)

以上思路不够完善:[1,0,1,1,1][1,1,1,0,1]也是符合题目要求的数组,但是当第一个指针和第二个指针分别指向1时,有的中间指针在0 前面,有的在0后面。因此,当两个指针指向的数字和他们中间的数字三者相同的时候,无法判断中间的数字是位于前面的子数组还是位于后面的,也就无法移动两个指针来缩小查找范围。此时,不得不采用顺序查找的方法。

完善版本:
运行时间:1005ms, 占用内存:5740k
新增加函数实现顺序查找功能

class Solution:
    def MinOrder(self, nums, left, right):
        res = nums[left]
        for i in range(left+1, right):
            #print(i,nums[i])
            if nums[i] < res:
                res = nums[i]
        return res

    def minNumberInRotateArray(self, rotateArray):
        # write code here
        if not rotateArray:
            return 0
        left, right = 0, len(rotateArray)-1
        mid = 0
        while rotateArray[left]>=rotateArray[right]:
            if right - left ==1:
                mid = right
                break

            mid = (left + right)//2

            if  rotateArray[left]==rotateArray[right] \
                and rotateArray:
                #print(left, right, rotateArray[left:right])
                return self.MinOrder(rotateArray, left, right)

            if rotateArray[mid]>= rotateArray[left]:
                left = mid
            else:
                right = mid
        return rotateArray[mid]

sl = Solution()
num = [2,2,2,2,1,2]
print(sl.minNumberInRotateArray(num))
上一篇下一篇

猜你喜欢

热点阅读