算法与设计分析作业(分治)

本文探讨了分治法在解决算法问题中的应用,包括寻找数组中第k大元素、寻找完全二叉树的局部最小值以及计算序列中显著逆序对的数量。通过对每个问题的分治策略、伪代码、子问题分解图和正确性证明的分析,展示了分治法在优化算法效率方面的效果。同时,提到了使用分治法实现Strassen矩阵乘法和Karatsuba乘法算法的C++代码实现,并与传统方法进行了性能比较。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法与设计分析作业


 

2 Divide and Conquer

Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.
For example, Given [3, 2 ,1, 5, 6, 4] and k = 2, return 5.

problem-solving ideas

借鉴快速排序的思想,快排每一次排序之后可以确定一个元素的位置,该元素的左边均小于该元素,右边均大于该元素,因此可以用该元素的位置与k比较,来确定第k大的元素在当前数组那个范围内,然后递归。从而省去另一部分的查找。

Pseudo-code
function findKthLargest(A,k,s,e){
    随机选取一个元素A[j]作为分界点
     for i=s to e do
       if A[i] < A[j]
         将A[i]放在A[j]的右边
       end if
       if A[i] > A[j]
         将A[i]放在A[j]的左边
       end if
     end for
    A[j]当前在A数组中的位置为w,数组长度为length(数组从0开始)
    if k == length-w
      返回 A[j]
    else if k> length-w
      返回 findKthLargest(A,k,s,w);
    else 
      返回 findKthLargest(A,k,w,e)
    end if   
}
Subproblem reduction graph

寻找第k大元素

Prove the correctness

根据快排的性质,每一次的递归都可以确定一个元素的位置,且该元素左边的元素均小于该元素值,右边的元素均大于该元素值,而我们要找的是第k大的元素,通过两者位置的比较,我们一定能确定第k大元素在哪一个范围内,随着递归深入范围逐渐缩小,因此最后一定能找到第k大的元素。

#### The complexity of your algorithm

最好的情况:每次挑选的元素都能将数组平均分成左右相等两边,则 T(n) =T(n/2)+cn。根据公式T(n)=O(n),所以时间复杂度为O(n)。

最坏的情况:每次挑选的元素都将数组分在一边(即挑选的都是最大值或最小值),则T(n)=T(n-1)+cn。T(n)=O(n2).

平均情况应该接近于最好的情况。


 

3 Divide and Conquer

Consider an n-node complete binary tree T, where n = 2d −1 for some d. Each node v of T is labeled with a real number xv. You may assume that the real numbers labeling the nodes are all distinct. A node v of T is a local minimum if the label xv is less than the label xw for all nodes w that are joined to v by an edge.
You are given such a complete binary tree T, but the labeling is only specified in the following implicit way: for each node v, you can determine the value xv by probing the node v. Show how to find a local minimum of T using only O(logn) probes to the nodes of T.

problem-solving ideas

​ 问题的关键在于如何分治把范围缩小,且范围内一定要有局部最小值。因此递归的子树的要求是,递归子树的根结点的值要小于父结点的值,这样的子树一定有局部最小值,且范围被缩小。

Pseudo-code
function findLocalMinimum(T){
    if T的值小于左右子树根结点的值,所有子树若为空则算小于
        返回 T->data
    else if T的值大于左子树根结点的值小于右子树根结点的值
        返回 findLocalMinimum(T的右子树)
    else if T的值小于左子树根结点的值大于右子树根结点的值
        返回 findLocalMinimum(T的左子树)
    else
        返回 findLocalMinimum(T的左子树或T的右子树)
    end if
}
Subproblem reduction graph

完全二叉树的局部最小值

Prove the correctness

任何一棵完全二叉树都有局部最小值,完全二叉树的子树仍然是完全二叉树。对于任何一棵树T,如果T的值小于左右子树的根节点,则T的值就是局部最小值,否则T的值一定大于其中某一棵子树,由于该子树的父结点大于该子树根结点的值,因此父结点不用考虑,且该子树一定有局部最小值。

The complexity of your algorithm

分析算法可得 T(n) =T(n/2)+c。根据公式T(n)=O(log n),所以时间复杂度为O(log n)。


 

6 Divide and Conquer

Recall the problem of finding the number of inversions. As in the course, we are given a sequence of n numbers a1,··· ,an, which we assume are all distinct, and we difine an inversion to be a pair i < j such that ai > aj.
We motivated the problem of counting inversions as a good measure of how different two orderings are. However, one might feel that this measure is too sensitive. Let’s call a pair a significant inversion if i < j and ai > 3aj. Given an O(nlogn) algorithm to count the number of significant inversions between two orderings.

problem-solving ideas

​ 只需在普通的寻找逆序对中改一些步骤。依旧需要归并排序,在归并排序的过程中声明j1,j2(普通的寻找逆序对只声明j),j1用来归并排序。j2来表示是否当前的指向的元素是否已经用来记过数(j2一开始指向A[mid+1],每当A[i]>3*A[j2]就记逆序对数,并且j2向后移动,表明已经被记过数了)。

Pseudo-code
function sortAndCount(A,s,e){
    if s < e
        mid = (s+e)/2
        count1 = sortAndCount(A,s,mid)
        count2 = sortAndCount(A,mid+1,e)
        返回 count1+count2+mergeAndCount(A,s,mid,e)
     else
        返回 0
     end if
}
function mergeAndCount(A,start,mid,end){
    i=start,j1=mid+1,j2=mid+1,count=0,k=0
    建立数组temp临时存放排序好的数组元素
    while i<=mid and j1<=end
        if A[i]<A[j1]
            if A[i]>3*A[j2]
                count += mid - i + 1
                j2++
             else 
                temp[k++]=A[i++]
             end if
         else 
            temp[k++]=A[j1++]
         end if
    end while
    while i<=mid
        if(j2<=end)
            if A[i]>3*A[j2]
                count += mid - i + 1
                j2++
             else 
                temp[k++]=A[i++]
             end if
         else
            temp[k++]=A[i++]
         end if
    end while
    while j1<end
        temp[k++]=A[j1++]
    end while
    将temp数组再重新赋给A
    返回 count
}
Subproblem reduction graph

寻找逆序对

Prove the correctness

普通的寻找逆序对的方法的正确性已经被证明,这里只是根据题意修改了计数的方法,因此该方法也应该是正确的。

The complexity of your algorithm

时间复杂度和普通的求逆序对算法一样为O(nlogn)。T(n)=2*T(n/2)+cn 推出 T(n)=O(nlogn)。


 

10 Divide and Conquer

Implement the Strassen algorithm algorithm for MatrixMultiplication problem in your favourite language, and compare the performance with grade-school method.

C++ Code
//矩阵乘法(分治)
#include <STDLIB.H>
#include <IOSTREAM>
#include <time.h>
using namespace std;

void print(int **M, int n)
{

    int i;
    int j;
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < n; j++)
        {
            cout << M[i][j] << " ";
        }
        cout << endl;
    }
}

void init(int ** &M, int n)
{
    M = new int *[n];
    int i;
    for (i = 0; i < n; i++)
    {
        M[i] = new int[n];
    }
}

void add(int **&A, int **&B, int n, int **&C) //A+B=C
{
    int i;
    int j;
    for (i = 0; i < n; i++)
    {
        for (j = 0; j 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值