牛客网_算法初级班_Lesson2_Part I_数组元素与target比较划分区域_荷兰国旗问题_改进后的经典快排_随机快排_python语言描述

一、数组元素与target比较划分区域

1.问题描述
给定一个target(目标值),依次和数组中的每一个数比较,如果小于或者等于target 就放进数组的左端,否则(也就是大于target),放在数组的右端。也就是把数组分成了两部分

2.思路
引入一个指针变量Less,指针变量左边的数全都是小于等于target,否则都放在指针变量的右边,这样既可完成划分。
步骤:

  1. 若cur(当前时刻的元素取值)≤ target,则cur与小于等于区域外的第一个数交换;然后小于等于区域扩大一个位置(即Less + 1);最后cur + 1
  2. 若cur(当前时刻的元素取值)≥ target,什么也不操作,直接cur + 1

3.python实现

def sort_area(array, L, R, target):
    '''
    问题一:
    给定一个target,依次和数组中的每一个数比较,如果小于或者等于target 就放进数组的左端,
    否则(也就是大于target),放在数组的右端。  也就是把数组分成了两部分
    '''
    cur = L
    Less = L - 1 
    while cur < R:
        if array[cur] <= target:
            array[Less+1], array[cur] = array[cur], array[Less+1]
            Less += 1
            cur += 1
        else:
            cur += 1
                      
    return array[0:Less+1], array[Less+1:]

arr = [3, 4, 6, 1, 8, 0]
less_arr, more_arr = sort_area(arr, 0, len(arr), 4)

print('小于等于域:', less_arr,' 大于域:', more_arr)

二、荷兰国旗问题

1.问题描述
与问题一是同类型的,唯一不同的是这个问题是三个区域,也就是大于target、小于target、等于target

2.思路
相比于问题一,只需要多设置一个区域指针,左边设置为小于区域Less,中间设置为等于区域,右边设置为大于区域More。当cur指针碰到了more指针以后就停止即可(终止条件)。
步骤:

  1. 若cur == num,则不必做操作,cur += 1
  2. 若cur < num ,则cur与小于区域外的第一个数交换;然后小于区域扩大一个(less++,cur++)
  3. 若cur > num, 则cur与大于区域的前一个数交换;大于区域向左扩充一个位置(more++);然后因为是从后面换到前面的,这个数还没有做过比较判断,因此不能让cur++,而是应该保留当前cur然后回去继续比较!!!
  4. 停止条件:cur与more撞上即停止

3.python实现

def Dutch_flag(array, L, R, target):
    '''
    问题二,荷兰国旗问题:
        与问题一是同类型的,唯一不同的是这个问题是三个区域,也就是大于target、小于target、等于target
        相比于问题一,只需要多设置一个区域指针,左边设置为小于区域,中间设置为等于区域,右边设置为大于区域
        
        当cur指针碰到了more指针以后 就停止即可
        
        
    '''      
    cur = L
    Less = L - 1 
    more = R + 1 
    while cur < more-1:
        print('cur', cur)
        if array[cur] < target:
            array[Less+1], array[cur] = array[cur], array[Less+1]
            Less += 1
            cur += 1
            
        if array[cur] > target:                   
            array[more-1], array[cur] = array[cur], array[more-1]
            more -= 1
            print('more', more)
            
        if array[cur] == target:           
            cur += 1
                      
    return array[L:Less+1], array[more:], array[Less+1:more]

arr = [3, 4, 5, 1, 8, 0, 6, 1, 5, 5, 9, 28]
less_arr, more_arr, equal_arr = Dutch_flag(arr, 0, len(arr)-1, 5)

print('小于域:', less_arr,' 大于域:', more_arr, '等于域:', equal_arr)

三、改进后的经典快排

1.问题描述

a.先说一下最原始的经典快排
(1)取数组的最后一个数(也可以取第一个数)记为x作为target,小于等于x的放在左边,大于x的放在右边;
(2)完成一次操作以后再把区域放缩,再次选取最后一个数记为a,依然小于等于的放左边,大于a的放右边(递归);在大于区域做同样的操作。
(3)递归,系统依次重复执行此操作

b.改进后的经典快排
它与最原始的经典快排的唯一区别就是前者的区域是两个区域,后者的区域是三个区域。因为如果把小于等于放在一起,那么一次只能搞定一个数,如果数据中有多个重复的数,那么一次只搞定了其中的一个,剩下的还混在了小于区域里面。而如果我们把小于等于分开,那么我们一次就搞定了一片数,效率自然会有提高。

2.python实现

ef quickSort(arr, L, R):
    '''
    改进后的快排快排
    问题三:
        改进后的经典快排与原始的经典的快排的区别就是,改进后的是分为了三个区域(小于、等于、大于),
        因此每次挑出来的参照x都可以搞定一片(只要相等的都会搞定)。但是经典的只能搞定一个,因为它把小于和等于放一起了。

    '''
    if L < R:
        small_index, large_index = partition(arr, L, R)
        quickSort(arr, L, small_index-1)
        quickSort(arr, large_index+1, R)
        
    return arr
       
def partition(array, L, R):
    '''
    给定一个target,依次和数组中的每一个数比较,如果小于或者等于target 就放进数组的左端,
    否则(也就是大于target),放在数组的右端。  也就是把数组分成了两部分
    '''
    cur = L
    Less = L - 1 
    more = R + 1
    target = array[R]
    
    while cur < more:
        if array[cur] < target:
            array[Less+1], array[cur] = array[cur], array[Less+1]
            Less += 1
            cur += 1
            
        if array[cur] > target:      
            array[more-1], array[cur] = array[cur], array[more-1]
            more -= 1
            
        if array[cur] == target:
            cur += 1
    return Less+1, more-1 


arr = [1,21515,3,1255,5,77,7,7,5]
fianlArr = quickSort(arr, 0, len(arr)-1)
print(fianlArr)            

四、随机快排(非常重要)

1.问题描述
相比于经典快排,无非就是加了一个概率。也就是说之前的数(target)要么取的是第一个数,要么取的是最后一个数,但是现在变成了随机选取target。

2.优势
因为经典快排分为最好、最坏情况,与数据情况有关,即若区域规模相差非常大,则有可能变成O(N^2),这样非常不好。那么如果我们加一个概率进去,,每次随机选数然后和数组的最后一个值交换,这样就加了一个概率时间。原先的不论最坏还是最好情况,现在都变成了一个概率问题,那么就得按照期望来看,因此就变成了O(logN)。
注意:因此我们可知,如果一种算法受数据情况的影响,那么我们可以从随机出发,消除数据最好最坏情况的影响。

3.python实现

import random

def randomQuickSort(arr, L, R):
    '''
    随机快排
    加了个概率,使得最坏最好情况等概率,这样大数据的情况下时间复杂度期望就是O(NlogN)
    '''
    if L < R:
        random_tmp = L + int(random.random()*(R - L + 1))  #与改进后的经典快排唯一的区别,就是选择数随机选取,然后再与最后一个数交换,以此往复
        arr[R], arr[random_tmp] =arr[random_tmp], arr[R]
        small_index, large_index = partition(arr, L, R)
        randomQuickSort(arr, L, small_index-1)
        randomQuickSort(arr, large_index+1, R)
        
    return arr
      
def partition(array, L, R):
    '''
    问题一:
    给定一个target,依次和数组中的每一个数比较,如果小于或者等于target 就放进数组的左端,
    否则(也就是大于target),放在数组的右端。  也就是把数组分成了两部分
    '''
    cur = L
    Less = L - 1 
    more = R + 1
    target = array[R]
    
    while cur < more:
        if array[cur] < target:
            array[Less+1], array[cur] = array[cur], array[Less+1]
            Less += 1
            cur += 1
            
        if array[cur] > target:      
            array[more-1], array[cur] = array[cur], array[more-1]
            more -= 1
            
        if array[cur] == target:
            cur += 1
    return Less+1, more-1 

arr = [1,21515,3,1255,5,77,7,7,5]
fianlArr = randomQuickSort(arr, 0, len(arr)-1)
print(fianlArr)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值