4排序与查找(上)——数据结构与算法Python版学习笔记

顺序查找Sequential Search

对于无序表

def sequentialSearch(alist,item):
    pos = 0
    found = False
    while pos < len(alist) and not found:
        if alist[pos] == item:
            found = True
        else:
            pos = pos + 1#下标顺序增长
    return found
testlist = [1,2,32,8,17,19,42,13,0]
print(sequentialSearch(testlist,3))
print(sequentialSearch(testlist,13))

终于开始用pycharm了真香😊在这里插入图片描述
对于有序表,当数据项不存在时比对可以提前结束
在这里插入图片描述

二分查找算法及分析

有序法依然改变不了数量级
与中间项比对,每次都会将比对范围缩小一半

def binarySearch(alist,item):
    first = 0
    last = len(alist)-1
    found = False
    while first<=last and not found:
        midpoint = (first + last)//2
        if alist[midpoint] == item:
            found = True
        else:
            if item < alist[midpoint]:
                last = midpoint - 1
            else:
                first = midpoint + 1
    return found
testlist = [0,1,2,8,13,17,19,32,42]
print(binarySearch(testlist,3))
print(binarySearch(testlist,13))

在这里插入图片描述
递归

def binarySearch(alist,item):
    if len(alist) == 0:
        return False
    else:
        midpoint = len(alist)//2
        if alist[midpoint] == item:
            return True
        else:
            if item<alist[midpoint]:
                return binarySearch(alist[:midpoint],item)
            else:
                return binarySearch(alist[midpoint+1:],item)
testlist = [0,1,2,8,13,17,19,32,42]
print(binarySearch(testlist,3))
print(binarySearch(testlist,13))

如果把第一个条件结构去掉
基本结束条件就没有路径能返回False
或者可以把len(alist)==1时item是不是等于列表当中的唯一项
在这里插入图片描述
算法分析:
当比对次数足够多以后,比对范围内就会仅剩余1个数据项
在这里插入图片描述
所以二分法查找的算法复杂的是O(log n)但,
这个递归调用使用了列表切片,而切片操作的复杂度是O(k)
总的来说是优于顺序查找的
但,
还要考虑对数据项进行排序的开销
所以,适合于排序一次查找多次的情况

冒泡排序Bubble Sort

冒泡排序的算法思路在于对无序表进行多趟比较交换,每趟包括了多次两两相邻比较,并将逆序的数据项互换位置,最终能将本趟的最大项就位
经过N-1趟比较交换,实现整表排序
每趟的过程类似于“气泡”在水中不断上浮到水面的经过

def bubbleSort(alist):
    for passnum in range(len(alist)-1,0,-1):
        for i in range(passnum):
            if alist[i]>alist[i+1]:
                temp = alist[i]
                alist[i] = alist[i+1]
                alist[i+1] = temp
                #其实python里面可以作直接交换alist[i],alist[i+1]=alist[i+1],alist[i]
alist = [54,26,93,17,77,31,44,55,20]
bubbleSort(alist)
print(alist)

算法分析:
比对的次数是固定的是1~n-1的累加
在这里插入图片描述
交换的时间复杂度也是O(n^2)最差就是每次比对都要交换,交换次数就等于比对次数

冒泡排序差在哪?大部分比对和交换的操作是无效的。
但有一点优势,无需任何额外的存储空间开销。

性能改进:监测每趟比对是否发生过交换,如果某趟比对没有发生任何交换,说明列表已经排好序,可以提前结束算法

def shortBubbleSort(alist):
    exchanges = True
    passnum = len(alist)-1
    while passnum > 0 and exchanges:
        exchanges = False
        for i in range(passnum):
            if alist[i]>alist[i+1]:
                exchanges = True
                temp = alist[i]
                alist[i] = alist[i+1]
                alist[i+1] = temp
        passnum = passnum-1
alist=[20,30,40,90,50,60,70,80,100,110]
shortBubbleSort(alist)
print(alist)

选择排序Selection Sort

选择排序对冒泡排序进行了改进,每趟仅进行1次交换,记录最大项所在位置,最后再跟本趟最后一项交换O(n)

def selectionSort(alist):
    for fillslot in range(len(alist)-1,0,-1):
        positionOfMax=0
        for location in range(1,fillslot+1):
            if alist[location]>alist[positionOfMax]:
                positionOfMax = location
        temp = alist[fillslot]
        alist[fillslot] = alist[positionOfMax]
        alist[positionOfMax] = temp
alist=[20,30,40,90,50,60,70,80,100,110]
selectionSort(alist)
print(alist)

插入排序Insertion Sort

时间复杂度仍是O(n^2),但算法思路不同

插入排序维持一个已排好序的子列表,其位置始终在列表的前部,然后逐步扩大这个子列表直到全表

第一趟,子列表仅包含第1和数据项,将第二个数据项作为“新项”插入到子列表的合适位置中,这样已排序的子列表就包含了2个数据项
第二趟,再继续将第3个数据项跟前2个数据项比对,并移动比自身大的数据项,空出位置来,以便加入到子列表中
经过n-1趟比对和插入,子列表扩展到全表,完成排序

分析:最差的情况是每趟都与子列表的所有项进行比对,总比对次数与冒泡排序相同,数量级仍是O(n^2)
思路:比对,移动所有比“新项”大的数据项

def insertionSort(alist):
    for index in range(1,len(alist)):
        currentvalue = alist[index]
        position = index
        while position>0 and alist[position-1]>currentvalue:
            alist[position]=alist[position-1]
            position = position-1
        alist[position]=currentvalue
alist=[20,30,40,90,50,60,70,80,100,110]
insertionSort(alist)
print(alist)

由于移动操作仅包含1次赋值,是交换操作的1/3,所以插入排序性能会较好一些

谢尔排序Shell Sort

列表越接近有序,插入排序的比对次数就越少
谢尔排序以排序作为基础,对无序表进行“间隔”划分子列表,每个子列表都执行插入排序

def shellSort(alist):
    sublistcount = len(alist)//2
    while sublistcount > 0:
        for startposition in range(sublistcount):
            gapInsertionSort(alist,startposition,sublistcount)
        print("After increments of size",sublistcount,
              "The list is",alist)
        sublistcount = sublistcount // 2
def gapInsertionSort(alist,start,gap):
    for i in range(start+gap,len(alist),gap):
        currentvalue = alist[i]
        position = i
        while position>=gap and alist[position-gap]>currentvalue:
            alist[position]=alist[position-gap]
            position = position-gap
        alist[position]=currentvalue
alist=[20,30,40,90,50,60,70,80,100,110]
shellSort(alist)
print(alist)

归并排序Merge Sort

def merge_sort(lst):
    #递归结束条件
    if len(lst) <= 1:
        return lst
    #分解问题,并递归调用
    middle = len(lst) // 2
    left = merge_sort(lst[:middle])#左半部排好序
    right = merge_sort(lst[middle:])#右半部排好序
    #合并左右半部,完成排序
    merged = []
    while left and right:
        if left[0] <= right[0]:
            merged.append(left.pop(0))
        else:
            merged.append(right.pop(0))
    merged.extend(right if right else left)
    return merged

分裂过程O(log n)
归并过程O(n)

快速排序算法Quick Sort

思路:依据一个“中值”数据项来把数据表分为两半:小于中值的一半和大于中值的一半,然后每部分分别进行快速排序(递归)

递归三要素

基本结束条件:数据表仅有1各数据项,自然是好排序的
缩小规模:根据”中值“,将数据表分为两半,最好情况是相等规模的两半
调用自身:将两半分别调用自身进行排序(排序基本操作在分裂过程中)

分裂数据表的目标:找到”中值“位置
分裂数据表的手段:

设置左右标
左标向右,右标向左移动

  • 左标一直向右移动,碰到比中值大的就停止
  • 右标一直向左移动,碰到比中值小的就停止
    然后把左右标所指的数据项交换

继续移动,直到左标移到右标的右侧,停止移动
这时右标所指位置就是”中值“应处的位置
将中值和这个位置交换
分裂完成,左半部比中值小,右半部比中值大

def quickSort(alist):
    quickSortHelper(alist,0,len(alist)-1)
def quickSortHelper(alist,first,last):
#基本结束条件
    if first<last:
        splitpoint = partition(alist,first,last)#分裂
        quickSortHelper(alist,first,splitpoint-1)
        quickSortHelper(alist,splitpoint+1,last)#递归调用
def partition(alist,first,last):#选定"中值"
    pivotvalue = alist[first]
    leftmark = first+1
    rightmark = last#左右标初值
    done = False
    while not done:
        while leftmark <= rightmark and \
                alist[leftmark] <= pivotvalue:#向右移动左标
            leftmark = leftmark + 1
        while alist[rightmark] >= pivotvalue and \
                rightmark >=leftmark:#向左移动右标
            rightmark = rightmark - 1
        if rightmark < leftmark:#两标向错就结束移动
            done = True
        else:#左右标的值交换
            temp = alist[leftmark]
            alist[leftmark] = alist[rightmark]
            alist[rightmark] = temp
    temp = alist[first]
    alist[first] = alist[rightmark]
    alist[rightmark] = temp#中值就位
    return rightmark#中值点,也是分裂点
alist=[54,26,93,17,77,31,44,55,20]
quickSort(alist)
print(alist)

分析:
快速排序过程分为两部分:分裂和移动
如果分裂总能把数据表分为相等的两部分,那么就是O(log n)的复杂度
而移动需要将每项都与中值进行比对,还是O(n)
综合起来就是O(nlog n )
而且,算法运行过程中不需要额外的存储空间
但是,如果中值所在的分裂点过于偏离中部造成左右两部分数量不平衡
极端情况,有一部分没有数据,这样时间复杂度就退化到O(n^2)
还要加上递归调用的开销(比冒泡排序还要糟糕)

改进:
适当改进中值的选取方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值