(四)排序与搜索

排序与搜索

排序算法(Sorting algorithm)是一种能将一串数据依照特定顺序进行排序的一种算法。

稳定排序:序列中任意两个记录的关键字相同,即ki=kj(i不等于j),若排序之前ki领先于kj,排序后这种关系保持不变的排序称为稳定排序,否则称为不稳定排序。

1. 冒泡排序

冒泡排序(Bubble Sort)算法的运作如下:

  • 比较相邻的元素。如果第一个比第二个大(升序),叫就换他们两个;
  • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完之后,最后的元素会是最大的数;
  • 针对所有的元素重复以上步骤,除了最后一个;
  • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
def bubble_sort(alist):
    """冒泡排序"""
    #最外层排序轮数n-1次
    for j in range(0,len(alist)-1):  
        #最内层是每一轮的比较
        count=0
         for i in range(0,len(alist)-1-j):
        	if alist[i]>alist[i+1]:
            	alist[i],alist[i+1]=alist[i+1],alist[i]
                count +=1
            if 0==count:
               return
            
 if __name__="__main__" :
    li=[4,9,0,8,3,2]
    bubble_sort(li)
    print(li)
  • 时间复杂度
    • 最优时间复杂度:O(n),表示遍历一次发现没有任何可以交换的元素,排序结束
    • 最坏时间复杂度:O(n^2)
    • 稳定性:稳定

2. 选择排序

每次选择最小的数,将之放到数组的最前面

def selection_sort(alist):
    """选择排序"""
    for j in range(0,len(alist)-1): #j: 0~n-2
   		 min_index=j
    	for i in range(1+j,len(alist)):   #j及前面的元素都已经有序
        	if alist[min_index]>alist[i]
           		min_index=i
    	 alist[j],alist[min_index]=alist[min_index],alist[j] 
  • 时间复杂度
    • 最优时间复杂度:O(n^2)
    • 最坏时间复杂度:O(n^2)
    • 稳定性:不稳定(考虑升序每次选择最大的情况)

3.插入排序

选择排序认为左边的序列有序,然后从右边的序列中找到最小值,然后放到左边来,操作的是右边的无序序列;

插入排序是将右边无序序列的第一个元素与左边的序列元素依次比较,看将该元素放到左边序列的哪一个位置上。

def insert_sort(alist):
    """插入排序"""
   #从右边的无序序列中取出多少个元素执行这样的过程
    for j in range(1,len(alist)):  #产生j:1~n-1
    	i=j    #i代表内层循环的起始值
        #执行从右边的无序序列中取出第一个元素,即i位置的元素,然后将其插入到前面的正确位置中
   		 while(i>0):
   			if  alist[i] < alist[i-1]:
       		 	alist[i],alist[i-1]=alist[i-1],alist[i]
        	 	i-=1
        	 else:
            	break
  • 时间复杂度
    • 最优时间复杂度:O(n),(升序排序,序列已经处于升序状态)
    • 最坏时间复杂度:O(n^2)
    • 稳定性:稳定

4.快速排序

快速排序(Quick Sort),又称为划分交换排序,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

步骤为:

  1. 从数列中挑选出一个元素,称为“基准”;
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆放在基准的后面(相同的数可以放到同一边),这个称为分区操作。
  3. 递归地把小于基准值元素的子数列和大于基准值元素的子数列排序。
def quick_sort(alist,first,last):
    """快速排序"""
     #递归算法的终止条件
    if first>=last:   
        return
    mid_value=alist[first]
    low=first
    high=last
   while low<high: 
    	#high 左移
        while low<high and  alist[high]>=mid_value :
                high-=1
        alist[low]=alist[high]
        
		#low 右移
        while low<high and alist[low]<mid_value:
              low+=1
         alist[high]=alist[low]   
   #从循环退出时,low和high相等
   alist[low]=mid_value
    
    #对low左边的列表执行快速排序
	quick_sort(alist,first,low-1)
    #对low右边的列表执行快速排序
	quick_sort(alist,low+1,last)	

  • 时间复杂度

    • 最优时间复杂度O(nlogn)

    • 最坏时间复杂度O(n^2)

    • 稳定性:不稳定

5. 希尔排序

希尔排序(Shell Sort)是插入排序的一种。也称为缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的元素个数越来越多,当增量减至为1时,整个记录恰好被分为一组,算法便终止。

def shell_sort(alist):
    """希尔排序"""
    n=len(alist) #n=9
    gap=n//2     #4
    
    #gap变化到0之前,插入算法执行的次数
    while gap>0:
        #插入算法与普通的插入算法的区别就是gap步长
        for j in range(gap,1,n):
        ##j=gap,gap+1,gap+2,……,n-1
            i=j
            while i>0:
                if alist[i]<alist[i-gap]:
                    alist[i],alist[i-gap]=alist[i-gap],alist[i]
                    i-=gap
                else:
                    break
          #缩短gap的长度          
          gap//=2
  • 时间复杂度

    最坏时间复杂度:O(n^2)

    最优时间复杂度:根据步长序列的不同而不同

    稳定性:不稳定

6.归并排序

归并排序是采用分治法的一个非常典型的应用。归并排序的思想就是先递归分解数组,再合并数组。

将数据分解最小后,然后合并两个有序数组,基本思路是比较两个数组的最前面的数,谁小了就先取谁,取了后相应的指针就往后移一位。然后再比较,直到一个数组为空,最后把另一个数组的剩余部分复制过来即可。

def merge_sort(alist):
    """归并排序"""
    n=len(alist)
    if n<=1:
        return alist
    mid=n//2
    
    #left表示采用归并排序后形成的有序的新列表
    left_li=merge_sort(alist[:mid])
    right_li=merge_sort(alist[mid:])
    
    #将两个有序的子序列合并为一个新的整体
    left_pointer,right_pointer=0,0
    result=[]
    while left_pointer<len(left_li) and right_pointer<len(right_li):
        if left_li[left_pointer]<=right_li[right_pointer]:
            result.append(left_li[left_pointer])
            left_pointer+=1
        else:
             result.append(right_li[right_pointer])
              right_pointer+=1
            
    result +=left_li[left_pointer:]    
    result +=right_li[right_pointer:] 
    return result

  • 时间复杂度

    • 最优时间复杂度:O(nlogn)
    • 最坏时间复杂度:O(nlogn)
    • 稳定性:稳定

    结束后产生一个新的列表,产生空间上的开销

7.堆排序

堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。

8. 常见排序算法效率比较

9. 搜索

搜索是在一个项目集合中找到一个特定项目的算法过程。搜索的几种常见方法:顺序查找、二分法查找、二叉树查找、哈希查找。

二分法查找

二分法查找又称折半查找,优点是比较次数少,查找速度快,平均性能好;缺点是要求待查表为有序表。使用的对象涉及到下标,故只能是顺序表。

def binary_search(alist,item):
    """二分法查找----递归实现"""
    n=len(alist)
    if n>0:
        mid=n//2
        if alist[mid]==item:
            return True
        elif item<alist[mid]:
            return binary_search(alist[:mid],item)  #注意这里并不包含mid,递归的时候产生了一个新的列表
         else:
            return binary_search(alist[mid+1:],item)  
      return False  

def binary_search_2(alist,item):
    """二分法查找----非递归实现"""
    n=len(alist)
    first=0
    last=n-1
    while first<=last:
        mid=(first+last)//2
        if alist[mid]==item:
             return True
        elif alist[mid]>item:
             last=mid-1
         else:
             first=mid+1
    return False
  • 时间复杂度
    • 最优时间复杂度:O(1)
    • 最坏时间复杂度:O(logn)
posted @   日积月累,水滴石穿  阅读(65)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示