最小的K个数(数组)

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

思路一:

import java.util.ArrayList;
import java.util.Arrays;

public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> list = new ArrayList<>();
        if (input.length <= 0 || k <= 0 || k > input.length) return list;
        Arrays.sort(input);
        for (int i = 0; i < k; i++)
            list.add(input[i]);
        return list;
    }
}

思路二:

快排,时间复杂度为O(NlogN)~O(N^2),空间复杂度为O(logN)~O(N)

import java.util.ArrayList;

public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> list = new ArrayList<>();
        if (input.length <= 0 || k <= 0 || k > input.length) return list;
        QuickSort(input, 0, input.length - 1);
        for (int i = 0; i < k; i++)
            list.add(input[i]);
        return list;
    }
    private void QuickSort(int[] arr, int low, int high)
    {
        int index;
        while (low < high)
        {
            index = partition(arr, low, high);
            QuickSort(arr, low, index - 1);
            low = index + 1; //优化1:避免了尾递归
        }
    }
    private int partition(int[] arr, int low, int high)
    {
        int flag = arr[low];
        while(low < high)
        {
            while (low < high && arr[high] >= flag)
                high--;
            arr[low] = arr[high];
            while(low < high && arr[low] <= flag)
                low++;
            arr[high] = arr[low];
        }
        arr[low] = flag;
        return low;
    }
}


快速选择:只作一次递归调用

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Solution {
    public static void main(String[] args) {
        Solution s = new Solution();
        int[] arr = {4,5,1,6,2,7,3,8,8,4,2};
        List<Integer> list = new ArrayList<>();
        list = s.GetLeastNumbers_Solution(arr, 5);
        System.out.println(Arrays.toString(list.toArray())); //输出:[1, 2, 2, 3, 4]
    }
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> list = new ArrayList<>();
        if (input.length <= 0 || k <= 0 || k > input.length) return list;
        QuickSort(input, 0, input.length - 1, k);
        for (int i = 0; i < k; i++)
            list.add(input[i]);
        return list;
    }
    private void QuickSort(int[] arr, int low, int high, int k)
    {
        if (low < high)
        {
            int m = (high + low) / 2;
            if (arr[m] < arr[low]) swap(arr, low, m);
            if (arr[high] < arr[low]) swap(arr, low, high);
            if (arr[high] < arr[m]) swap(arr, m, high); //此时,m端的值为中间值

            swap(arr, m, high - 1);
            int flag = arr[high - 1];

            int i = low;
            int j = high - 1;
            for ( ; ; )
            {
                while(arr[++i] < flag);
                while (arr[--j] > flag);
                if (i < j)
                    swap(arr, i, j);
                else
                    break;
            }
            swap(arr, i, high - 1);

            if (k <= i)
                QuickSort(arr, low, i - 1, k);
            else
                QuickSort(arr, i + 1, high, k);
        }
    }
    private void swap(int[] arr, int i, int j)
    {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}


思路三:

堆排序

构建一个大顶堆的时间复杂度为O(N),重建大顶堆的时间复杂度为O(NlogN),总时间复杂度为O(NlogN)

空间复杂度O(1)

import java.util.ArrayList;

public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> list = new ArrayList<>();
        if (input.length <= 0 || k <= 0 || k > input.length) return list;
        HeapSort(input);
        for (int i = 0; i < k; i++)
            list.add(input[i]);
        return list;
    }
    private void HeapSort(int[] arr) // 从小到大排序,建立一个大顶堆;从大到小排序,建立一个小顶堆
    {
        for (int i = arr.length/2-1; i >=0; i--)  //非终端节点的范围0~length/2-1
        {
            HeapAdjust(arr, i, arr.length);
        }
        for (int i = arr.length - 1; i >= 0; i--)
        {
            swap(arr, 0, i);
            HeapAdjust(arr, 0, i);
        }
    }
    private void HeapAdjust (int[] arr, int s, int m)
    {
        int temp = arr[s];
        int j = 2 * s + 1;
        for (; j <= m - 1; j = 2 * j + 1)
        {
            if (j < m - 1 && arr[j] < arr[j + 1]) ++j;
            if (temp >= arr[j]) break;
            arr[s] = arr[j];
            s = j;
        }
        arr[s] = temp;
    }
    private void swap(int[] arr, int i, int j)
    {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

思路四:

改进的堆排序

对数组的前k个数建立一个大顶堆,将剩余N-K个数,依次与堆顶比较,若比堆顶小,则替换堆顶,再调整堆。最终将堆里的k个数加入列表中。

建立一个大顶堆的时间复杂度为O(k),调整大顶堆的时间复杂度为O((N-k)logk),总的时间复杂度为O(Nlogk)

空间复杂度为O(k+1)即O(k)

最后输出的k个数可以不按照从小到大输出

import java.util.ArrayList;

public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> list = new ArrayList<>();
        if (input.length <= 0 || k <= 0 || k > input.length) return list;
        HeapSort(input, k);
        for (int i = 0; i < k; i++)
            list.add(input[i]);
        return list;
    }
    private void HeapSort(int[] arr, int k) // 从小到大排序,建立一个大顶堆;从大到小排序,建立一个小顶堆
    {
        int[] karr = new int[k];
        for (int i = 0; i < k; i ++)
            karr[i] = arr[i];
        for (int i = k/2-1; i >=0; i--)  //非终端节点的范围0~length/2-1
        {
            HeapAdjust(karr, i, k);// // karr为大顶堆
        }
        for (int i = k; i < arr.length; i++)
        {
            if (arr[i] < karr[0])
            {
                karr[0] = arr[i];
                HeapAdjust(karr, 0, k);
            }
        }
        for (int i = 0; i < k; i++)
            arr[i] = karr[i];
    }
    private void HeapAdjust (int[] arr, int s, int m)
    {
        int temp = arr[s];
        int j = 2 * s + 1;
        for (; j <= m - 1; j = 2 * j + 1)
        {
            if (j < m - 1 && arr[j] < arr[j + 1]) ++j;
            if (temp >= arr[j]) break;
            arr[s] = arr[j];
            s = j;
        }
        arr[s] = temp;
    }
}


如果要求输出的k个数从小到大输出,在上述基础上加个循环

import java.util.ArrayList;

public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> list = new ArrayList<>();
        if (input.length <= 0 || k <= 0 || k > input.length) return list;
        HeapSort(input, k);
        for (int i = 0; i < k; i++)
            list.add(input[i]);
        return list;
    }
    private void HeapSort(int[] arr, int k) // 从小到大排序,先建立一个大顶堆;从大到小排序,先建立一个小顶堆
    {
        int[] karr = new int[k];
        for (int i = 0; i < k; i ++)
            karr[i] = arr[i];
        for (int i = k/2-1; i >=0; i--)  //非终端节点的范围0~length/2-1
        {
            HeapAdjust(karr, i, k);
        }
        for (int i = k; i < arr.length; i++)
        {
            if (arr[i] < karr[0])
            {
                karr[0] = arr[i];
                HeapAdjust(karr, 0, k);
            }
        }
        for (int i = k - 1; i >= 0; i--)//加循环
        {
            swap(karr, 0, i);
            HeapAdjust(karr, 0, i);
        }
        for (int i = 0; i < k; i++)
            arr[i] = karr[i];
    }
    private void HeapAdjust (int[] arr, int s, int m)
    {
        int temp = arr[s];
        int j = 2 * s + 1;
        for (; j <= m - 1; j = 2 * j + 1)
        {
            if (j < m - 1 && arr[j] < arr[j + 1]) ++j;
            if (temp >= arr[j]) break;
            arr[s] = arr[j];
            s = j;
        }
        arr[s] = temp;
    }
    private void swap(int[] arr, int i, int j)
    {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}





对于一个有序矩阵,我们可以使用最小堆来找到第 k 个最小数组和。首先,我们将矩阵的第一行中的每个元素以及对应的列索引加入到最小堆中。然后,我们执行以下步骤 k 次: 1. 从最小堆中弹出堆顶元素(即当前的最小数组和),记为 curr_sum。 2. 如果弹出的元素所在列索引不是该行的最后一列,则将下一个元素(即同一行的下一个元素)以及对应的列索引加入到最小堆中,它们的和为 next_sum = curr_sum + matrix[row][col + 1]。 3. 如果弹出的元素所在列索引是该行的最后一列,并且还有下一行,则将下一行的第一个元素以及对应的列索引加入到最小堆中,它们的和为 next_sum = curr_sum + matrix[row + 1][0]。 4. 重复步骤 1-3 直到找到第 k 个最小数组和。 以下是一个示例代码实现: ```python import heapq def kthSmallest(matrix, k): if not matrix or not matrix[0]: return None m, n = len(matrix), len(matrix[0]) heap = [] for j in range(n): heapq.heappush(heap, (matrix[0][j], 0, j)) while k > 1: curr_sum, row, col = heapq.heappop(heap) if col < n - 1: next_sum = curr_sum + matrix[row][col + 1] heapq.heappush(heap, (next_sum, row, col + 1)) if col == n - 1 and row < m - 1: next_sum = curr_sum + matrix[row + 1][0] heapq.heappush(heap, (next_sum, row + 1, 0)) k -= 1 return heapq.heappop(heap)[0] # 示例输入 matrix = [[1, 3, 5], [6, 7, 12], [11, 14, 14]] k = 3 # 示例输出 print(kthSmallest(matrix, k)) # 输出: 5 ``` 在上述示例中,我们使用最小堆来找到有序矩阵中的第 3 个最小数组和。初始时,堆中的元素为 [(1, 0, 0), (3, 0, 1), (5, 0, 2)]。在执行第一次循环后,堆中的元素为 [(3, 0, 1), (5, 0, 2), (6, 1, 0)]。最后,弹出堆顶元素 5,即为第 3 个最小数组和。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值