排序算法实现

来源:互联网 发布:淘宝网出售二手床 编辑:程序博客网 时间:2024/06/03 19:28

排序算法实现

前言

最近处理数据时常会用到排序算法,故有整理各种排序算法的想法。博客中有很多非常好的文章排序算法介绍及实现,但经常发现其实现代码并不能很好的运行,或多或少都会有bug,故写下以下文章总结一下各算法中需要注意的问题。  

     

插入排序


直接插入排序


算法思想将记录逐个插入到已存在的有序表中,从而得到一个新的有序表。即:先将序列的第1个记录看成是一个有序的子序列,然后从第2个记录逐个进行插入,直至整个序列有序。


示意图


图1 直接插入排序


算法实现(Python实现)


第一版

class InsertSort(object):    def __init__(self):        return    def sort(self, mlist):  # mlist为待排序列表(数组)        for count in xrange(1, len(mlist)):            index = count - 1  # index待比较位置, index+1为待插入位置            ele = mlist[count]  # 复制为哨兵x,即存储待排序元素            if ele > mlist[count - 1]:                continue  # 若待插入大于序列最后一个元素,直接插入。                while(ele < mlist[index]):                mlist[index + 1] = mlist[index]  # 若ele小于待插入位置的元素则后移                index = index - 1  # 继续向左比较直到找到比ele大的元素(索引index),插入index+1位置            mlist[index + 1] = ele  # 插入到正确位置if __name__ == '__main__':    myList = [3, 1, 5, 7, 2, 4, 9, 6]    mInsertSort = InsertSort()    mInsertSort.sort(myList)    print myList

以上代码描述了插入排序算法的基本动作,但仔细看会发现这段代码并不能很好的运行:在ele比较插入位置时,若比序列中第一个元素还要小,索引index继续左移很显然逻辑错误。

第二版

class InsertSort(object):      def __init__(self):        return      def sort(self, mlist):        print mlist        for count in xrange(1, len(mlist)):            index = count - 1  # index待比较位置, index+1为待插入位置            ele = mlist[count]  # 复制为哨兵x,即存储待排序元素            if ele > mlist[count - 1]:                print(mlist, ele, count)                continue  # 若第i个元素大于i-1元素,直接插入。              while(ele < mlist[index] and index >= 0):                mlist[index + 1] = mlist[index]  # 若ele小于待插入位置的元素则后移                index = index - 1  # 继续向左比较            mlist[index + 1] = ele  # 插入到正确位置              print(mlist, ele, count)  # 打印每趟排序的结果    if __name__ == '__main__':    myList = [3, 1, 5, 7, 2, 4, 9, 6]    mInsertSort = InsertSort()    mInsertSort.sort(myList)    print myList

在while循环中加入index >= 0的限制index取值,当待插入值比序列第一个元素还要小则插入当前位置.

效率分析

直接插入算法中基本操作(可理解为最内层循环执行的语句)的频度与待排序序列长度N成平方关系,则时间复杂度为O(N^2)。很显然对于这种算法效率并不是很高,下文介绍另一种插入排序算法(希尔排序)能很好提高效率。


希尔排序


算法思想:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量  =1(  <  …<d2<d1),即所有记录放在同一组中进行直接插入排序为止。希尔排序实质是分组插入方法。


示意图


图2 希尔排序


算法实现(Python实现)

class ShellSort(object):

    def __init__(self):        return    def insertSort(self, mlist, div):        for count in xrange(div, len(mlist)):  # 取分组序列            index = count - div  # index待比较位置, index+1为待插入位置            ele = mlist[count]  # 复制为哨兵x,即存储待排序元素            if ele > mlist[count - div]:                print(mlist, ele, count)                continue  # 若第i个元素大于i-1元素,直接插入。            while(ele < mlist[index] and index >= 0):                mlist[index + div] = mlist[index]  # 若ele小于待插入位置的元素则后移                index = index - div  # 继续向左比较            mlist[index + div] = ele  # 插入到正确位置            print(mlist, ele, count)  # 打印每趟排序的结果        return    def shellSort(self, mlist):        for div in xrange(len(mlist), 0, -1):            self.insertSort(mlist, div)        returnif __name__ == '__main__':    myList = [3, 1, 5, 7, 2, 4, 9, 6]    mInsertSort = ShellSort()    mInsertSort.shellSort(myList)    print myList


另一种实现

import randomclass ShellSort(object):    def __init__(self):        return    def insertSort(self, mlist, div):        for head in xrange(0, div):            for count in xrange(head, len(mlist), div):  # 取分组序列                index = count - div  # index待比较位置, index+div为待插入位置                ele = mlist[count]  # 复制为哨兵x,即存储待排序元素                if ele > mlist[count - div]:                    print(mlist, ele, count)                    continue  # 若第i个元素大于i-div元素,直接插入。                while(ele < mlist[index] and index >= 0):                    mlist[index + div] = mlist[index]  # 若ele小于待插入位置的元素则后移                    index = index - div  # 继续向左比较                mlist[index + div] = ele  # 插入到正确位置                print(mlist, ele, count)  # 打印每趟排序的结果    def shellSort(self, mlist):        for div in xrange((len(mlist) / 2) + 1, 0, -1):            self.insertSort(mlist, div)        returnif __name__ == '__main__':    myList = list()    for count in xrange(100):        myList.append(random.randint(1, 100))    mInsertSort = ShellSort()    mInsertSort.shellSort(myList)    print myList

以上两种代码实现思路相似,只是第二种更接近图片描述的先分组再排序。

效率分析

希尔排序基于插入排序算法, 在此算法基础之上增加了分组特性,提高了效率。希尔排序的时间复杂度与增量序列的选取有关,例如希尔增量时间复杂度为O(n²),而Hibbard增量的希尔排序的时间复杂度为O(n^(2/3)),希尔排序时间复杂度的下界是n*log2n。希尔排序没有快速排序算法快 O(n(logn)),因此中等大小规模表现良好,对规模非常大的数据排序不是最优选择,但是比O(n^2 )复杂度的算法快得多。

希尔排序非常容易实现,算法代码短而简单。 此外,希尔算法在最坏的情况下和平均情况下执行效率相差不是很多,与此同时快速排序在最坏的情况下执行的效率会非常差。专家们提倡,几乎任何排序工作在开始时都可以用希尔排序,若在实际使用中证明它不够快,再改成快速排序这样更高级的排序算法. 本质上讲,希尔排序算法是直接插入排序算法的一种改进,减少了其复制的次数,速度要快很多。 原因是,当n值很大时数据项每一趟排序需要的个数很少,但数据项的距离很长。当n值减小时每一趟需要和动的数据增多,此时已经接近于它们排序后的最终位置。 正是这两种情况的结合才使希尔排序效率比插入排序高很多。Shell算法的性能与所选取的分组长度序列有很大关系。只对特定的待排序记录序列,可以准确地估算关键词的比较次数和对象移动次数。想要弄清关键词比较次数和记录移动次数与增量选择之间的关系,并给出完整的数学分析,至今仍然是数学难题。(摘自百度百科)



选择排序


直接选择排序


算法思想

在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。


示意图


图3 简单选择排序


算法实现(Python实现)

简单插入排序

class selectSort(object):    def __init__(self):        return    def sort(self, data):        """选择排序 """        for sortCount in range(len(data)):            min = sortCount  # 待排序序列最小值索引            for index in range(sortCount, len(data)):  # 获取待排序序列最小值索引                if data[index] < data[min]:                    min = index            data[sortCount], data[min] = data[min], data[sortCount]            print '第%s趟排序' %sortCount, dataif __name__ == '__main__':    data = [94, 13, 83, 23, 65, 63, 15, 27, 48, 39]    selectSort = selectSort()    selectSort.sort(data)


简单选择排序,每趟循环定位待排序序列最小值索引。我们可以考虑改进为每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。改进后对n个数据进行排序,最多只需进行[n/2]趟循环即可

二元选择排序

class selectSort(object):    def __init__(self):        return    # 简单选择排序    def sort1(self, data):        """选择排序 """        for sortCount in xrange(len(data)):            min = sortCount  # 待排序序列最小值索引            for index in xrange(sortCount, len(data)):  # 获取待排序序列最小值索引                if data[index] < data[min]:                    min = index            data[sortCount], data[min] = data[min], data[sortCount]            print '第%s趟排序' %sortCount, data    # 二元选择排序    def sort(self, data):        for sortCount in xrange(len(data) / 2):            min = sortCount  # 待排序序列最小值索引            max = len(data) - sortCount - 1  # 待排序序列最大值索引            for index in xrange(sortCount, len(data) - sortCount):  # 获取待排序序列最小值索引                if data[index] < data[min]:                    min = index                    continue                if data[index] > data[max]:                    max = index            # 这里不能直接交换,因为交换会改变数组值造成索引指向偏离预期的值            # 错误的做法#             data[sortCount], data[min] = data[min], data[sortCount]#             data[len(data) - sortCount - 1], data[max], = data[max], data[len(data) - sortCount - 1]            # 正确的做法            data[sortCount], data[min] = data[min], data[sortCount]            if max == sortCount:                max = min            data[len(data) - sortCount - 1], data[max], = data[max], data[len(data) - sortCount - 1]            print '第%s趟排序' %sortCount, dataif __name__ == '__main__':    data = [94, 13, 83, 23, 65, 63, 15, 27, 48, 39]    selectSort = selectSort()    selectSort.sort(data)


效率分析

直接选择排序和二元选择排序时间复杂度为O(N^2),下文介绍简单选择排序改进算法“堆排序”。

待更新

0 0