分治法-线性时间选择

算法思想:分治法

实际问题:线性时间选择

编写语言:Java

问题描述

给定线性序集中 n 个元素和一个整数 k, 1 <= k <= n, 要求找出这 n 个元素中第 k 小的元素。即如果将这 n 个元素依其线性序排列时,排在第 k 个位置的元素即为要找的元素。

本篇博文代码会用到 分治法-快速排序 博文中用到的基准选择方法。 方法使用位置:代码第 25 行 方法实现位置:代码第 37 - 68 行

Java代码

import java.util.Random;

public class RandomizedSelect
{
    public static void main(String[] args)
    {
        int[] a = new int[] {1, 3, 2, 6, 5, 8, 4, 9, 7, 0};
        int t = select(a, 0, a.length - 1, 4); //选出第四大的数
        System.out.println(t);
    }

    //参数含义:a为待查询的数组,low为起点,high为终点,target为带查询的目标
    public static int select(int[] a, int low, int high, int target)
    {
        if(low == high)
            return a[low];
        //将数组以i为基准分为两部分,左边的都小于i,右边的都大于i
        //此处会用到快速排序算法中的划分方法来找基准
        int i = randPart(a, low, high);
        int length = i - low + 1; //数组左半部分的长度
        //如果第target小的数小于等于左半部分的长度,则这个数在此部分内
        if(target <= length)
            return select(a, low, i, target);
        //如果第target小的数大于左半部分的长度,则这个数在右半部分内,
        //且左半部分的数都小于第target小的数
        else
            return select(a, i + 1, high, target - length);
    }

    //划分寻找基准-随机化优化
    public static int randPart(int[] a, int low, int high)
    {
        Random r = new Random();
        //随机产生一个 low 到 high 的整数
        int flag = low + r.nextInt(high - low + 1);

        int pivot = a[flag];

        //实际还是相当于以a[0]为基准
        a[flag] = a[low];
        a[low] = pivot;


        while(low < high)
        {
            //1、从右往左找比基准小的数
            while(low < high && a[high] > pivot)
                high--;
            if(low < high)
                a[low] = a[high];
            //2、从左往右找比基准大的数
            while(low < high && a[low] <= pivot)
                low++;
            if(low < high)
                a[high] = a[low];
        }

        //将pivot放到low和high相遇的地方
        a[high] = pivot;

        return high;
    }
}

运行结果

结果示例