剑指offer-面试题30-最小的K个数

1,O(n)的算法,只有当我们可以修改输入的数组时可用

package case30_GetLeastNum;

/**
 * 题目:输入n个整数,找出其中最小的k个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4
 * 方法:利用partition函数完成,采用这种思路是有限制的。我们需要修改输入的数组,因为函数Partition会调整数组中数字的顺序
 * 
 * 
 * @author WangSai
 *
 */
public class GetLeastNum1 {
	/**
	 * 
	 * @param arr[]
	 *            需要被寻找的数组
	 * @return 寻找出来满足条件的k个数字
	 */
	public static void main(String[] args) {
		int[] arr = { 4, 5, 1, 4, 4, 7, 3, 8 };
		int k = 4;
		getNumbers(arr, k);
	}

	// 通过partition函数,获取arrOut数组
	private static void getNumbers(int[] arr, int k) {
		// 异常值判断
		if (arr == null || arr.length < k || arr.length <= 0 || k <= 0)
			throw new IllegalArgumentException("输入的数组非法...");
		// 采用递归的方式完成
		int low = 0;
		int high = arr.length - 1;
		int index = partition(arr, low, high);
		while (index != k - 1) {
			if (index < k - 1) {
				low = index + 1;
				index = partition(arr, low, high);
			} else {
				high = index - 1;
				index = partition(arr, low, high);
			}
		}
		// 输出满足条件的数
		for (int i = 0; i < k; i++)
			System.out.print(arr[i] + " ");
	}

	// 快排中用到的partition函数
	private static int partition(int[] arr, int low, int high) {
		int pivotKey = arr[low];
		while (low < high) {
			while (low < high && arr[high] >= pivotKey)
				high--;
			arr[low] = arr[high];
			while (low < high && arr[low] <= pivotKey)
				low++;
			arr[high] = arr[low];
		}
		arr[low] = pivotKey;
		return low;
	}

}


2,O(nlogk)的算法,特别适合处理海量数据

package case30_GetLeastNum;

/**
 * 通过使用大顶堆完成。时间复杂度O(nlogk)
 * 
 * @author WangSai
 *
 */
public class ByHeap1 {

	// 1,创建容器,并填充arr的前K个元素
	// 2,对这[0...K-1]序列创建大顶堆
	// 3,原序列从第K个开始,与[0...K-1]序列作比较,若大于堆顶元素则进行替换
	// 4,替换完成后,对序列重新调整变成大顶堆
	// 3,4步骤循环进行
	public static void main(String[] args) {
		int[] arr = { 1,2,3,4,5,6,7,8,4,433,32,0 };
		int k = 4;
		getLeatK(arr, k);
		int[] arr1={1};
		k =1;
		getLeatK(arr1, k);
	}

	// 获取最小的K个元素的方法
	private static void getLeatK(int[] arr, int k) {
		// 异常情况检测
		if (arr == null || arr.length <= 0 || arr.length < k || k <= 0)
			throw new IllegalArgumentException("输入的参数非法,请重新检查...");
		// 创建容器,并填充arr的前K个元素
		int[] heap = new int[k];
		for (int i = 0; i < k; i++) {
			heap[i] = arr[i];
		}
		buildMaxTopHeap(heap);
		// arr中的元素从第K个开始依次与大顶堆heap作比较,如果大于对顶则替换,并对新的堆做处理。
		for (int i = k; i < arr.length; i++) {
			if (arr[i] < heap[0]) {
				heap[0] = arr[i];
				adjustDownToUp(heap, 0);
			}
		}
		for (int i = 0; i < heap.length; i++) {
			System.out.print(heap[i] + "     ");
		}
		System.out.println();
	}

	// 将含有K个元素的无序序列构建大顶堆
	private static void buildMaxTopHeap(int[] heap) {
		for (int i = heap.length / 2 - 1; i >= 0; i--) {
			// 自底向上调整堆
			adjustDownToUp(heap, i);
		}
	}

	// 自底向上调整堆为大顶堆
	private static void adjustDownToUp(int[] heap, int i) {
		// 判断该节点与其子节点的大小
		int temp = heap[i];
		for (int j = 2 * i + 1; j < heap.length - 1; j = 2 *j + 1) { //i为初始化为节点k的左孩子,沿节点较大的子节点向下调整
			if (j <= heap.length - 1 && heap[j] < heap[j + 1]) //取节点较大的子节点的下标
				j++;					//如果节点的右孩子>左孩子,则取右孩子节点的下标
			if (temp >= heap[j])		//根节点 >=左右子女中关键字较大者,调整结束
				break;
			//根节点 <左右子女中关键字较大者
			else {					
				heap[i] = heap[j];	//将左右子结点中较大值array[i]调整到双亲节点上
				i = j;				//【关键】修改k值,以便继续向下调整
			}
		}
		heap[i] = temp;				//被调整的结点的值放入最终位置
	}
}


3,O(n*k)的算法,用数组ArrayList保存K个数字,然后依次遍历数组arr中剩余的n-K个数字,依次比较并替换ArrayList中的最大值


package case30_GetLeastNum;

import java.util.ArrayList;

public class GetLeastNumByList {

	public static void main(String[] args) {
		//测试
		int[] arr = { 4, 5, 1, 4, 4, 7, 3, 8 };
		int k = 4;
		ArrayList<Integer> alist = getNum(arr, k);
		for (Integer num : alist)
			System.out.print(num + " ");

	}
	//获取满足条件的最小的k个数字
	private static ArrayList<Integer> getNum(int[] arr, int k) {
		// 判断异常情况
		if (arr == null || arr.length <= 0 || arr.length < k || k <= 0)
			throw new IllegalArgumentException("非法的输入参数...");
		// 新建容器,当容器中的数小于k的时候,直接添加。
		ArrayList<Integer> alist = new ArrayList<>();
		for (int i = 0; i < k; i++)
			alist.add(arr[i]);

		// 当容器满了之后,继续从剩下的arr[]数组的取出数。若取出来的数小于alist中最大的数,则直接替换掉。
		for (int j = k; j < arr.length; j++) {
			// 获取alist中最大的数值
			int indexOfMaxValue = getMaxIndex(alist);
			int max = alist.get(indexOfMaxValue);
			// 若arr中当前的值小于alist中的最大值,则替换掉alist中的最大值
			if (arr[j] < max) {
				alist.set(indexOfMaxValue, arr[j]);
			}
		}
		return alist;
	}

	// 获取arraylist中的最大数值的角坐标
	private static int getMaxIndex(ArrayList<Integer> alist) {
		int max = alist.get(0);
		int i = 0;
		for (int j = 1; j < alist.size(); j++) {
			if (alist.get(j) > max) {
				max = alist.get(j);
				i = j;
			}
		}
		return i;
	}
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值