【算法设计与分析】第三章 排序与分治法

3 排序与分治法

3.1 排序介绍

3.1.1 内部排序

  1. 插入类-插入排序
  2. 选择类-选择排序
  3. 交换类-冒泡排序
  4. 归并类-归并排序

3.1.2 外部排序

3.2 分治法

3.2.1 分治设计

  1. 问题划分为子问题
  2. 求解子问题
  3. 合并子问题的解

3.2.2 分治分析

  • 即求解递归方程T(n):
    • Divide:
      • 划分为a个 n b \frac{n}{b} bn大小的子问题
      • D(n)
    • Conquer
      • 递归调用: a T ( n b ) aT(\frac{n}{b}) aT(bn)
    • Combine
      • C(n)

3.2.3 例题(1):求解max和min问题

  • 问题定义:

    • 输入:数组A[1,…,n]
    • 输出:A中的max和min
  • 基本思想:

    • 划分为两个子问题:求解max + 求解min
    • 将**A[i]A[n-i+1]**比较,小元素放在前,大元素放在后
    • 使得最小元素出现在A[1,2,…,[ n 2 \frac{n}{2} 2n]]中

    ​ 【算法一】

    • 过程图示:在这里插入图片描述

    • 伪码实现:比较次数= O [ 3 × n 2 − 2 ] O[3\times \frac{n}{2}-2] O[3×2n2]

      Max-min(A)
      Input:   数组A[1,…,n]
      Output:  数组A[1,…,n]中的max和min
         For  i<-1  To  n/2  Do
                IF  A[i] > A[n-i+1] THEN   swap(A[i],A[n-i+1]); //当A[i]更大,交换
         max <- A[n]; min <- A[1];	//从后往前搜索最大值,从前往后搜索最小值
         For  i<-2   To   [n/2]  Do
                 IF  A[i] < min THEN min <- A[i];
                 IF  A[n-i+1] > max  THEN max <- A[n-i+1];
          print max, min;
      

      【算法二】

      • 过程图示:在这里插入图片描述

      • 伪码实现:

        Max-min(low,high)
         IF high-low = 1 //将子问题拆分为两个数的比较,返回小值在前,大值在后
                IF  A[low] < A[high] THEN  return (A[low], A[high])
         	    ELSE return (A[high], A[low])
         ELSE
                 mid <- (low+high)/2 //当还没有拆分完全时,使用二分法拆分
                 (x1,y1) <- Max-min(low,mid)
          	     (x2,y2) <- Max-min(mid+1,high)
                 x <- min{x1,x2} //取最小
                 y <- max{y1,y2} //取最大
                 return (x,y)
        
      • 复杂度分析:

        在这里插入图片描述

3.2.4 例题(2):大数乘法

  • 问题定义:

    • 输入:n位二进制整数X和Y
    • 输出:X和Y的乘积
  • 简单分治算法:

    • 将A=X[0…N/2-1], B=X[N/2…N], C=Y[0…N/2-1], D=Y[N/2…N]

    • X Y = ( A 2 N / 2 + B ) ( C N / 2 + D ) = A C 2 N + ( A D + B C ) 2 N / 2 + B D XY=(A2^{N/2}+B)(C^{N/2}+D)=AC2^{N}+(AD+BC)2^{N/2}+BD XY=(A2N/2+B)(CN/2+D)=AC2N+(AD+BC)2N/2+BD

    • T ( n ) = 4 T ( n 2 ) + θ ( n ) = O ( n 2 ) T(n)=4T(\frac{n}{2})+θ(n)=O(n^2) T(n)=4T(2n)+θ(n)=O(n2)

  • 改进分治算法

  • A D + B C = ( A − B ) ( D − C ) + A C + B D AD+BC=(A-B)(D-C)+AC+BD AD+BC=(AB)(DC)+AC+BD

  • T ( n ) = 3 T ( n 2 ) + θ ( n ) = O ( n l o g 3 ) T(n)=3T(\frac{n}{2})+θ(n)=O(n^{log3}) T(n)=3T(2n)+θ(n)=O(nlog3)

3.2.3 例题(3):棋盘覆盖问题

在这里插入图片描述
在这里插入图片描述

3.3 基于分治思想的排序算法

3.3.1 merge sort

  • 选择一个位置将数组划分成两个部分

  • MergeSort(A,i,j)
    Input:  A[i,…,j]
    Output:排序后的A[i,…,j]
    k <-(i+j)/2;
     MergeSort(A,i,k);  //对A[i...k]进行划分
     MergeSort(A,k+1,j);//对A[k+1...j]进行划分
       l<-i;	//指针l从i开始向后遍历到k
       h<-k+1;	//指针h从k+1开始向后遍历到j
       t=i      //t为数据插入目标数组的指针
       //A为原数组,B为目标数组
       While l < k &  h < j   Do
       	   //随指针扫描进行逐一比较,按序将数据加入目标数组中,对应指针移动
           IF   A[l] < A[h]  
           			THEN   B[t] <- A[l]; l <- l+1; t <- t+1;
           ELSE B[t] <- A[h]; h <- h+1; t <- t+1;
        IF  l<k   THEH          //第一个子问题有剩余元素,直接将剩余元素并入B
              For   v <- l   To   k   Do
                     B[t] <- A[v]; t <- t+1;
        IF  h<j   THEN          //第二个子问题有剩余元素
             For   v <- h   To   j   Do
                     B[t] <- A[v]; t <- t+1;
     For  v <- i   To   j   Do  //将归并后的数据复制到A中
            A[v] <- B[v];
    
    

在这里插入图片描述

  • 中位数问题:

    本算法讨论如何在O(n)时间内从n个不同的数中选取第i大的元素
    (中位数其实就是第n/2大的元素)
    (于是把输出转换为:x∈X使得X中恰好有i-1个元素<x)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

伪码
    Select(A,i)
    Input:   数组A[1:n], 1<i<n
    Output: A[1:n]中的第i-大的数
    for  j<-1   to  n/5                           
        InsertSort(A[(j-1)*5+1 : (j-1)*5+5]); //分组插入排序
    	swap(A[j], A[(j-1)*5+3]);//将中位数放置于数组前n/5个
    x <- Select(A[1: n/5],  n/10 );//递归找出中位数的中位数MoM
    k <- partition(A[1:n], x);//用MoM进行划分,k为x的下标
    if  k=i  
    	then   return x; //如果k=i,则直接返回MoM
    else if  k>i  
        then   retrun Select(A[1:k-1],i);   //如果k>i,返回第一个部分(值均小于M)第i大的值
    else       retrun Select(A[k+1:n],i-k); //如果k<i,返回第三个部分(值均大于M)第k-i大的值

在这里插入图片描述
在这里插入图片描述

3.3.2 quick sort

  • 选择一个划分标准x,根据元素与x的大小关系来划分
    在这里插入图片描述
  PartitionSort(A,i,j)
  Input:    A[i,,j], x
  Output: 排序后的A[i,,j]
  x<-A[i];                           //以确定的策略选择x
  k=partition(A,i,j,x);              //用x完成划分
  partitionSort(A,i,k);              //递归求解子问题
  partitionSort(A,k+1,j); 
  //通过x对A[i...j]进行划分
  Partition(A,i,j,x)
  low<-i ;  high<-j;
  While( low < high ) Do
           swap(A[low], A[high]);	   //对应下述过程,当已经在x左侧找到比x大的数,则与x右侧比x小的数交换
           While( A[low] < x )  Do   //先交换一次并不影响,大概是因为如果符合的话,还会换回来的
                   low<-low+1;
           While( A[high] >=x )  Do
                  high<-high-1;
   return(high)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 随机QuickSort算法

    temp<-rand(i,j);                   //产生i,j之间的随机数
    x<-A[temp];                        //以确定的策略选择x
    

在这里插入图片描述
在这里插入图片描述

那么如何计算 p i j p_{ij} pij呢?

我们可以用树来模拟算法中的比较过程,即划分为根节点,小数在左,大数在右。

分析比较过程我们可以知道几个特点:

  • 每个根节点都会与其子树中的所有节点比较
  • 左子树和右子树中的结点不会比较
  • 两个节点最多比较一次

所以, p i j = 2 j − i + 1 p_{ij}=\frac{2}{j-i+1} pij=ji+12,即只要有一个被选为根节点,则发生比较。
在这里插入图片描述

3.5 排序问题的下界

  • 问题的下界是解决该问题的算法所需要的最小时间复杂性

  • 排序问题的下界是不唯一的

    –例如. Ω(n), Ω(n log n) 都是排序的下界

  • 如果一个问题的下界是Ω(n log n) 且算法的时间复杂性是 O(n log n), 那么这个算法是最优

    否则可以改进问题的下界或者算法的时间复杂性。

  • 又因为排序算法均可以用决策树进行表示,故,排序算法的下界可以用决策树的高度来表示。

  • n!种不同排列,决策树结点。

  • 决策树为平衡树是高度最小, log ⁡ ( n ! ) = Ω ( n log ⁡ n ) \log(n!)=Ω(n\log n) log(n!)=Ω(nlogn)为排序的下界。

  • 描述排序问题的平均下界时:

    • 仍然使用平衡决策树进行计算,平均复杂性用从根结点到每个叶子结点的路径长度的总长度描述。

在这里插入图片描述
在这里插入图片描述

一些题目:
在这里插入图片描述
在这里插入图片描述

(二分法即可,注意A[mid]≥k,且print up,且low=mid+1)

在这里插入图片描述

    closest_pair(A)
    If A.size <= 1 return INF
    sort_by_x(A)				//对n个点的横坐标进行排序
    m = A.size/2;  x= A[m].first  //按照x坐标划分子问题
    d = min(closest_pair(A[0..m]),closest_pair(A[m+1..n])) //d为左右两区域的最小距离
    sort_by_y(A)				//根据y坐标值进行排序
    B = []
    For i = 1 to n
    	If A[i].first-x >= d continue;//从下往上寻找x∈[m-d,m+d]中的点,m为对应划分点
    	for j=0 to b.size		   //对于x处于范围内的每个点,exA2(x2,y2)
    		//.first为x坐标,.second为y坐标
    		dx = A[i].first – B[b.size-j].first //寻找y∈[y2-d,y2+d]的点
    		dy = A[i].second – B[b.size-j].second
    		If dy >= d break
    		d = min(d,sqrt(dx*dx+dy*dy))	//取最小距离。
    	B.push(A[i])
    Return d
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值