【2】算法基础:思想

1.蛮力法

直接解决,常用到遍历

示例1:选择排序

每次遍历整个列表哦,选择一个最小的与第一个没有被交换的进行交换,下一次从被交换的下一个元素开始遍历

O(n)~O(n^2)

def selection_sort(wait_list):
    for i in range(len(wait_list)):
        min_index=i
        for j in range(i,len(wait_list)):
            if wait_list[j]<wait_list[min_index]:
                min_index=j
        wait_list[i],wait_list[min_index]=wait_list[min_index],wait_list[i]
    print(wait_list)

wait_list=[2,6,1,5,8,3,38,27,75,34,78,24]
selection_sort(wait_list)

示例2:冒泡排序

从第一个元素开始遍历列表直到不确定位置的最后一个元素,如果相邻元素是逆序则交换位置,每一次遍历后,有一个元素被防止到对应位置上

O(n)~O(n^2)

def bubble_sort(wait_list):
    for i in range(len(wait_list)):
        for j in range(i,len(wait_list)):
            if wait_list[j]<wait_list[i]:
                wait_list[i],wait_list[j]=wait_list[j],wait_list[i]
    print(wait_list)

wait_list=[2,6,1,5,8,3,38,27,75,34,78,24]
bubble_sort(wait_list)

2.分治法

将问题实例划分为同一个问题的几个较小的实例,对这些较小的实例求解后合并得到原问题的解。

能够使用分治法的问题的特征:问题规模缩小到一定程度就很容易解决(时间、空间);问题能够划分为多个相互独立的子问题。尤其适合于可并行执行的算法。

示例1:合并排序

将需要排序的数组分为两个子数组,分别对两个数组排序,最后将两个子数组合并。

O(n*logn)

def merge(sub_list1,sub_list2):
    all_list=[]
    i=0
    j=0
    while i<len(sub_list1) and j<len(sub_list2):
        if sub_list1[i]<=sub_list2[j]:
            all_list.append(sub_list1[i])
            i+=1
        else:
            all_list.append(sub_list2[j])
            j+=1
    if i<len(sub_list1):
        all_list.extend(sub_list1[i:])
    if j<len(sub_list2):
        all_list.extend(sub_list2[j:])
    return all_list

def merge_sort(wait_list):
    if len(wait_list)==1 or len(wait_list)==0:
        return wait_list
    mid=int(len(wait_list)/2)
    sub_list1=wait_list[:mid]
    sub_list2=wait_list[mid:]
    sub_list1=merge_sort(sub_list1)
    sub_list2 =merge_sort(sub_list2)
    all_list=merge(sub_list1,sub_list2)
    return all_list

if __name__=="__main__":
    wait_list = [2, 6, 1, 5, 8, 3, 38, 27, 75, 34, 78, 24]
    sort_list=merge_sort(wait_list)
    print(sort_list)

示例2:快速排序

对所有元素进行分区,在下标s前的元素的值都小于等于下标为s的元素值,之后的则大于下标为s的元素值。这样,下标为s的元素所在的位置与目标升序数组中的位置相同,接下来对前后两个分区的数据使用同样的方式进行分区,直到每个分区都只有一个元素。

O(n*logn)~O(n^2)

def quick_sort(wait_list):
    if len(wait_list)==1 or len(wait_list)==0:
        return wait_list
    s=int(len(wait_list)/2)
    lower_list=[]
    higher_list=[]
    for i in range(len(wait_list)):
        if i==s:
            continue
        curr=wait_list[i]
        if curr<=wait_list[s]:
            lower_list.append(curr)
        else:
            higher_list.append(curr)
    lower_list=quick_sort(lower_list)
    higher_list=quick_sort(higher_list)
    sort_list=lower_list
    sort_list.append(wait_list[s])
    sort_list.extend(higher_list)
    return sort_list

if __name__=="__main__":
    wait_list = [2, 6, 1, 5, 8, 3, 38, 27, 75, 34, 78, 24]
    sort_list=quick_sort(wait_list)
    print(sort_list)

示例3:有一个由N个实数构成的数组,如果一对元素A[i]和A[j]是倒序的,即i<j但A[i]>A[j],则他们是一个倒置,设计一个复杂度为O(nlogn)的算法计算该数组中所有倒置数量。

结合合并排序

def merge(sub_list1,sub_list2):
    reverse_count=0
    all_list=[]
    i=0
    j=0
    while i<len(sub_list1) and j<len(sub_list2):
        if sub_list1[i]<=sub_list2[j]:
            all_list.append(sub_list1[i])
            i+=1
        else:
            all_list.append(sub_list2[j])
            j+=1
            reverse_count+=(len(sub_list1)-i)
    if i<len(sub_list1):
        all_list.extend(sub_list1[i:])
    if j<len(sub_list2):
        all_list.extend(sub_list2[j:])
    return all_list,reverse_count
def search_reverse(wait_list):
    if len(wait_list)==1 or len(wait_list)==0:
        return wait_list,0
    mid=int(len(wait_list)/2)
    sub_list1=wait_list[:mid]
    sub_list2=wait_list[mid:]
    sub_list1,reverse_count_1 = search_reverse(sub_list1)
    sub_list2,reverse_count_2 = search_reverse(sub_list2)
    sort_list,reverse_count_new = merge(sub_list1,sub_list2)
    return sort_list,reverse_count_1+reverse_count_2+reverse_count_new

if __name__=="__main__":
    wait_list = [2, 6, 1, 5, 8, 3, 38, 27, 75, 34, 78, 24]
    sort_list,reverse_count=search_reverse(wait_list)
    print(reverse_count)

3.减治法

利用一个问题给定实例的解和同样问题较小实例的解之间的关系,将一个大规模问题逐步简化为一个小规模的问题,常用递归。

分治法是化为多个小问题,每个小问题之间有联系,减治法是化为一个小问题,小问题与原问题之间有联系。

示例1:插入排序

假设前n-1个元素已经排好序,递推关系就是如何将最后一个元素插入到其他元素中去。

O(n)~O(n^2)

def insertion_sort_cycle(wait_list):
    for i in range(0,len(wait_list)):
        for j in range(i,0,-1):
            if wait_list[j]<wait_list[j-1]:
                wait_list[j],wait_list[j-1]=wait_list[j-1],wait_list[j]
    return wait_list
def insertion_sort_recurrent(wait_list):
    if len(wait_list)==1:
        return wait_list
    front_list=insertion_sort_recurrent(wait_list[:-1])
    curr=wait_list[-1]
    if curr<=front_list[0]:
        front_list.insert(0,curr)
    elif curr>front_list[-1]:
        front_list.append(curr)
    else:
        for i in range(1,len(front_list)):
            if curr>front_list[i-1] and curr<=front_list[i]:
                front_list.insert(i,curr)
                break
    return front_list

if __name__=="__main__":
    wait_list = [2, 6, 1, 5, 8, 3, 38, 27, 75, 34, 78, 24]
    sort_list_cycle=insertion_sort_cycle(wait_list)
    sort_list_recurrent=insertion_sort_recurrent(wait_list)
    print(sort_list_cycle)
    print(sort_list_recurrent)

 

示例2:Shell排序

按照设计好的间隔不停分组,间隔递减,组内插入排序。

O(n)~O(n^2)

def shell_sort(wait_list):
    gap=int(len(wait_list)/2)
    while gap>=1:
        for i in range(gap,len(wait_list)):
            for j in range(i,0,-gap):
                if wait_list[j]<wait_list[j-gap]:
                    wait_list[j], wait_list[j - gap] = wait_list[j - gap], wait_list[j]
        gap=int(gap/2)
    return wait_list

if __name__=="__main__":
    wait_list = [2, 6, 1, 5, 8, 3, 38, 27, 75, 34, 78, 24]
    sort_list=shell_sort(wait_list)
    print(sort_list)

示例3:N个士兵过河,有两个小孩在划船,一条船只能搭载2个小孩或者1个士兵,需要来回多少次?

当只有一个士兵时,两个小孩先划过去,一个小孩划回来,士兵划过去,另一个小孩划回来。多个士兵时,不断重复,每次送一名士兵过去。

4.变治法

通过转换问题使得原问题更容易求解:使用中间操作逐步简化问题,改变使用的数据结构,映射到新的问题进行求解,例如预排序,先排序再求解原问题。

示例1:有n个数字构成的一个数组和一个整数s,如何最快确定数组中是否存在两个数的和恰好等于s。

法1预排序,从头和尾开始找,缩小范围;法2将遇到过的数加入容器,如果目标值减去当前数的结果在容器中则存在。

def sum_exist_1(wait_list,target):
    wait_list=sorted(wait_list)
    i=0
    j=len(wait_list)-1
    while i<=j:
        temp_sum=wait_list[i]+wait_list[j]
        if temp_sum==target:
            return True
        elif temp_sum>target:
            j-=1
        else:
            i+=1
    return False

def sum_exist_2(wait_list,target):
    pre_list=[]
    for curr in wait_list:
        if target-curr in pre_list:
            return True
        else:
            pre_list.append(curr)
    return False


if __name__=="__main__":
    wait_list = [2, 6, 1, 5, 8, 3, 38, 27, 75, 34, 78, 24]
    sum_exist_1(wait_list,109)
    sum_exist_2(wait_list, 109)

示例2:在实数域上有n个开区间,开区间由严格位于区间端点之间的所有点构成,求给定开区间集合中包含共同点的区间的最大数量。

转换为多个点的排序,遍历各个点,遇到“起始”状态当前经历点数加1,遇到“结束”状态当前经理点数减1,记录整个过程当前点数的最大值。

class Point:
    def __init__(self,value,state):
        self.value=value
        self.state=state # 1 for start, 0 for end
def common_point(space_list):
    point_list=[]
    for space in space_list:
        point_list.append(Point(space[0], 1))
        point_list.append(Point(space[1], 0))
    point_list=sorted(point_list,key=lambda x:x.value)
    current_point_count=0
    max_point_count=0
    for point in point_list:
        if point.state:
            current_point_count += 1
        else:
            current_point_count -= 1
        if current_point_count>max_point_count:
            max_point_count=current_point_count
    return max_point_count

if __name__=="__main__":
    space_list=[(1,4),(0,3),(-1.5,2),(3.6,5)]
    common_count=common_point(space_list)
    print(common_count)

示例3:在一个给定的单词集合中,快速查找所有的变位词。

对单词集中每个单词做字母序排序,相同字母序的就是一组变位词。

from collections import defaultdict

def change_position_words(word_list):
    s_dict=defaultdict(list)
    for i,word in enumerate(word_list):
        word_s="".join(sorted(word))
        s_dict[word_s].append(i)
    for s in s_dict:
        if len(s_dict[s])>1:
            for i in s_dict[s]:
                print(word_list[i],end=" ")
            print()

if __name__=="__main__":
    change_position_words(word_list=["eat","aaa","esc","tea","baa","ate","sec"])

5.时空权衡

常见的方式有用空间换时间,即预处理时对额外信息进行存储,加速后面问题的求解。

预构造,使用额外的空间实现更快、更方便的数据存储:散列,B树索引

动态规划:减治找到递推关系,记录上一次的求解结果

示例1:计数排序

多次扫描列表,利用额外空间记录比每一个元素小的元素的个数,最后把原列表的元素复制到新数组对应下标。

def count_sort(wait_list):
    count_list=[]
    for i in range(len(wait_list)):
        count_list.append(0)
    for i,curr in enumerate(wait_list):
        for j,wait in enumerate(wait_list):
            if wait<curr:
                count_list[i]+=1
    sort_list=[]
    for i in range(len(wait_list)):
        sort_list.append(0)
    for i,c in enumerate(count_list):
        sort_list[c]=wait_list[i]
    return sort_list
    
if __name__=="__main__":
    wait_list = [2, 6, 1, 5, 8, 3, 38, 27, 75, 34, 78, 24]
    sort_list=count_sort(wait_list)
    print(sort_list)

示例2:Boyer-Moore字符串匹配

https://blog.csdn.net/iteye_15446/article/details/82508387

 

示例3:币值最大化问题

给定一排n个硬币,面值均为正整数c1,c2,……cn,这些整数并不一定两两不同,如何选择硬币使得在其原始位置互不相邻的条件下,所选硬币的总金额最大。

最大可选金额F(n),所有可行的选择划分为两组:包括最后一枚硬币的和不包括最后一枚硬币的。第一组中,可选硬币的最大金额等于cn+F(n-2),另一组中最大可选金额等于F(n-1)。所以有递推方程::F(n)=max{cn+F(n-2),F(n-1)},n>1,F(0)=0,F(1)=c1。

用数组保存中间结果的循环结构比递推结果更好,时间空间都为O(n)。

import copy

def coin_max_value_cycle(value_list):
    if len(value_list)==0:
        return [0,[]]
    elif len(value_list)==1:
        return [value_list[0],[value_list[0]]]
    else:
        F=[[0,[]],[value_list[0],[value_list[0]]]]
        for i in range(2,len(value_list)):
            F_2=F[i-2]
            F_1=F[i-1]
            if F_2[0]+value_list[i]>F_1[0]:
                F_c=copy.deepcopy(F_2)
                F_c[0]=F_c[0]+value_list[i]
                F_c[1].append(value_list[i])
                F.append(F_c)
            else:
                F_c=copy.deepcopy(F_1)
                F.append(F_c)
        return F[-1]
def coin_max_value_recurrent(value_list):
    if len(value_list)==0:
        return [0,[]]
    elif len(value_list)==1:
        return [value_list[0],[value_list[0]]]
    else:
        F_2=coin_max_value_recurrent(value_list[:-2])
        F_1=coin_max_value_recurrent(value_list[:-1])
        if F_2[0]+value_list[-1]>F_1[0]:
            F_2[0] = F_2[0] + value_list[-1]
            F_2[1].append(value_list[-1])
            return F_2
        else:
            return F_1
if __name__=="__main__":
    print(coin_max_value_cycle([5,1,2,10,6,2]))
    print(coin_max_value_recurrent([5, 1, 2, 10, 6, 2]))

示例4:找零问题

需找零金额为n,最少要用多少面值为d1<d2<……<dm的硬币(假设每种面值的硬币数量无限可得)。

设F(n)为总金额为n的数量最少的硬币数目,获得n的途径只能是在总金额为n-dj的一堆硬币上加入一个面值为dj的硬币,其中j=1,2,……,m,并且n>=dj。因此我们只需要考虑所有满足上述要求的dj并选择使得F(n-dj)+1最小的dj即可。F(n)的递归公式:F(n)-min{F(n-dj)}+1(n>0,n>=dj),F(0)=0。

import copy

def change_making(N,value_list):
    F=[[0,[]]]
    if len(value_list)==0:
        return F[-1]
    else:
        for n in range(1,N+1):
            f=[float('inf'),[]]
            add_coin=-1
            for j in range(len(value_list)):
                if n<value_list[j]:
                    break
                if F[n - value_list[j]][0]<f[0]:
                    f=copy.deepcopy(F[n - value_list[j]])
                    add_coin=value_list[j]
            f[0]+=1
            f[1].append(add_coin)
            F.append(f)
        return F[-1]

if __name__=="__main__":
    print(change_making(6,[1,3,4]))

示例3:硬币收集问题

在nxm格木板中放有一些硬币,每格的硬币数目最多为一个,在木板左上方的一个机器人需要收集尽可能多的硬币并把它们带到右下方的单元格。每一步,机器人可以从当前的位置向右移动一个或者向下移动一格。当机器人遇到一个有硬币的单元格时,就会将这枚硬币收集起来。设计一个算法找出机器人能找到的最大硬币数并给出相应的路径。

令F(i,j)为机器人截止到第i行第j列单元格(i,j)能够收集到的最大硬币数,单元格(i,j)可以经由上方相邻单元格(i-1,j)或者左边相邻单元格(i,j-1)。单元格(i-1,j)和单元格(i,j-1)中最大的硬币数分别是F(i-1,j)和F(i,j-1)。第一行单元格没有上方相邻单元格,第一列没有左边相邻单元格,对于这些单元格,假定F(i-1,j)和F(i,j-1)的值为0。截止到单元格(i,j)机器人能够收集到的最大硬币数是这两个数的较大值加上单元格(i,j)中可能存在的一枚硬币。递归公式:F(i,j)=max{F(i-1,j),F(i,j-1)+cij}(1<=i<=n,1<=j<=m);F(0,j)=0(1<=j<=m);F(i,0)=0(1<=i<=m)。

示例1,2中我们在解决问题的过程中记录了硬币的选择,示例3中我们使用回溯的方法找到可能的路径。

import copy

def coin_collect(C_mat):
    n=len(C_mat)
    m=len(C_mat[0])
    F=[]
    for r in range(n+1):
        row=[]
        for c in range(m+1):
            row.append(0)
        F.append(row)

    for r in range(1,n+1):
        for c in range(1,m+1):
            F[r][c]=max(F[r-1][c],F[r][c-1])+C_mat[r-1][c-1]
    return F
def track(F_mat,C_mat,track_stack,path_list):
    r,c=track_stack[-1]
    if r==1 and c==1:
        path_list.append(copy.deepcopy(track_stack))
    else:
        if F_mat[r][c]==F_mat[r-1][c]+C_mat[r-1][c-1]: # up
            track_stack.append([r-1, c])
            track(F_mat, C_mat, track_stack,path_list)
            track_stack.pop()
        if F_mat[r][c]==F_mat[r][c-1]+C_mat[r-1][c-1]: # left
            track_stack.append([r,c-1])
            track(F_mat, C_mat, track_stack,path_list)
            track_stack.pop()
def coin_collect_trackback(F_mat,C_mat):
    path_list=[]
    track_stack=[]
    r=len(F_mat)-1
    c=len(F_mat[0])-1
    track_stack.append([r,c])
    track(F_mat,C_mat,track_stack,path_list)
    return path_list

if __name__=="__main__":
    C=[[0,0,0,0,1,0],
                    [0,1,0,1,0,0],
                    [0,0,0,1,0,1],
                    [0,0,1,0,0,1],
                    [1,0,0,0,1,0]]
    F=coin_collect(C)
    for f in F:
        print(f)
    path_list=coin_collect_trackback(F,C)
    for p in path_list:
        print(p)

 

参考链接:

https://www.cnblogs.com/xiaochun126/p/5086037.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值