数据结构--python 第八章 排序

随着信息科技的普及与全球国际化的影响,企业拥有的数据量成倍数地增长。无论是庞大的商业应用软件,还是个人的文字处理软件,每项工作的核心都与数据库有莫大的关系,而数据库中最常见且重要的功能就是排序和查找。

排序(sorting)是指将一组数组按照特定的规则调换位置,使得数据具有某种顺序关系(递增或递减)。例如数据库内可针对某一字段进行排序,而此字段被称为键(key),字段里的值就被称为键值(key value).

8.1 排序简介:

在排序过程中,根据数据的移动方式,可将排序方式分为:直接移动和逻辑移动两种方式。

直接移动:是指直接交换存储数据的位置;缺点是:直接移动会浪费许多时间进行数据的移动。

逻辑移动并不会移动数据存储的位置,仅仅改变指向这些数据的辅助功能指针的值;优点是:只要改变辅助指针指向的位置就能达到排序的目的。

8.1.1 排序的分类 

排序可以按照执行时所使用的内存种类区分为以下两种方式:

1)内部排序:排序数量小,可以全部加载到内存中进行排序。

2)外部排序:数据量大,无法全部一次性加载到内存中进行排序,而必须借助辅助存储器(如硬盘)进行排序。

8.1.2 排序算法的分析

1)算法稳定与否?

稳定的排序是指在经过排序后,两个相同键值的记录仍然保持原来的次序。即同一数字在排序前后的相对位置不会发生改变。

2)时间复杂度?Time Complexity

当数据量较大时,排序算法所花费的时间就显得尤为重要。排序算法的时间复杂度分为:最好情况(Best Case)、平均情况(Average Case)和最差情况(Worst Case).

最好情况就是值原本的数列已经是经过排序之后的结果;最差结果是数列中的每一个值都需要重新排列。

3)空间复杂度?Space Complexity

空间复杂度是指算法在执行过程中所需要占用的额外内存空间。任何排序算法都有数据的对调操作,数据对调就会暂时用到一个额外的空间,这也是排序中空间复杂度要考虑的问题。排序算法占用的额外空间越小,空间复杂度就越低。

下仅仅介绍内部排序算法(数据量小,可一次性全部加载到内存中)。

8.2 内部排序法(8种)

排序算法算得上数据结构这门学科的精髓所在,每一种排序算法都有其适应的情况与数据种类。

排序算法名称稳定性时间复杂度空间复杂度
冒泡排序(Bubble Sort)稳定好:O(n) ;差:O(n^{2}o(1)
选择排序(Selection Sort)不稳定\frac{n\times (n-1)}{2} = O(n^{2})o(1)
插入排序(Insertion Sort)稳定好:O(n) ;差:\frac{n\times (n-1)}{2} = O(n^{2})o(1)
希尔排序(Shell Sort)稳定O(n^{\frac{3}{2}})o(1)
合并排序(Merge Sort)稳定O(nlogn)o(n)
快速排序(Quick Sort)不稳定好:O(nlog_{2}n);差:O(n^{2})好:o(log_{2}n);差:o(n):平均运行时间最快
堆积排序(Heap Sort)不稳定O(nlogn)o(1)
基数排序(Radix Sort)稳定o(nlog_{p}k)o(n*p)

最后一个基数排序中,k是指的原始数据中的最大值;n是指的原始数据个数;p是指的数据字符数(例如最大是是三位数,则p=3)。

排序算法:冒泡、选择、插入、希尔(group)、合并、快速(中间,一分为二)、堆积(堆积树)、基数(个十百位)

不稳定的排序算法:选择、快速、堆积

8.2.1 冒泡排序法(稳定排序法):

冒泡排序法又被称为交换排序法,是从观察水中气泡变化构思而成的,原理是从第一个元素开始,比较相邻元素的大小,如大小顺序有误,则对调后再进行下一个元素的比较,就仿佛气泡逐渐从水底冒升到水面一样。如此扫描过一次之后,能确保最后一个元素是最大值,则下一次进行比较的时候,可以忽略最后一个位置的元素;第二次从第一个元素开始,先与第二个元素进行比较,逐步向后,直到与倒数第二个元素进行比较。
冒泡法分析

1)最坏情况和平均情况均需要比较(n-1)+(n-2)+……+3+2+1 = n*(n-1)/2次,时间复杂度为O(n^{2});最好的情况时只需要完成一次扫描,发现没有执行数据的交换操作,就表示已经排序完成,仅仅操作了n-1次比较操作,时间复杂度o(n)

2) 由于冒泡排序是相邻两个数据相互比较和对调,并不会改变原本排列的顺序,因此是稳定的排序算法。

3)只需要一个额外的空间,所以空间复杂度最佳O(1)

4)此排序法适用于数据量小或有部分数据已经排过序的情况。‘

例子:设计一个python程序,并使用冒泡排序算法对以下的数列进行排序。

# CH08-01.PY
data=[16,25,39,27,12,8,45,63]	# 原始数据 
print('冒泡排序法:原始数据为:')
for i in range(8):
    print('%3d' %data[i],end='')
print()

for i in range(7,-1,-1): #扫描次数
    for j in range(i):
        if data[j]>data[j+1]:#比较,交换的次数
            data[j],data[j+1]=data[j+1],data[j]#比较相邻的两数,如果第一个数较大则交换
    print('第 %d 次排序后的结果是:' %(8-i),end='') #把各次扫描后的结果打印出来
    for j in range(8):
        print('%3d' %data[j],end='')
    print()
	
print('排序后的结果为:')
for j in range(8):
    print('%3d' %data[j],end='')
print()

# 结果
冒泡排序法:原始数据为:
 16 25 39 27 12  8 45 63
第 1 次排序后的结果是: 16 25 27 12  8 39 45 63
第 2 次排序后的结果是: 16 25 12  8 27 39 45 63
第 3 次排序后的结果是: 16 12  8 25 27 39 45 63
第 4 次排序后的结果是: 12  8 16 25 27 39 45 63
第 5 次排序后的结果是:  8 12 16 25 27 39 45 63
第 6 次排序后的结果是:  8 12 16 25 27 39 45 63
第 7 次排序后的结果是:  8 12 16 25 27 39 45 63
第 8 次排序后的结果是:  8 12 16 25 27 39 45 63
排序后的结果为:
  8 12 16 25 27 39 45 63

发现该算法无论如何都一定会执行n*(n-1)/2次运算,希望设置一个岗哨,可以通过判断提前中断程序。

例子:设计一个Python程序,使用岗哨的概念,可以提前中断程序,又可以得到正确的排序结果,以此来提高程序执行的效率。

# CH08-02.PY
#[示范]:改进的冒泡排序法
def showdata(data):    #使用循环打印数据
    for i in range(6):
        print('%3d' %data[i],end='')
    print()
    
def bubble (data):
    for i in range(5,-1,-1):
        flag=0    #flag用来判断是否执行了交换操作
        for j in range(i):
            if data[j+1]<data[j]:
                data[j],data[j+1]=data[j+1],data[j]
                flag+=1  #如果执行过交换操作,则flag不为0
        if flag==0:
            break
        #当执行完一次扫描就判断是否执行过交换操作,如果没有交换过数据,
	    #表示此时数组已完成排序,故可直接跳出循环 
        print('第 %d 次排序:' %(6-i),end='')
        for j in range(6):
            print('%3d' %data[j],end='')
        print()
    print('排序后的结果为:',end='')
    showdata (data)

def main():
    data=[4,6,2,7,8,9]  #原始数据
    print('改进的冒泡排序法测试用的原始数据为:')
    bubble (data)
    
main()

# 结果
改进的冒泡排序法测试用的原始数据为:
第 1 次排序:  4  2  6  7  8  9
第 2 次排序:  2  4  6  7  8  9
排序后的结果为:  2  4  6  7  8  9

8.2.2 选择排序法(不稳定排序法):

选择排序法可以使用两种方式进行排序:即在所有的数据中,

当从大到小排序时,将最大值放在第一个位置;

当从小到大排序时,将最小值放在第一个位置;。

举例说明从小到大的选择排序:

1)首先找到此数列中的最小值与第一个值进行交换,

2)从第二个值开始找,找到数列中(不包含第一个)的最小值,再和第二个值进行交换

3)从第三个值开始找,找到数列中(不包含第一个、第二个)的最小值,再和第三个值进行交换

4)从第四个值开始找,找到数列中(不包含第一个、第二个、第三个)的最小值,再和第四个值进行交换。

……直到抵达最后一个值,即完成了从小到大的排序。

选择排序法的分析:

1)无论是最坏情况、最佳情况还是平均情况都需要找到最大值(最小值),因此其比较次数为(n-1)+(n-2)+……+3+2+1 = n*(n-1)/2,时间复杂度为O(n^2)

2)由于选择排序是以最大或最小值直接与最前方未排序的键值进行交换,数据排列顺序很有可能被改变,故不是稳定排序算法

3)只需要一个额外的空间,所以空间复杂度最佳

4)该排序算法适用于数据量小或有部分数据已经经过排序了。

例子:设计一个Python程序,并使用选择排序法对以下的数列进行排序。

# CH08-03.PY
def showdata (data):
    for i in range(8):
        print('%3d' %data[i],end='')
    print()

def select (data):
    for i in range(7):
        for j in range(i+1,8):
            if data[i]>data[j]: #比较第i个和第j个元素
                data[i],data[j]=data[j],data[i]
    print()

data=[16,25,39,27,12,8,45,63]
print('原始数据为:')
for i in range(8):
    print('%3d' %data[i],end='')
print('\n-------------------------------------')
select(data)
print("排序后的数据为:")
for i in range(8):
    print('%3d' %data[i],end='')
print('')

# 结果
原始数据为:
 16 25 39 27 12  8 45 63
-------------------------------------

排序后的数据为:
  8 12 16 25 27 39 45 63

8.2.3 插入排序

插入排序是将数组中的元素逐一与已经排好序的数据进行比较,前两个元素先排好,再将第三个元素插入适当的位置,所以这三个元素仍然是已经排好序的;接着将第四个元素加入,重复此步骤,直到排序完成为止。

思考:55、23、87、62、16的插入排序法的实现(从小到大)

插入排序法的分析:

1)最坏情况和平均情况均需要比较(n-1) + (n-2) +……+3+2+1 = n*(n-1)/2,时间复杂度为o(n^2),最好情况的时间复杂度为o(n)

2)插入排序法是稳定的排序算法

3)只需要一个额外的内存空间,所以空间复杂度是最佳的

4)此排序适用于大部分数据已经排过序或已排数据新增数据后进行排序的情况。

5)插入排序会造成数据大量搬移,所以建议在链表上使用

例子:设计一个python程序,并使用插入排序法对以下的数列进行排序。16、25、39、27、12、8、45、63

# CH08-04.py
SIZE=8        #定义数组大小
def showdata(data):
    for i in range(SIZE):
        print('%3d' %data[i],end='')   #打印数组数据
    print()
    
def insert(data):
    for i in range(1,SIZE):
        tmp=data[i] #tmp用来暂存数据
        no=i-1
        while no>=0 and tmp<data[no]:
            data[no+1]=data[no]	#就把所有元素往后推一个位置
            no-=1
        data[no+1]=tmp #最小的元素放到第一个位置

def main():
    data=[16,25,39,27,12,8,45,63]
    print('原始数组是:')
    showdata(data)
    insert(data)
    print('排序后的数组是:')
    showdata(data)

main()
# 结果:
原始数组是:
 16 25 39 27 12  8 45 63
排序后的数组是:
  8 12 16 25 27 39 45 63

、8.2.4 希尔排序法(group)

在原始记录的键值大部分已经排好序的情况下,插入排序会非常有效。因为它不需要执行太多的数据的搬移操作。希尔排序算法可以减少插入排序算法中数据搬移的次数,以加速排序的进行。排序的原则是将数据区分成特定间隔的几个小区块,以插入排序法排完区块内的数据后再渐渐减少间隔的距离。

例如:63、92、27、36、45、71、58、7

首先将其划分成(8/2)4个group:

63 92 27 36

45 71 58 7

竖列进行排序,然后再写成一维数组:45 71 27 7 63 92 58 36

继续将其划分成(8/2/2)2个group:

45 71

27 7

63 92

58 36

竖列进行排序,然后再写成一维数组:27 7 45 36 58 71 63 92

最后将其划分成(8/2/2/2)1个group,进行排序,即可得到最后排好序的数列:7 27 36 45 58 63 71 92

希尔排序法的分析:

1)任何情况的时间复杂度均为O(n^{\frac{3}{2}})

2)希尔排序算法和插入排序一样,都是稳定排序算法

3)只需要一个额外空间,所以空间复杂度是最佳的

4)此排序法适用于数据大部分已经排好序的情况

例子:设计一个python程序,并使用希尔排序法来对以下的数列排序:16、25、39、27、12、8、45、63

# CH08-05.py
SIZE=8

def showdata(data):
    for i in range(SIZE):
        print('%3d' %data[i],end='')
    print()

def shell(data,size):
    k=1 #k打印计数
    jmp=size//2
    while jmp != 0:
        for i in range(jmp, size):  #i为扫描次数 jmp 为设置间距的位移量
            tmp=data[i]  #tmp用来暂存数据
            j=i-jmp      #以j来定位比较的元素
            while tmp<data[j] and j>=0:  #插入排序法
                data[j+jmp] = data[j]
                j=j-jmp
            data[jmp+j]=tmp
        print('第 %d 次排序过程:' %k,end='')  
        k+=1
        showdata (data)
        print('-----------------------------------------')
        jmp=jmp//2    #控制循环数

def main():
    data=[16,25,39,27,12,8,45,63]
    print('原始数组是:     ')	
    showdata (data)
    print('-----------------------------------------')
    shell(data,SIZE)

main()

# 结果
原始数组是:     
 16 25 39 27 12  8 45 63
-----------------------------------------
第 1 次排序过程: 12  8 39 27 16 25 45 63
-----------------------------------------
第 2 次排序过程: 12  8 16 25 39 27 45 63
-----------------------------------------
第 3 次排序过程:  8 12 16 25 27 39 45 63
-----------------------------------------


8.2.5 合并排序法;

合并排序法的工作原理是针对已排好序的两个或两个以上的数列(或数据文件),通过合并的方式将其组合成一个大的且已经排好序的数列(或数据文件),步骤如下:
1)将N个长度为1的键值成对地合并成N/2个长度为2的键值组

2)将N/2个长度为2的键值组成对地合并成N/4个长度为4的键值组

3)将键值组不断地组合,直到合并成一组长度为N的键值组为止。

更详细的步骤解释:

1)将N个长度为1的数列合并成N/2个已排序妥当且长度为2的数列

2)将N/2个长度为2的数列合并成N/4个已排序妥当且长度为4的数列

3)将N/4个长度为4的数列合并成N/8个已排序妥当且长度为8的数列

4)将N/2^(i-1)个长度为2^(i-1)的数列合并成N/2^i个已排序妥当且长度为N/2^i的数列

合并排序算法的分析:

1)使用合并排序法,n项数据一般需要约log2n次处理,每次处理的时间复杂度为O(n),所以合并排序算法的最佳情况、最差情况以及平均情况的复杂度均为O(nlogn)

2)由于在排序过程中需要一个与数列(或数据文件)大小同样的额外空间,故其空间复杂度为O(n)

3)是一个稳定的排序算法

例子:设计一个python程序,实现两个数列list1:20 45 51 88 和list2: 10 15 23 98的合并排序。(较为复杂)

# CH08-06.py
# 合并排序法(Merge Sort)

#99999为数列1的结束数字不列入排序
list1 = [20,45,51,88,99999]
#99999为数列2的结束数字不列入排序
list2 = [98,10,23,15,99999] 
list3 = []

def merge_sort():
    global list1
    global list2
    global list3
        
    # 先使用选择排序将两个数列排序,再进行合并
    select_sort(list1, len(list1)-1)
    select_sort(list2, len(list2)-1)

    
    print('\n第1个数列的排序结果为: ', end = '')
    for i in range(len(list1)-1):
        print(list1[i], ' ', end = '')

    print('\n第2个数列的排序结果为: ', end = '')
    for i in range(len(list2)-1):
        print(list2[i], ' ', end = '')
    print()

    for i in range(60):
        print('=', end = '')
    print()

    My_Merge(len(list1)-1, len(list2)-1)

    for i in range(60):
        print('=', end = '')
    print()

    print('\n合并排序法的最终结果为: ', end = '')
    for i in range(len(list1)+len(list2)-2):
        print('%d ' % list3[i], end = '')
        
def select_sort(data, size):
    for base in range(size-1):
        small = base
        for j in range(base+1, size):
            if data[j] < data[small]:
                small = j
        data[small], data[base] = data[base], data[small]

def My_Merge(size1, size2):
    global list1
    global list2
    global list3

    index1 = 0
    index2 = 0
    for index3 in range(len(list1)+len(list2)-2):
        if list1[index1] < list2[index2]: # 比较两个数列,数小的先存于合并后的数列
            list3.append(list1[index1])
            index1 += 1
            print('此数字%d取自于第1个数列' % list3[index3])
        else:
            list3.append(list2[index2])
            index2 += 1
            print('此数字%d取自于第2个数列' % list3[index3])
        print('目前的合并排序结果为: ', end = '')
        for i in range(index3+1):
            print(list3[i], ' ', end = '')
        print('\n')

#主程序开始

merge_sort()  #调用所定义的合并排序法函数

# 结果:
第1个数列的排序结果为: 20  45  51  88  
第2个数列的排序结果为: 10  15  23  98  
============================================================
此数字10取自于第2个数列
目前的合并排序结果为: 10  

此数字15取自于第2个数列
目前的合并排序结果为: 10  15  

此数字20取自于第1个数列
目前的合并排序结果为: 10  15  20  

此数字23取自于第2个数列
目前的合并排序结果为: 10  15  20  23  

此数字45取自于第1个数列
目前的合并排序结果为: 10  15  20  23  45  

此数字51取自于第1个数列
目前的合并排序结果为: 10  15  20  23  45  51  

此数字88取自于第1个数列
目前的合并排序结果为: 10  15  20  23  45  51  88  

此数字98取自于第2个数列
目前的合并排序结果为: 10  15  20  23  45  51  88  98  

============================================================

合并排序法的最终结果为: 10 15 20 23 45 51 88 98 

8.2.6 快速排序算法(不稳定算法)

快速排序法又被称为分割交换排序法,是目前公认最佳的排序法。也是“分而治之”思想的应用。先会在数据中找到一个虚拟的中间值,并按此中间值将所有打算排序的数据分为两部分,其中小于中间值的数据放在左边,大于中间值的数据放在右边,再以同样的方式分别处理左右两边的数据,直到排完序为止。

快速排序法的分析:

1)在最快和平均情况下,时间复杂度为o(nlog2n).最坏的情况就是每次挑选的中间值不是最大就是最小,因而最坏情况的时间复杂度为o(n^2)

2)快速排序法不是稳定的排序算法

3)在最差的情况下,空间复杂度是o(n),而最佳情况是o(log2n)

4)快速排序法是平均运行时间最快的排序法。

例子:设计一个python程序,并使用快速排序法对速记产生的数列进行排序。

# CH08-07.PY
import random

def inputarr(data,size):
    for i in range(size):
        data[i]=random.randint(1,100)
        
def showdata(data,size):
    for i in range(size):
        print('%3d' %data[i],end='')
    print()

def quick(d,size,lf,rg):
    #第一项键值为d[lf]
    if lf<rg:  #排序数列的左边与右边
        lf_idx=lf+1
        while d[lf_idx]<d[lf]:
            if lf_idx+1 >size:
                break
            lf_idx +=1
        rg_idx=rg
        while d[rg_idx] >d[lf]:
            rg_idx -=1
        while lf_idx<rg_idx:
            d[lf_idx],d[rg_idx]=d[rg_idx],d[lf_idx]
            lf_idx +=1
            while d[lf_idx]<d[lf]:
                lf_idx +=1
            rg_idx -=1
            while d[rg_idx] >d[lf]:
                rg_idx -=1
        d[lf],d[rg_idx]=d[rg_idx],d[lf]

        for i in range(size):
            print('%3d' %d[i],end='')
        print()
       
        quick(d,size,lf,rg_idx-1)   #以rg_idx为基准点分成左右两半以递归方式
        quick(d,size,rg_idx+1,rg)   #分别为左右两半进行排序直至完成排序               
		
def main():
    data=[0]*100
    size=int(input('请输入数组大小(100以下):'))
    inputarr (data,size)
    print('您输入的原始数据是:')
    showdata (data,size)
    print('排序过程如下:')
    quick(data,size,0,size-1)
    print('最终的排序结果为:')
    showdata(data,size)

main()

# 结果
请输入数组大小(100以下):10
您输入的原始数据是:
 83 14  1 16 63 93 85 19 78 19
排序过程如下:
 19 14  1 16 63 19 78 83 85 93
 19 14  1 16 19 63 78 83 85 93
 16 14  1 19 19 63 78 83 85 93
  1 14 16 19 19 63 78 83 85 93
  1 14 16 19 19 63 78 83 85 93
  1 14 16 19 19 63 78 83 85 93
  1 14 16 19 19 63 78 83 85 93
最终的排序结果为:
  1 14 16 19 19 63 78 83 85 93

8.2.7 堆积排序法(不稳定的排序算法)

堆积排序算法是选择排序算法的改进版本,他可以减少在选择排序中的比较次数,进而减少排序时间。堆积排序用到了二叉树的技巧,它是利用堆积树来完成排序的。堆积树是一种特殊的二叉树,可分为最大堆积树和最小堆积树。

最大堆积树满足如下三个条件:

1)它是一个完全二叉树

2)所有节点都大于或等于它左右子节点的值

3)树根是堆积树中最大的值

最小堆积树满足如下三个条件:

1)它是一个二叉树

2)所有节点都小于或等于它左右子节点的值

3)树根是对堆积树中最小的值

QQ:二叉树转换为最大堆积树:

从树根往下进行比较,大的值就往上排列,知道到达了树叶节点即可。(从二叉树的树根开始从上往下逐一按堆积树建立原则来改变各节点的值,最终得到一棵最大堆积树)

然后根据建立的最大堆积树,从大到小进行提取数据。

首先提取树根作为排好序数组的第一个值,然后在删除了树根的基础上,重新针对剩下的节点建立最大堆积树,然后再将树根放入数组作为第二个值,……

堆积排序法的分析:

1)在所有情况下,时间复杂度均为o(nlogn)

2)堆积排序不是稳定的排序算法

3)只需要一个额外的空间,空间复杂度为o(n)

例子:设计一个python程序,并使用堆积排序法进行排序

# CH08-08.py
def heap(data,size):
    for i in range(int(size/2),0,-1):#建立堆积树节点
        ad_heap(data,i,size-1)
    print()
    print('堆积的内容:',end='')
    for i in range(1,size): #原始堆积树的内容
        print('[%2d] ' %data[i],end='')
    print('\n')
    for i in range(size-2,0,-1): #堆积排序
        data[i+1],data[1]=data[1],data[i+1]#头尾节点交换
        ad_heap(data,1,i)#处理剩余节点
        print('处理过程为:',end='')
        for j in range(1,size):
            print('[%2d] ' %data[j],end='')
        print()

def ad_heap(data,i,size):
    j=2*i
    tmp=data[i]
    post=0
    while j<=size and post==0:
        if j<size:
            if data[j]<data[j+1]: #找出最大节点
                j+=1
        if tmp>=data[j]: #若树根较大,结束比较过程
            post=1
        else:
            data[int(j/2)]=data[j]#若树根较小,则继续比较
            j=2*j
    data[int(j/2)]=tmp #指定树根为父节点

def main():
    data=[0,5,6,4,8,3,2,7,1]	#原始数组的内容
    size=9
    print('原始数组为:',end='')
    for i in range(1,size):
        print('[%2d] ' %data[i],end='')
    heap(data,size) #建立堆积树
    print('排序结果为:',end='')
    for i in range(1,size):
        print('[%2d] ' %data[i],end='')
        
main()

# 结果
原始数组为:[ 5] [ 6] [ 4] [ 8] [ 3] [ 2] [ 7] [ 1] 
堆积的内容:[ 8] [ 6] [ 7] [ 5] [ 3] [ 2] [ 4] [ 1] 

处理过程为:[ 7] [ 6] [ 4] [ 5] [ 3] [ 2] [ 1] [ 8] 
处理过程为:[ 6] [ 5] [ 4] [ 1] [ 3] [ 2] [ 7] [ 8] 
处理过程为:[ 5] [ 3] [ 4] [ 1] [ 2] [ 6] [ 7] [ 8] 
处理过程为:[ 4] [ 3] [ 2] [ 1] [ 5] [ 6] [ 7] [ 8] 
处理过程为:[ 3] [ 1] [ 2] [ 4] [ 5] [ 6] [ 7] [ 8] 
处理过程为:[ 2] [ 1] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] 
处理过程为:[ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] 
排序结果为:[ 1] [ 2] [ 3] [ 4] [ 5] [ 6] [ 7] [ 8] 

8.2.8 基数排序法

基数排序法和之前的冒泡、选择、插入、希尔、合并、快速、堆积排序算法都不相同,它并不需要进行元素之间的比较操作。而是属于一种分配模式的排序方式。

基数排序方法按照比较的方向可分为最高位优先(Most  Significant Digit First,MSD)和最低位优先(Least Significant Digit First,LSD)两种。MSD算法是从最左边的位数开始比较,而LSD是从最右边的位数开始比较。以LSD对三位数的整数数据来加以排序,它是按照个位、十位数、百位数这样的次序进行排序的。(P283)

1)首先每个整数按照个位数字放到列表中  + 从左到右合并后成为:……

2)按照十位数字,将其放到列表中:  + 从左到右合并后成为 ……

3)按照百位数字,按序放到列表中   + 最后合并即可完成最后的排序

 

基数排序法分析:

1)在所有的情况下,时间复杂度为o(nlog_{p}k),k是原数数据的最大值

2)基数排序算法是稳定的算法

3)基数排序算法会使用到很大的额外空间来存放列表数据,其空间复杂度为o(n*p),n是原始数据的个数,P是数据字符数(百位数即为3)。

4)若n很大,p固定或很小,则此排序法将会很有效率。

例子:设计一个python程序,并使用基数排序法来进行排序。

# CH08-09.PY
# 基数排序法:从小到大排序
import random

def inputarr(data,size):
    for i in range(size):
        data[i]=random.randint(0,999) #设置 data 值最大为 3 位数

def showdata(data,size):
    for i in range(size):
        print('%5d' %data[i],end='')
    print()

def radix(data,size): 
     n=1 #n为基数,从个位数开始排序
     while n<=100:
        tmp=[[0]*100 for row in range(10)] # 设置暂存数组,[0~9位数][数据个数],所有内容均为0
        for i in range(size):    # 对比所有数据
            m=(data[i]//n)%10    # m为 n 位数的值,如 36 取十位数(36/10)%10=3
            tmp[m][i]=data[i]    # 把 data[i] 的值暂存在 tmp 中
        k=0
        for i in range(10):
            for j in range(size):
                if tmp[i][j] != 0:    # 因为一开始设置 tmp ={0},故不为 0 者即为
                    data[k]=tmp[i][j] # data 暂存在 tmp 中的值,把 tmp 中的值放
                    k+=1              # 回 data[ ]里
        print('经过%3d位数排序后:' %n,end='')
        showdata(data,size)
        n=10*n

def main():
    data=[0]*100
    size=int(input('请输入数组大小(100以下):'))
    print('您输入的原始数据是:')
    inputarr (data,size)
    showdata (data,size)
    radix (data,size)

main()

# 结果
请输入数组大小(100以下):10
您输入的原始数据是:
  190  814  205  550  862  831  461  495  452  819
经过  1位数排序后:  190  550  831  461  862  452  814  205  495  819
经过 10位数排序后:  205  814  819  831  550  452  461  862  190  495
经过100位数排序后:  190  205  452  461  495  550  814  819  831  862

关于计算机中常用的排序算法,也可以参见我的另一篇博客:

七种经典的排序算法

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值