算法上机1

题目1

Design a Θ(n lg n)-time algorithm that, given a set S of nintegers and another integer x, determines whether or not there exist twoelements in S whose sum is exactly x.

分析

    题目要求设计一个复杂度为Θ(n lg n)的算法。当给定一个数组S和一个整数x时,可以通过这个算法求出S中是否恰好有两个数的和是x。

    首先依次遍历S = [d0, d1, d2, …, dn]中的各个元素,对S中任意的元素di,若dj = x –di 也在数组S中,那么di和dj的和就是x。易得,遍历数组的复杂度是Θ(n),那么查找dj的时间要控制在Θ(lg n)以内才能满足题目的条件。

    采用复杂度为Θ( lgn)折半查找的方法在S中寻找是否存在这样的dj(递归树推演复杂度如下图)。而在折半查找前需要对数组进行排序,可以采用复杂度为Θ(n lg n)的归并排序。



算法源码


'''
Design a Θ(n lg n)-time algorithm that,
given a set S of n integers and another integer x,
determines whether or not there exist two elements in S whose sum is exactly x.
'''
import math

def MERGE(A, p, q, r):
    '''
    A是数组,
    p是第一个部分的起始下标,
    q是第一个部分的结尾下标,
    r是第二个部分的结尾下标
    '''
    n1 = q - p + 1
    n2 = r - q
    L = []
    R = []
    #拷贝第一部分的内容到新数组L
    for i in range(0, n1):
        L.append(A[p + i])
    #拷贝第二部分的内容到新数组R
    for j in range(0, n2):
        R.append(A[q + 1 + j])

    i, j = 0, 0

    for k in range(p, r+1):
        if i < n1 and j < n2:
            if L[i] <= R[j]:
                A[k] = L[i]
                i += 1
            else:
                A[k] = R[j]
                j += 1
        else:
            break

    #把L或R中的剩余部分加入
    if i < n1:
        while k <= r:
            A[k] = L[i]
            k += 1
            i += 1
    if j < n2:
        while k <= r:
            A[k] = R[j]
            k += 1
            j += 1

def MERGE_SORT(A, p, r):
    '''
    把过程MERGE作为归并排序算法中的子程序实现排序
    A是数组,
    p是第一个部分的起始下标,
    r是第二个部分的结尾下标
    '''
    if p < r:
        q = int(math.floor(p+r)/2)
        MERGE_SORT(A, p, q)
        MERGE_SORT(A, q+1, r)
        MERGE(A, p, q, r)

def BINARY_SEARCH(A, value, p, r):
    '''
    A是数组,
    value查找的值,
    p起始位置
    r结束位置
    若在A中找不到value返回-1, 否则返回其下标
    '''
    if p > r:
        return -1
    else:
        q = int(math.floor(p+r)/2)
        if A[q] == value:
            return q
        elif A[q] > value:
            return BINARY_SEARCH(A, value, p, q-1)
        else:
            return BINARY_SEARCH(A, value, q+1, r)

def IS_EXIST(A, value):
    '''
    A是数字
    value为X的值
    若A中有两个元素和为X则返回1, 否则返回0
    '''
    #对数组进行排序
    MERGE_SORT(A, 0, len(A)-1)
    #查找元素
    for i in range(0, len(A)):
        rest = value - A[i]
        j = BINARY_SEARCH(A, rest, i+1, len(A)-1)

        if j != -1:
            return 'x={}时, 存在'.format(value)
    return 'x={}时, 不存在'.format(value)

#test
A = [2, 4, 5, 7, 1, 2, 3, 6]
print(IS_EXIST(A, 6))
print(IS_EXIST(A, 13))
print(IS_EXIST(A, 100))


运行结果截图

测试数据:

数组S = [2, 4, 5, 7, 1, 2, 3, 6]

总结

做本题时,看完题目的第一反应就是用两个for循环遍历数组找出值,但这种方法的复杂度是Θ(n^2),而采用上面的算法,同样功能的程序复杂度降低到了Θ(n lg n)。可见,使用适当的算法,往往可以降低程序的复杂度,更高效的实现目的。

上面的算法仍存在缺陷。比如只能找出一对符合条件的数。当数组S中有多对数的和等于x时,无法将它们全部找出。

 

 

 

题目2

Let A[1 ‥ n] be anarray of n distinct numbers. If i < j and A[i] > A[j], then the pair (i,j) is called an inversion of A. Give an algorithm that determines the number ofinversions in any permutation on n elements in Θ(n lg n) worst-case time.(Hint: Modify merge sort.)

分析

    题目要求设计一个最坏情况下复杂度为Θ(n lg n)的算法。当给定一个数组S时,可以求出其中inversion的个数。该题涉及前后元素的大小比较,很显然是一个排序问题。

    采用归并排序对数组进行排序。在特殊情况时,即当数组被分解为多组两两比较时,若比较的两个数需要对换,则这两个数必是inversion。当情况更加普遍一些时, 设L = [q0,  q1 ,… , qn]和R = [p0,p1, …, pn](L中的元素序号小于R中的元素序号)是被分解得到的数组中的任意两个,当L和R比较排序时,若存在qi, pj, 使得qi > pj成立,则L中qi到qn的所(n-i+1)个元素都与pj构成inversion。



算法源码
 
import math
count = 0
def MERGE(A, p, q, r):
    '''
    A是数组,
    p是第一个部分的起始下标,
    q是第一个部分的结尾下标,
    r是第二个部分的结尾下标
    '''
    n1 = q - p + 1
    n2 = r - q
    L = []
    R = []
    #拷贝第一部分的内容到新数组L
    for i in range(0, n1):
        L.append(A[p + i])
    #拷贝第二部分的内容到新数组R
    for j in range(0, n2):
        R.append(A[q + 1 + j])

    i, j = 0, 0

    for k in range(p, r+1):
        if i < n1 and j < n2:
            if L[i] <= R[j]:
                A[k] = L[i]
                i += 1
            else:
                A[k] = R[j]
                j += 1
                global count
                count += n1 - i
        else:
            break

    #把L或R中的剩余部分加入
    if i < n1:
        while k <= r:
            A[k] = L[i]
            k += 1
            i += 1
    if j < n2:
        while k <= r:
            A[k] = R[j]
            k += 1
            j += 1

def MERGE_SORT(A, p, r):
    '''
    把过程MERGE作为归并排序算法中的子程序实现排序
    A是数组,
    p是第一个部分的起始下标,
    r是第二个部分的结尾下标
    '''
    if p < r:
        q = int(math.floor(p+r)/2)
        MERGE_SORT(A, p, q)
        MERGE_SORT(A, q+1, r)
        MERGE(A, p, q, r)
#test
A = [2, 1, 3, 8, 5, 7, 4, 6, 10]
print('数组:{}'.format(A))
MERGE_SORT(A, 0, len(A)-1)
print('个数:{}'.format(count))
count=0
A = [2, 1, 0, -1]
print('数组:{}'.format(A))
MERGE_SORT(A, 0, len(A)-1)
print('个数:{}'.format(count))
count=0
A = [1, 2, 3]
print('数组:{}'.format(A))
MERGE_SORT(A, 0, len(A)-1)
print('个数:{}'.format(count))
count=0

运行结果截图

总结

采用分治的方法,有时可以非常有效的提高解决问题的效率。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值