算法复习——分治算法

侧重于分解:快速排序、次序选择

侧重于合并:归并排序、逆序计数、最大子数组

最大子数组问题

GetAcross(A, left, mid, right)
{
    // mid左边
    s_left = -INF, sum = 0;
    for(i=mid to left) {
        sum += A[i];
        if (sum > s_left) {
            s_left = sum;
        }
    }
    // mid右边
    s_right = -INF, sum =0;
    for (i=mid+1 to right) {
    	sum += A[i];
        if (sum > s_right) {
            s_right = sum;
        }
    }
    return (s_left + s_right);
}

MNC(A, left, right)
{
    if (left >= right) return A[left];
    else {
        mid = (left + right)/2;
        s1 = MNC(A, left, mid);
        s2 = MNC(A, mid+1, right);
        s3 = GetAcross(A, left, mid, right);
        return max(s1, s2, s3);
    }
}

归并排序问题

Merge(A, left, mid, right)
{
    B[left..right] = A[left..right];
    k = 0;
    i = left, j = mid+1;
    while(i<=mid and j<=right) {
        if(B[i] <= B[j]) {
            A[k] = B[i];
            k++, i++;
        }
        else {
            A[k] = B[j];
            k++, j++;
        }
    }
    
    while (i <= mid) {
        A[k..right] = B[i..mid];
    }
    
    while (j <= right) {
        A[k..right] = B[j..right];
    }
    return A[left..right];
}

MergeSort(A, left, right)
{
    if(left >= right) return A[left]
	else {
        mid = (left + right)/2;
        MergeSort(A, left, mid);       // T(n/2)
        MergeSort(A, mid+1, right);	   // T(n/2)
        Merge(A, left, mid, right);    // O(n)
        return A[left..right];
    }
}

逆序计数问题

Merge(A, left, mid, right)
{
    B[left..right] = A[left..right];
    k = 0;
    i = left, j = mid+1;
    s3 = 0;
    while(i<=mid and j<=right) {
        if(B[i] <= B[j]) {
            A[k] = B[i];
            k++, i++;
        }
        else {
            A[k] = B[j];
            k++, j++;
            s3 += mid-i+1; // 如果B[i] > B[j], 那么B[i..mid] > B[j]
        }
    }
    
    while (i <= mid) {
        A[k..right] = B[i..mid];
    }
    
    while (j <= right) {
        A[k..right] = B[j..right];
    }
    return A[left..right];
}

MergeCount(A, left, right)
{
    if(left >= right) return 0,A[left]
	else {
       mid = (left + right)/2;
       s1 = MergeCount(A, left, mid);       	// T(n/2)
       s2 = MergeCount(A, mid+1, right);	   // T(n/2)
       s3 = Merge(A, left, mid, right);    		// O(n)
       return (s1+s2+s3), A[left..right];
    }
}

快速排序算法

Partation(A, p, r)
{
    x = A[r]; // 选取固定主元
    i = p - 1; 
    for (j=p to r-1) {
        if (A[j] <= x) {
           exchange(A[i+1], A[j]);
           i = i+1;
        }
    }
    exchange(A[r], A[i+1]); // 把主元放到中间
    return i+1;
}

QuickSort(A, p, r)
{
    if (p < r) {
    	q = Partation(A, p, r);
        QuickSort(A, p, q);
        QuickSort(A, q+1, r);
    }
    
}

时间复杂度为\(O(n^2)\)

随机化选取主元

Randomized-Partation(A, p, r)
{
    k = random(p,r)
    x = A[k]; 
    exchange(A[k], A[r]);
    //
    i = p - 1; 
    for (j=p to r-1) {
        if (A[j] <= x) {
           exchange(A[i+1], A[j]);
           i = i+1;
        }
    }
    exchange(A[r], A[i+1]); // 把主元放到中间
    return i+1;
}



Randomized-QuickSort(A, p, r) {
    if (p < r) {
    	q = Randomized-Partation(A, p, r);
        Randomized-QuickSort(A, p, q);
        Randomized-QuickSort(A, q+1, r);
    }
}

时间复杂度为\(O(nlogn)\)

次序选择

找一个数组中第k小的元素

思路一:将整个数组排序(\(O(nlogn)\)), 然后直接选取下标为left+k-1的元素即可

思路二:根据快速排序数组划分的思想,每次选取一个主元,然后把数组划分成左右两边,判断k 和 q-p+1的大小关系

SelectionProblem(A, left, right)
{
    q = Randomized-Partation(A, left, right);  //主要是使用了随机化快速排序,降低了时间复杂度
    if (k == q-p+1){
        return A[q];
    }
    else if (k < q-p+1) {
        return SelectionProblem(A, left, q-1);
    }
    else {
        return SelectionProblem(A, q+1, right);
    }
}

时间复杂度是\(O(n)\)

基于比较的排序算法时间复杂度的下限是\(O(nlogn)\)

posted @ 2020-12-26 09:41  VanHope  阅读(93)  评论(0编辑  收藏  举报