使用lomuto划分实现快速选择算法Java版

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


使用lomuto划分实现选择算法(Java版含伪代码)

本文是对《算法设计与分析基础》减治法一章的学习基础上使用java实现快速选择算法
新手上路,如有不当之处,欢迎大家指导。


一、lomuto划分的原理与实现

1.lomuto划分的原理

为了了解Lomuto划分所包含的思想,考虑一个数组——或更一般地,一个子数组A [l…r] (0<=l<=r<=n-1),该数组由连续的三段组成。这三段按顺序排在中轴p的后面:一段为已知大于或等于p的元素,还有一段还未同p比较过的元素(参见图a)。请注意,这些段可以为空。例如在算法开始时,前两段通常是空段。
从i=1+1开始,算法从左到右扫描子数组A [l…r] ,并保持这个结构直到划分完成。在每一次迭代中,它把未知段的第一个元素(图a中由扫描索引i指出)与中轴p进行对比

  • 如果A [i]>=p,则只要i加1,就扩大了大于等于p元素的段,缩小了未处理的段。
  • 如果A [i]<p,则小于p元素的段需要扩大。这将通过s加1来实现,s指向第一段中最后一个元素,再交换A [i]和A [s],然后i加1,使之指向缩小后未处理段的第一个元素。在未处理元素为空后(图b),算法把中轴和A [s]交换,就得到一个我们所要求的划分(图c
    lomuto划分

2.Lomuto划分的伪代码实现

代码如下:

LomutoPartition (A[l..r])
//采用Lomuto算法,用第一个元素作为中轴对子数组进行划分
//输入:数组A[0..n-1]的一个子数组A[l..r],它由左右两边的索引l和r(l<=r)定义
//输出:A[l..r]的划分和中轴的新位置
p<-A[l]
s<-l
for i<-l+1 to r do
	if A[i]<p
		s<-s+1;swap (A[s],A[i])//交换二者位置
swap (A[l],A[s])
return s

二、基于划分的递归算法解决选择问题

1.快速选择(quickselect)算法的实现原理

目前我们已经成功划分列表,那如何利用其寻找第k个最小元素呢?

假设列表是以数组实现的,其元素索引从0开始,而s是划分的分割位置,也就是划分后中轴所在的索引。

  • 如果s=k-1,中轴p本身显然是第k小的元素。
  • 如果s>k-1,整个列表的第k小元素就是被划分组左边部分的第k小的元素
  • s<k-1,就是数组右边部分的第(k-s)小元素。

    虽然我们没有彻底解决该问题,但是它的实例规模变得更小了,这个较小的实例可以用同样的方法解决,即递归求解。

2.快速选择(quickselect)伪代码实现

代码如下:

Quickselect (A[l..r],k)
//用基于划分的递归算法解决选择问题
//输入:可排序数组A[0..n-1]的子数组A[l..r]和整数k(1<=k<=r-l+1)
//输出:A[l..r]第k小元素的值
s<-LomutoPartition (A[l..r])//Lomuto划分
if s=l+k-1 returnA[s]
else if s>l+k-1 Quickselect (A[l..s-1],k)
else Quickselect (A[s+1..r],l+k-1-s)

实际上,该思想也可以不用递归法实现。对于非递归的版本,甚至不需要调整k值,只要一直做到s=k-1为止.

3.快速选择的时间效率

对于一个n元素数组进行划分总是要n-1次键值比较。如果不需要更多的迭代就能得到分割位置而使问题得解。在这种最好情况下,有C(n)=n-1∈O(n)。然而更普遍的是,算法有可能对给定数组产生一个极度不平衡的划分,这个划分的一部分是空而另一部分包含n-1个元素。在这个最坏情况下,n-1次迭代的每一次都会出现这种情况。这意味着 C(n)=(n-1)+(n-2)+ *** +1=(n-1)n/2∈O(n^2).

三、基于Lomuto划分实现快速选择算法(java实现代码)

import java.util.ArrayList;
import java.util.Scanner;

public class jianzhifa{
    public int LomutoPartition(int[] n, int l, int r){
        int p=n[l];
        int s=l;
        for(int i=l+1;i<=r;i++){
            if(n[i]<p) {
                s++;
                int tmp= n[i];
                n[i]=n[s];
                n[s] =tmp ;
            }
        }
        int tmp=n[l];
        n[l]=n[s];
        n[s]=tmp;
        return s;
    }

    public int Quickselect(int[] n, int l, int r, int k){
        int s=LomutoPartition(n,l,r);
        if(s==l+k-1){ return n[s];}
        else if(s>l+k-1){return Quickselect(n,l,s-1,k);}
        else{return Quickselect(n,s+1,r,l+k-1-s);}
    }

    public static void main(String[] args) {
        int []n={5,7,4,3,1,6,2,9,8,0};
        int l=0,r=9;
        System.out.println("请输入你要选择第几小的数字:");
        jianzhifa jz=new jianzhifa();
        Scanner sc=new Scanner(System.in);
        int k=sc.nextInt();
        int m=jz.Quickselect(n,l,r,k );
        System.out.println(m);
    }
}

Reference:
算法设计与分析基础(第3Anany Levitin潘彦 译)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值