每天学一点算法-线性查找算法

线性查找算法

定义

BFPRT 算法解决的问题十分经典,即从某n个元素的序列中选出第k大(第k小)的元素,通过巧妙的分析,BFPRT 可以保证在最坏情况下仍为线性时间复杂度。该算法的思想与快速排序思想相似,当然,为使得算法在最坏情况下,依然能达到o(n)的时间复杂度,五位算法作者做了精妙的处理。

步骤

1.将n个元素每 5 个一组,分成n/5(上界)组。

2.  取出每一组的中位数,任意排序方法,比如插入排序。

3.  递归的调用 selection 算法查找上一步中所有中位数的中位数,设为x,偶数个中位数的情况下设定为选取中间小的一个。

4.  用x来分割数组,设小于等于x的个数为k,大于x的个数即为n-k。

5.  若i==k,返回x;若i<k,在小于x的元素中递归查找第i小的元素;若i>k,在大于x的元素中递归查找第i-k 小的元素。

 终止条件:n=1 时,返回的即是i小元素。

时间复杂度

O(n)

代码

public class BFPRT {
	public int find(int[] a, int j) {
		System.out.println("================================");
		System.out.println("待查找的数组 : ");
		print(a);
		System.out.println("找出第 " + j + "小的数");
		if (a.length / 5 == 1) {
			return a[a.length - 1];
		}

		int x = getCenterForArray(a);

		// 4. 用x来分割数组,设小于等于x的个数为k,大于x的个数即为n-k。
		List<Integer> left = new ArrayList<Integer>();
		List<Integer> right = new ArrayList<Integer>();

		for (int i = 0; i < a.length; i++) {
			if (a[i] <= x) {
				left.add(a[i]);
			} else {
				right.add(a[i]);
			}
		}
		// 5. 若i==k,返回x;若i<k,在小于x的元素中递归查找第i小的元素;若i>k,在大于x的元素中递归查找第i-k 小的元素。
		int K = left.size();
		if (j == K) {
			return x;
		} else if (j < K) {
			int[] leftArray = new int[left.size()];
			for (int i = 0; i < left.size(); i++) {
				leftArray[i] = left.get(i);
			}
			return find(leftArray, j);
		} else {
			int[] rightArray = new int[right.size()];
			for (int i = 0; i < right.size(); i++) {
				rightArray[i] = right.get(i);
			}
			return find(rightArray, j - K);
		}
	}

	// 得到整个数组的分割数
	private int getCenterForArray(int[] a) {
		// 1.将n个元素每5个一组,分成n/5(上界)组,最后的一个组的元素个数为n%5,有效的组数为n/5。
		int length = a.length;
		int num = length / 5;
		int[] centers = new int[num];
		for (int i = 0; i < num; i++) {
			// 取每一组中的中位数
			int center = getCenterPerArray(a, 5 * i, 5 * (i + 1) - 1);
			centers[i] = center;
			System.out.println("该数组的中位数 : " + center);
		}
		System.out.println("中位数的集合 : ");
		print(centers);
		// 3. 递归的调用 selection 算法查找上一步中所有中位数的中位数,设为x,偶数个中位数的情况下设定为选取中间小的一个。
		new Selectionsort().sort(centers);
		System.out.println("经过选择排序后的中位数数组 : ");
		print(centers);
		int x = 0;

		if (centers.length % 2 == 0) {
			// 偶数个中位数
			x = min(centers[centers.length / 2 - 1],
					centers[centers.length / 2]);
		} else {
			x = centers[centers.length / 2];
		}
		System.out.println("定义的x 为 : " + x);

		return x;
	}

	private int min(int a, int b) {
		if (a >= b) {
			return b;
		} else {
			return a;
		}
	}

	// 得到每个子数组中的中位数
	private int getCenterPerArray(int[] a, int start, int end) {
		// 2.取出每一组的中位数,最后一个组的不用计算中位数,任意排序方法,这里的数据比较少只有5个,可以用简单的冒泡排序或是插入排序。
		int[] b = new int[end - start + 1];
		int index = 0;
		for (int i = start; i <= end; i++) {
			b[index++] = a[i];
		}
		System.out.println("被分割的数组 : ");
		print(b);
		new Insertsort().sort(b);
		return b[b.length / 2];
	}

	public static void main(String[] args) {
		// 求数组中第7小的数

		int[] datas = { 4, 1, 2, 56, 24, 5, 6, 97, 8, 0, 4, 8, 6, 2, 3, 6, 1,
				9, 3, 4, 6, 2 };
		int index = 8;
		int findX = new BFPRT().find(datas, index);
		datas = new QuickSort().sort(datas);
		print(datas);
		System.out.println("第" + index + "小的数为 : " + findX);
	}

	public static void print(int[] datas) {
		for (int i = 0; i < datas.length; i++) {
			System.out.print(datas[i] + " ");
		}
		System.out.println("");
	}

}

输出

================================
待查找的数组 : 
4 1 2 56 24 5 6 97 8 0 4 8 6 2 3 6 1 9 3 4 6 2 
找出第 8小的数
被分割的数组 : 
4 1 2 56 24 
该数组的中位数 : 4
被分割的数组 : 
5 6 97 8 0 
该数组的中位数 : 6
被分割的数组 : 
4 8 6 2 3 
该数组的中位数 : 4
被分割的数组 : 
6 1 9 3 4 
该数组的中位数 : 4
中位数的集合 : 
4 6 4 4 
经过选择排序后的中位数数组 : 
4 4 4 6 
定义的x 为 : 4
================================
待查找的数组 : 
4 1 2 0 4 2 3 1 3 4 2 
找出第 8小的数
被分割的数组 : 
4 1 2 0 4 
该数组的中位数 : 2
被分割的数组 : 
2 3 1 3 4 
该数组的中位数 : 3
中位数的集合 : 
2 3 
经过选择排序后的中位数数组 : 
2 3 
定义的x 为 : 2
================================
待查找的数组 : 
4 4 3 3 4 
找出第 2小的数
0 1 1 2 2 2 3 3 4 4 4 5 6 6 6 6 8 8 9 24 56 97 
第8小的数为 : 4

    原文作者:查找算法
    原文地址: https://blog.csdn.net/itfootball/article/details/41575059
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞