学习笔记:数据结构与算法Python版[搜索和排序-上]

 顺序查找

存储于列表集合中的数据项彼此在线性或顺序关系,每个数据项的位置与其他数据项相关。
在python列表中,数据项的位置就是它的下标。因为下标是有序的,所以能够顺序访问,由此可以进行顺序搜索
 
 
 
无序列表的顺序搜索代码——复杂度O(n)
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,6,14,19,13,44]
print(sequentialSearch(testlist,14))

 

有序列表的顺序搜索——复杂度O(n)
def sequentialSearch(alist,item):
    pos=0
    found=False
    stop=False
    
    while pos<len(alist) and not found and not stop:
        if alist[pos]==item:
            found=True
        else:
            if alist[pos]>item:
                stop=True
            else:
                pos=pos+1
            
    return found
testlist=[1,2,3,4,14,19,20,44]
print(sequentialSearch(testlist,14))

二分查找

二分搜索与顺序查找不同它不是从第一个元素开始搜索列表,而是从中间元素着手。
如果这个元素就是目标元素,那就立即停止搜索;如果不是,则可以利用列表有序的特性,排除一半元素。
如果目标元素比中间大,就可以直接排除列表的左半部分和中间的元素。这时因为,如果列表包含目标元素,它必定位于右半部分。
eg:
 
有序列表的二分搜索
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[midponit]:
                last=midpoint-1
            else:
                first=midpoint+1
                
    return found
testlist=[1,2,3,4,14,19,20,44]
print(binarySearch(testlist,14))

 

 
二分搜索的递归版本——算法复杂度O(logn)
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)
                
    return found
testlist=[1,2,3,4,14,19,20,44]
print(binarySearch(testlist,14))

 

二分搜索算法分析

在进行二分搜索时,每一次比较都将待考虑元素减半。假设列表共有n个元素,第一次比较后剩下 2/n个元素,第二次4/n 第三次 8/那一次类推。。。。

排序

1

冒泡排序

冒泡排序多次遍历列表,它比较相邻的元素,将不和顺序的交换。每一轮遍历都将下一个最大值放到正确的位置上。本质上,每个元素通过"冒泡"找到自己所属的位置
时间复杂度O(n2)
冒泡排序第一趟
代码:
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
 
 
alist = [54,26,93,17,77,31,44,55,20]
bubbleSort(alist)
print(alist)
            
python允许同时赋值 例如执行 a,b=b,a 相当于同时执行两条赋值语句 
temp=alist[j]
arr[j]=arr[j+1]
arr[j+1]=temp
修改后:
arr[j],arr[j+1]=arr[j+1],arr[j]
冒泡排序通常被认为是效率最低的排序算法,因为在确定最终的位置前必须交换元素。多余  的交换操作代价很大。 
升级版短冒泡排序

 选择排序

选择排序在冒泡排序的基础上做了改进,每次遍历列表时只做一次交换。要实现这一点,选择排序
在每次遍历时寻找最大值,并在遍历完之后将它放到正确位置上。
 
选择排序是一种简单直观的排序算法,无论什么数据进去都是 O(n²) 的时间复杂度。
所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。
 
 
算法步骤:
1.首先在末尾排序序列中找到最小(大)元素,存放排序序列的起始位置。
2.再从剩余未排序元素中继续寻找最小(大)元素,然后放到自己已排序序列的末尾。
3.重复第二布,知道所有元素均排序完。

 

 

代码
def selectionSort(arr):
    for i in range(len(arr)-1):
        minIndex=i
        for j in range(i+1,len(arr)):
           if arr[j]<arr[minIndex]:
               minIndex=j        
        if i!=minIndex:
            arr[i],arr[minIndex]=arr[minIndex],arr[i]
    return arr
 
alist = [54,26,93,17,77,31,44,55,20]
selectionSort(alist)
print(alist)

 

 插入排序

插入排序维持一个已拍好序的子列表,其位置适中在列表的前部,然后逐步扩大这个子列表直到全表。
1.插入排序的比对主要用来寻找"新项"的插入位置
2.最差情况时没糖都与子列表中所有项进行比对,总比对次数与冒泡排序相同,数量级仍是O(n2)
3.最好情况,列表已经排好序的时候,每趟仅需一次比对,总次数是O(n)
算法步骤
1.第一趟,子列表仅包含第一个数据项,将第二个数据项作为"新项"插入到子列表的核实位置中,这样已排序的子列表就包含了2个数据项。
2.第二趟,在继续将第三个数据项跟前两个数据项比对,并移动比自身大的数据项,空出位置来,以便加入到子列表中。
3.经过n-1趟比对和插入,子列表扩展到全表,排序完成。
排序思路
算法代码
def insertionSort(alist):
    for index in range(1,len(alist)):
        #将当前值存到currentvalue中
        currentvalue=alist[index]
        #将当前下标存到position中 这个就是下标指针
        position=index
        
        #循环判断当前下标大于0 和当前数据是否小于前一位数据
        while position>0 and alist[position-1]>currentvalue:
            #如果小于则:
            #将前一位数据赋值给当前数据所在的位置
            alist[position]=alist[position-1]
            #给下标指针-1
            position=position-1
            
        alist[position]=currentvalue
        
alist = [54,26,93,17,77,31,44,55,20]
insertionSort(alist)
print(alist)

算法分析

最坏的情况下比较次数时前n-1个整数之和 对应的时间复杂度时O(n2)

希尔排序

希尔排序也称"递减增量排序",它对插入排序做了改进,将列表分成数个子列表,并对每一个子列表应用插入排序。
算法步骤
这个列表有9个元素。如果增量为3,就有3个子列表
每个都可以应用插入排序。尽管列表仍然不完全有序,但通过给子列表排序,我们已经让元素离它们的最终位置更近了
有了之前的子列表排序,因此移动次数已经减少了,本利只需要再移动4次
 
算法代码
def shellSort(alist):
    #计算步长 每个n/2个来分成若干份
    sublistcount=len(alist)//2
 
 
    #如果步长大于0则执行
    while sublistcount > 0:
        #循环排序每组元素
        for startposition in range(sublistcount):
            #执行插入排序 alist是数据 startposition是开始下标 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 = [54,26,93,17,77,31,44,55,20]
shellSort(alist)
print(alist)

 

算法分析
介于O(n)和O(n2)之

 

 归并排序

归并排序,采用的是分治策略,他是一个递归算法,每次将一个列表一分为二。
如果有列表为空或只有一个元素,那么从定义上来说它就是有序的(基本情况)、如果列表不知一个元素,就将列表一分为二。
并对两部分都递归调用归并排序。
算法步骤
结束条件是 数据表中仅有1个数据项,自然是排好序的
缩小规模:将数据表分裂成相等的凉拌,规模减为原来的二分之一;
调用自身:将两半分别调用自身排序,然后将分别排好序的两半进行归并,得到排好序的数据表
 
算法代码
#将数组元素递归分成两半
#def mergeSort(alist):
#    print("Splitting",alist)
#    if len(alist)>1:
#        mid=len(alist)//2
#        lefthalf=alist[:mid]
#        righthalf=alist[mid:]
#        
#        mergeSort(lefthalf)
#        mergeSort(righthalf)
#        
#        i=j=k=0
#        
#        #将数组元素进行比较归并
#        #判断左边和右边是否有数据
#        while i<len(lefthalf) and j<len(righthalf):
#            #如果左边的数据小于右边的数据则 将左边数据赋值给alist[k] 否则将右边的赋值给
#            #不管是谁谁的自身下标移动+1
#            print(alist[k])
#            if lefthalf[i]<righthalf[j]:
#                alist[k]=lefthalf[i]
#                i=i+1
#            else:
#                alist[k]=righthalf[j]
#                j=j+1
#            k=k+1
#        print('alist:',alist)
#  
#            
#        while i<len(lefthalf):
#            alist[k]=lefthalf[i]
#            i=i+1
#            k=k+1
#        
#        while j<len(righthalf):
#            alist[k]=righthalf[j]
#            j=j+1
#            k=k+1
#    
#   
#alista = [54,26,93,17,77,31,44,55,20]
#mergeSort(alista)
#print(alista)
算法复杂度
分裂过程 O(log n)
归并过程 O(n)
总的时间复杂度O(nlog n)
 
注意:这个算法额外使用了1倍的存储空间用于归并

 快速排序

快速排序算法思路是依据一个 “中值”数据项来把数据表分为两半:小于中值的一般和大于中值的一般,然后每部分分别进行快速排序(递归)
 
如果希望这两班拥有相等的数量的数据项,则应该找到数据表的"中位数"
但找中卫书余姚计算开销!要想没有开销,只能随意找一个数来充当"中值" 比如第一个数
快速排序的递归算法 递归三要素
1.基本结束条件:数据表仅有1个数据项,自然是拍好序的
2.缩小规模:根据"中值",将数据表分为两半,最好情况时相等规模的两半
3.调用自身:将两半分别调动自身进行排序(排序基本操作在分裂过程中)
 
分裂数据表的目标:找到“中值”的位置
分裂数据表的手段
    设置左右标(left/rightmark)
    坐标向右移动,右标向左移动
    继续移动知道左标一道右标的右侧,停止移动
    这时右标所指的位置就是"中值"应处的位置
    将中值这个位置交换
    分裂完成,做颁布比值小,又半步比中值打
示例代码
 
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
 

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)
        
alista = [54,26,93,17,77,31,44,55,20]
quickSort(alista)
print(alista) 

算法分析

O(n)

总结

1

posted @ 2020-05-26 08:57  笑花大王  阅读(351)  评论(0编辑  收藏  举报