zoukankan      html  css  js  c++  java
  • 十大排序

    一、十大排序总结

    https://www.cnblogs.com/guoyaohua/p/8600214.html总结得很好。

    1、原地排序:快速排序、堆排序、插入排序、冒泡排序、希尔排序、直接选择排序

    2、非原地排序:归并排序、计数排序、基数排序、桶排序

    3、对有序的序列排序:冒泡、直接插入

    4、对无序的序列排序:快排

    5、对大数据排序:O(nlog(n))都比较好,快排、堆排、归并【归并稳定、快排和堆排不稳定】

    6、

    7、常见的快速排序、归并排序、堆排序、冒泡排序等属于比较排序

    计数排序、基数排序、桶排序则属于非比较排序

    8、算法复杂度与初始状态无关的排序算法有:选择排序、堆排、归并、基数(一龟选友)

    元素总比较次数与初始状态无关的有:选择排序基数排序

    元素总移动次数与初始状态无关的有:归并排序基数排序

    9、稳定的排序:冒泡、插入、归并、计数、桶、基数

    【稳定性:相同的数字其索引位置也有序,即且在原本的列表中 出现在 之前,在排序过的列表中 也将会是在 之前。】

    不稳定的排序:选择、希尔、快排、堆排(一好东西需要快速 选择share

    二、冒泡排序:

    比较相邻的两项,交换顺序排错的项。每对列表实行一次遍历,就有一个最大项排在了正确的位置。第二次遍历开始时,最大的数据项已经归位。

    1、未改进的冒泡排序:两个循环

    def bubbleSort(nums):
        for num in range(len(nums)-1,0,-1):
            for i in range(num):
                if nums[i]>nums[i+1]:
                    temp=nums[i+1]
                    nums[i+1]=nums[i]
                    nums[i]=temp

    2、改进的冒泡排序:

    只要在某一次排完序之后顺序是正确的,那么就停止排序。【设置一个标记,如果顺序正确就停止遍历】

    def bubbleSort(nums):
        n=len(nums)-1
        exit=True
        while n>0 and exit:
            exit=False
            for i in range(n):
                if nums[i]>nums[i+1]:
                    exit=True
                    temp=nums[i+1]
                    nums[i+1]=nums[i]
                    nums[i]=temp
            n-=1

    三、选择排序

    就是最普通那种思路,找到最大的往最右换,接着找到次大的再往右换。

    def selectionSort(nums):
        n=len(nums)-1
        for i in range(n,0,-1):
            maxIndex=0
            for j in range(i+1):
                if nums[j]>nums[maxIndex]:
                    maxIndex=j
            temp=nums[i]
            nums[i]=nums[maxIndex]
            nums[maxIndex]=temp

    四、插入排序

    从前往后来遍历数,新的数据和前面的已经排好的子表进行比较插入到正确的位置。

    def insertSort(nums):
        for i,num in enumerate(nums):
            position=i     
            while position>0 and nums[position-1]>num:
                nums[position]=nums[position-1]
                position=position-1
            nums[position]=num
    def insertsort(arr):
        if not arr:
            return arr
        for i in range(len(arr)):
            for j in range(i):
                if arr[j] > arr[i]:
                    arr[i],arr[j] = arr[j],arr[i]
        return arr
    
    
    if __name__ =='__main__':
        arr = [43,4,12,4,6,9]

    五、希尔排序

    希尔排序是简单插入排序的改进,shell排序是相当于把一个数组中的所有元素分成几部分来排序;先把几个小部分的元素排序好,让元素大概有个顺序,最后再全面使用插入排序。一般最后一次排序都是和上面的插入排序一样的;

    对于下标为【0】【3】【6】【9】的数据即【5】【12】【9】【7】来分割,

    【5】【12】【9】【7】为一组,【16】【3】【17】为一组,【20】【8】【19】为一组。三组进分别行直接插入排序进行交换,结果为

    【5】【7】【9】【12】,【3】【16】【17】,【8】【19】【20】,三组数据是在原本的位置即【5】【3】【8】【7】【16】【19】【9】【17】【20】【12】

    代码:

    def shell_sort(lists):
        if not lists:
            return lists
        n = len(lists)
        gap = n // 2
        while gap > 0:
            ###以下是插入排序
            for i in range(gap, n):
                temp = lists[i]
                preIndex = i - gap
                while preIndex >= 0 and lists[preIndex] > temp:
                    lists[preIndex + gap] = lists[preIndex]
                    preIndex -= gap
                lists[preIndex + gap] = temp
    
            gap //= 2
        return lists
    
    
    if __name__ == '__main__':
        arr = [3, 5, 15, 26, 2, 27, 4, 19, 36, 50, 12]
        res = shell_sort(arr)
        print(res)

    六、快速排序

    快速排序的基本思想是,通过一轮的排序将序列分割成独立的两部分,其中一部分序列的关键字(这里主要用值来表示)均比另一部分关键字小。继续递归对长度较短的序列进行同样的分割,最后到达整体有序。在排序过程中,由于已经分开的两部分的元素不需要进行比较,故减少了比较次数,降低了排序时间。

    平均时间:O(nlogn)

    平均空间:O(nlogn)

    最好时间:O(nlogn)

     
    最坏时间:O(n2) 最坏空间:O(n)

     最坏情况的发生是在数组有序且为完全逆序时,此时快排退化成冒泡;

    解决方法:随机取主元或者取中位数,三路快排(解决大量重复数据)

    def quick_sort(arr,l,r):
        if l < r:
            q = partition(arr,l,r)
            quick_sort(arr,l,q-1)
            quick_sort(arr,q+1,r)
    def partition(arr,l,r):
        x = arr[r]
        i = l-1
        for j in range(l,r):
            if arr[j] < x:
                i += 1
                arr[i],arr[j] = arr[j],arr[i]
        arr[i+1] , arr[r] = arr[r] ,arr[i+1]
        return i+1

     随机选择主元,二路快排

    import random
    def quick_sort(arr , l , r):
        if len(arr) <= 1:
            return arr
        if l < r:
            p = partition(arr, l , r)
            quick_sort(arr, l , p - 1)
            quick_sort(arr, p + 1 , r)
    def partition(arr,l,r):
        if l < r:
            ###randint是生成l<=index<=r的随机整数
            ##随机生成主元
            index = random.randint(l,r)
            x = arr[index]
            arr[r],arr[index] = arr[index],arr[r]
            i = l - 1
            for j in range(l,r):
                if arr[j] < x:
                    i += 1
                    arr[i] , arr[j] = arr[j] , arr[i]
            arr[i+1],arr[r] = arr[r],arr[i+1]
            return i + 1
    arr = [1,3,4,6,2,7]
    quick_sort(arr , 0 , len(arr)-1)
    print(arr)
                

    双路快排和三路快排:

    三路快排优点:解决了近乎有序的数组和有大量重复数组的元素排序问题

    三路快排的代码:

    import random
    #三路排序
    def partition(arr,pivot,l,r):
        if not arr:
            return arr
        small ,index , big = l-1 , l , r+1
        while index != big:
            if arr[index] < pivot:
                small += 1
                arr[small] , arr[index] = arr[index] , arr[small]
                index += 1
            elif arr[index] == pivot:
                index += 1
            else:
                big -= 1
                arr[big] , arr[index] = arr[index] , arr[big]
        return small,big
    #快排,随机选取主元
    def quick_sort(arr,l,r):
        if len(arr) <= 1:
            return arr
        pivot = random.sample(arr,1)[0]
        if l < r:
            l1,r1 = partition(arr,pivot,l,r)
            quick_sort(arr,l,l1)
            quick_sort(arr,r1,r)
        return arr
    
    if __name__=='__main__':
        arr = [9,0,1,3,4,3,1]
        res = quick_sort(arr,0,len(arr)-1)
        print(res)
    
    if __name__=='__main__':
        arr = [9,0,1,3,4,3,1]
        res = quick_sort(arr,0,len(arr)-1)
        print(res)

    七、堆排序

    http://www.cnblogs.com/0zcl/p/6737944.html

    先建立堆,然后排序。

    满足堆的性质:子结点的键值或索引总是小于(或者大于)它的父节点。

    7.1 算法描述

        调整堆;

        循环(条件:堆不为空){

          取出堆顶元素;

          将最后一个元素移动到堆顶位置;

          调整使之再次成为堆;

        }

    例子:

    给定一个列表array=[16,7,3,20,17,8],对其进行堆排序。

     一、将列表中的数值构建成一个完全二叉树:

    二、调整:初始化大顶堆,即将列表中的最大值放在堆顶。并且构建成堆。即父>=子。 初始化大顶堆时 是从最后一个有子节点开始往上调整最大堆。

    三、交换:堆顶元素和堆尾【最后一个元素】交换,此时可以砍掉堆尾元素了,因为最大值已经排好序。而堆顶元素(最大数)与堆最后一个数交换后,需再次调整成大顶堆,此时是从上往下调整的。

    四、重复步骤二、三,直到结束。即【重新调整剩余的堆并交换堆顶和堆尾的数【除了20之外的数】】。

    (1)调整(2)交换

    (1)调整(2)交换

    (1)调整(2)交换

    (1)调整(2)交换

    代码

    #堆排序算法
    #全局变量
    n = 0
    def heap_sort(arr):
        global n
        n = len(arr)
        if n <= 1 :
            return arr
        #  1、构建一个最大堆
        buildMaxHeap(arr)
        # 2、循环将堆首(最大值)与末位交换,然后再重新调整最大堆
        while n > 0:
            arr[0] , arr[n-1] = arr[n-1] , arr[0]
            n -= 1
            adjustHeap(arr,0)
    
        return arr
    
    #建立最大堆
    def buildMaxHeap(arr):
        global n
        for i in range(n//2-1,-1,-1):
            adjustHeap(arr,i)
    
    #调整成为最大堆
    def adjustHeap(arr,i):
        global n
        maxIndex = i
        #如果有左子树,且左子树大于父节点,则将最大指针指向左子树
        if i * 2 < n and arr[i * 2] > arr[maxIndex]:
            maxIndex = i * 2
        # 如果有右子树,且右子树大于父节点,则将最大指针指向右子树
        if i * 2 + 1 < n and arr[i*2 + 1] > arr[maxIndex]:
            maxIndex = i * 2 + 1
        # 如果父节点不是最大值,则将父节点与最大值交换,并且递归调整与父节点交换的位置。
        if maxIndex != i:
            arr[maxIndex] , arr[i] = arr[i] , arr[maxIndex]
            adjustHeap(arr,maxIndex)
    
    if __name__ == '__main__':
        #自己建堆
        arr2 = [3, 5, 15, 26, 2, 27, 4, 19, 36, 50, 12]
        heap_sort(arr2)
        print(arr2)  #[2, 3, 4, 5, 12, 15, 19, 26, 27, 36, 50]

     

    八、归并排序

    • 把长度为n的输入序列分成两个长度为n/2的子序列;
    • 对这两个子序列分别采用归并排序;
    • 将两个排序好的子序列合并成一个最终的排序序列。
    #归并排序
    def merge(left,right):
        if not left:
            return right
        if not right:
            return left
        arr = []
        i,j = 0,0
        while i < len(left) and j < len(right):
            if left[i] >= right[j]:
                arr.append(right[j])
                j += 1
            else:
                arr.append(left[i])
                i += 1
        arr += left[i:]
        arr += right[j:]
        return arr
    
    #合并两个子序列
    def merge_sort(arr):
        if not arr or len(arr) == 1:
            return arr
        mid = len(arr) // 2
        left = merge_sort(arr[:mid])
        right = merge_sort(arr[mid:])
        return merge(left,right)
    
    if __name__=="__main__":
        arr = [3, 5, 15, 26, 2, 27, 4, 19, 36, 50, 12]
        res = merge_sort(arr)
        print(res)

    排序中topK那点事(转)

    问题描述:有 N (N>1000000)个数,求出其中的前K个最小的数(又被称作topK问题)。

    这类问题似乎是备受面试官的青睐,相信面试过互联网公司的同学都会遇到这来问题。下面由浅入深,分析一下这类问题。

    思路1:【全部排序】

      最基本的思路,将N个数进行完全排序,从中选出排在前K的元素即为所求。有了这个思路,我们可以选择相应的排序算法进行处理,快速排序,堆排序和归并排序都能达到O(NlogN)的时间复杂度。当然,这样的答案也是无缘offer的。

    思路2:【只对K部分排序,冒泡或选择】

    可以采用数据池的思想,选择其中前K个数作为数据池,后面的N-K个数与这K个数进行比较,若小于其中的任何一个数,则进行替换。这种思路的算法复杂度是O(N*K),当答出这种算法时,似乎离offer很近了。

    有没有算法复杂度更低的方法呢?

    从思路2可以想到,剩余的N-K个数与前面K个数比较的时候,是顺序比较的,算法复杂度是K。怎么在这方面做文章呢? 采用的数据结构是堆。

    思路3:【K部分堆排序】

    大根堆维护一个大小为K的数组,目前该大根堆中的元素是排名前K的数,其中根是最大的数。此后,每次从原数组中取一个元素与根进行比较,如小于根的元素,则将根元素替换并进行堆调整(下沉),即保证大根堆中的元素仍然是排名前K的数,且根元素仍然最大;否则不予处理,取下一个数组元素继续该过程。该算法的时间复杂度是O(N*logK),一般来说企业中都采用该策略处理topK问题,因为该算法不需要一次将原数组中的内容全部加载到内存中,而这正是海量数据处理必然会面临的一个关卡。如果能写出代码,offer基本搞定。

    class Solution:
        def __init__(self):
            self.n = 0
        def heap_sort(self,arr):
            self.n = len(arr)
            self.buildHeap(arr)
            return arr
        def buildHeap(self,arr):
            for i in range(self.n//2-1,-1,-1):
                self.adjustHeap(arr,i)
        def adjustHeap(self,arr,i):
            maxIndex = i
            if i * 2 < self.n and arr[i*2] > arr[i]:
                maxIndex = i * 2
            if i * 2 + 1 < self.n and arr[i*2 +1] > arr[i]:
                maxIndex = i * 2 + 1
            if maxIndex != i:
                arr[i] , arr[maxIndex] = arr[maxIndex] , arr[i]
                self.adjustHeap(arr,maxIndex)
        def GetLeastNumbers_Solution(self, tinput, k):
            # write code here
            if not tinput or k <= 0 or k > len(tinput):
                return []
            if len(tinput) == k:
                return sorted(tinput)
            arr = self.heap_sort(tinput[:k])
            for i in range(k,len(tinput)):
                if tinput[i] < arr[0]:
                    arr[0],tinput[i] = tinput[i],arr[0]
                    self.heap_sort(arr)
            return sorted(arr)

    还有没有更简单的算法呢?答案是肯定的。

    思路4:【不排序---快排】

    利用快速排序的分划函数找到分划位置K,则其前面的内容即为所求。该算法是一种非常有效的处理方式,时间复杂度是O(N*logK)(证明可以参考算法导论书籍)。对于能一次加载到内存中的数组,该策略非常优秀。如果能完整写出代码,那么相信面试官会对你刮目相看的。

    下面,给出思路4的Python代码:

    def partition(L, left, right):
        """
        将L[left:right]进行一次快速排序的partition,返回分割点
       :param L: 数据List
        :param left: 排序起始位置
       :param right: 排序终止位置
       :return: 分割点
        """
        if left < right:
            print left
            key = L[left]
            low = left
            high = right
            while low < high:
                while low < high and L[high] >= key:
                    high = high - 1
                L[low] = L[high]
                while low < high and L[low] <= key:
                    low = low + 1
                L[high] = L[low]
            L[low] = key
        return low
    
    def topK(L, K):
        """
        求L中的前K个最小值
       :param L: 数据List
        :param K: 最小值的数目
        """
        if len(L) < K:
            pass
        low = 0
        high = len(L) - 1
        j = partition(L, low, high)
        while j != K: # 划分位置不是K则继续处理
          if K > j: #k在分划点后面部分
             low = j + 1
            else:
                high = j           # K在分划点前面部分
          j = partition(L, low, high)

    扩展问题:

    1.如果需要找出N个数中最大的K个不同的浮点数呢?比如,含有10个浮点数的数组(1.5,1.5,2.5,3.5,3.5,5,0,- 1.5,3.5)中最大的3个不同的浮点数是(5,3.5,2.5)。
         解答:四种思路都可以。

    2. 如果是找第k到第m(0<k<=m<=n)大的数呢?
           解答:可以用小根堆来先求出m个最大的,然后从中输出k到m个。
    3. 在搜索引擎中,网络上的每个网页都有“权威性”权重,如page rank。如果我们需要寻找权重最大的K个网页,而网页的权重会不断地更新,那么算法要如何变动以达到快速更新(incremental update)并及时返回权重最大的K个网页?
       解答:(解法三)用堆排序当每一个网页权重更新的时候,更新堆。
     举一反三:查找最小的K个元素
        解答:最直观的方法是用快速排序或堆排序先排好,在取前K小的数据。最好的办法是利用解法四和解法三的原理进行查找。

  • 相关阅读:
    二分练习题4 查找最接近的元素 题解
    二分练习题5 二分法求函数的零点 题解
    二分练习题3 查找小于x的最大元素 题解
    二分练习题2 查找大于等于x的最小元素 题解
    二分练习题1 查找元素 题解
    code forces 1176 D. Recover it!
    code forces 1173 B. Nauuo and Chess
    code forces 1173 C. Nauuo and Cards
    吴恩达深度学习课程笔记-15
    吴恩达深度学习课程笔记-14
  • 原文地址:https://www.cnblogs.com/Lee-yl/p/8969558.html
Copyright © 2011-2022 走看看