剑指offer_数组---最小的K个数

题目描述

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

解题思路

实际上考察的是对排序的理解。这里我用以下几种方法去做
1,用快速排序的方式
2,用堆排序的方式
3,用Array.sort函数
4,用优先队列
5,用归并排序的方式

代码实现

/**
 * 
 */
package 数组;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Scanner;

/**
 * <p>
 * Title:GetLeastNumbers
 * </p>
 * <p>
 * Description:
 * </p>
 * 
 * @author 田茂林
 * @data 2017年8月26日 上午11:48:20
 */
public class GetLeastNumbers {
    public static ArrayList<Integer> GetLeastNumber(int[] input, int k) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        if (input == null || input.length < 1) {
            return list;
        }
        if (k < 1 || k > input.length) {
            return list;
        }
        // 插入排序方式
        // insert(input);
        // 快速排序方式
        // fastsort(input, 0, input.length - 1);
        // 堆排序方式
        //heap(input);
        // 归并排序方式
        //merge(input);
        // 优先队列方式
        priority(input);
        // Arrays.sort函数
        //Func(input);
        for (int i = 0; i < k; i++) {
            list.add(input[i]);
        }
        return list;

    }

    public static void insert(int[] array) { // ==============================插入排序方式================最好n,最坏n^2
        int insElement = 0;
        for (int i = 1; i < array.length; i++) {
            insElement = array[i]; // 抓到的手牌
            int j = i - 1;
            while (j >= 0 && array[j] > insElement) { // 和之前的底牌逐个比较
                array[j + 1] = array[j];
                j--;
            }
            array[j + 1] = insElement;
        }

    }

    public static void fastsort(int[] array, int left, int right) { // ===================================快速排序方式nlogn
        int preindex = 0; // 基准的索引
        if (left < right) {
            preindex = partition(array, left, right);
            fastsort(array, left, preindex - 1); // 基准左边排序
            fastsort(array, preindex + 1, right); // 基准右边排序

        }

    }

    public static int partition(int[] array, int left, int right) {
        int temp = array[left];
        int i = left;
        int j = right;
        while (i != j) {
            while (array[j] >= temp && i < j) {
                j--;

            }
            while (array[i] <= temp && i < j) {
                i++;
            }
            if (i < j) {
                exchange(array, i, j);
            }
        }
        array[left] = array[i];
        array[i] = temp;
        return i;

    }

    public static void merge(int[] array) { // ===========================================================// 归并排序方式
        int[] temp = new int[array.length];
        sort(array, 0, array.length - 1, temp);

    }

    private static void sort(int[] arr, int left, int right, int[] temp) { // 递归来归并
        if (left < right) {
            int mid = (left + right) / 2;
            sort(arr, left, mid, temp);// 左边归并排序,使得左子序列有序
            sort(arr, mid + 1, right, temp);// 右边归并排序,使得右子序列有序
            merge(arr, left, mid, right, temp);// 将两个有序子数组合并操作
        }
    }

    private static void merge(int[] arr, int left, int mid, int right,
            int[] temp) { // ===========================合并有序数组函数
        int i = left;// 左序列指针
        int j = mid + 1;// 右序列指针
        int t = 0;// 临时数组指针
        while (i <= mid && j <= right) {
            if (arr[i] <= arr[j]) {
                temp[t++] = arr[i++];
            } else {
                temp[t++] = arr[j++];
            }
        }
        while (i <= mid) {// 将左边剩余元素填充进temp中
            temp[t++] = arr[i++];
        }
        while (j <= right) {// 将右序列剩余元素填充进temp中
            temp[t++] = arr[j++];
        }
        t = 0;
        // 将temp中的元素全部拷贝到原数组中
        while (left <= right) {
            arr[left++] = temp[t++];
        }
    }

    public static void heap(int[] array) { // ===============================================================堆排序方式
        int heapsize = array.length;
        buildheap(array, heapsize); // 建好了堆(大顶堆)
        // ================================================
        for (int i = 0; i < heapsize; i++) {
            System.out.print(array[i]);
        }// 测试点,打印堆:
            // ====================================================
        for (int i = heapsize; i > 1; i--) { // 堆顶和堆底互换,堆顶的索引是1,但是对应数组array[0]
            exchange(array, 0, i - 1); // 将堆顶元素(当前最大值)与堆的最后一个元素互换(该操作很有可能把后面元素的稳定性打乱,所以堆排序是不稳定的排序算法)
            heapsize--; // 从堆中去掉最后一个元素
            heapify(array, 0, heapsize); // 从新的堆顶元素开始进行堆调整,
        }
    }

    public static void buildheap(int[] array, int heapsize) { // 建堆函数

        for (int i = heapsize / 2; i >= 1; i--) { // 注意,这里的i用的是堆的标号方式,从1开始。但在堆里的索引1对应数组array[0],所以传入的为i-1
            heapify(array, i - 1, heapsize);
        }
    }

    public static void heapify(int[] array, int i, int heapsize) { // 堆调整函数(这里使用的是最大堆),也就是根是大于左右子树的
        int leftchild = 2 * i + 1; // 左孩子索引
        int rightchild = 2 * i + 2; // 右孩子索引
        int largest = 0; // 选出当前结点与左右孩子之中的最大值
        if (leftchild < heapsize && array[leftchild] > array[i]) { // 当前节点的左孩子比当前节点大
            largest = leftchild;
        } else
            largest = i;
        if (rightchild < heapsize && array[rightchild] > array[largest]) {
            largest = rightchild;
        } // 以上的两个if判断出三者之中最大的,没有管谁是第二大的
        if (largest != i) {
            exchange(array, i, largest); // 把当前结点和它的最大(直接)子节点进行交换
            heapify(array, largest, heapsize);
        }
    }

    public static void priority(int[] array) { // 优先队列排序方式
        PriorityQueue<Integer> queue = new PriorityQueue<Integer>();
        for (int i = 0; i < array.length; i++) {
            queue.offer(array[i]);
        }
        for (int i = 0; i < array.length; i++) {
            array[i] = queue.poll();
        }

    }

    public static void Func(int[] array) { // Array.sort方式
        Arrays.sort(array);
        /*
         * java中Arrays.sort使用了两种排序方法,快速排序和优化的合并排序。
         * 快速排序主要是对哪些基本类型数据(int,short,long等)排序, 而合并排序用于对对象类型进行排序。
         * 使用不同类型的排序算法主要是由于快速排序是不稳定的,而合并排序是稳定的。
         * 这里的稳定是指比较相等的数据在排序之后仍然按照排序之前的前后顺序排列。 对于基本数据类型,稳定性没有意义,
         * 而对于对象类型,稳定性是比较重要的,因为对象相等的判断可能只是判断关键属性,最好保持相等对象的非关键属性的顺序与排序前一致;
         * 另外一个原因是由于合并排序相对而言比较次数比快速排序少,移动(对象引用的移动)次数比快速排序多,而对于对象来说,比较一般比移动耗时。
         * 补充一点合并排序的时间复杂度是n*logn, 快速排序的平均时间复杂度也是n*logn,但是合并排序的需要额外的n个引用的空间
         * ......
         */

    }

    public static void exchange(int[] array, int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        Scanner sc = new Scanner(System.in);
        String s = sc.nextLine();
        int k = sc.nextInt(); // 输入k的值
        String[] strs = s.split(" ");
        int[] array = new int[strs.length]; // 得到数组
        for (int i = 0; i < strs.length; i++) {
            array[i] = Integer.valueOf(strs[i]);
        }
        list = GetLeastNumber(array, k);
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }

    }

}
©️2020 CSDN 皮肤主题: 点我我会动 设计师: 上身试试 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值