实验室暑假学习第一周算法总结

LeetCode 378. 有序矩阵中第K小的元素

难度 中等

给定一个n × n矩阵,其中每行和每列元素均按升序排序,找到矩阵中第k小的元素。
请注意,它是排序后的第k小元素,而不是第k个不同的元素。

示例

matrix = [
   [ 1,  5,  9],
   [10, 11, 13],
   [12, 13, 15]
],
k = 8,

返回 13。

提示
你可以假设 k 的值永远是有效的,1 ≤ k ≤ n²


解法一 将矩阵转化为一维数组并排序
语言:Python 3

class Solution:
    def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
        matrix = [i for j in matrix for i in j]
        matrix = sorted(matrix)
        return matrix[k - 1]

解法二 二分查找法
语言:C

int kthSmallest(int** matrix, int matrixSize, int* matrixColSize, int k) {
    int left = matrix[0][0], right = matrix[matrixSize - 1][matrixSize - 1];
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (check(matrix, matrixSize, mid, k)) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }
    return left;
}

int check(int** matrix, int matrixSize, int mid, int k) {
    int num = 0;
    int i = matrixSize - 1, j = 0;
    while (i >= 0 && j < matrixSize) {
        if (matrix[i][j] <= mid) {
            j++;
            num += i + 1;
        } else {
            i--;
        }
        if (num >= k)
            return 0;
    }
    return 1;
}

依据题意,矩阵里的元素是自左上至右下递增的,即matrix[0][0]为最小值,记为leftmatrix[n-1][n-1]为最大值,记为right。这样,所要查找的元素x必定满足left ≤ x ≤ right
与此同时,我们可以发现,任取一个数midmid满足left ≤ mid ≤ right,都有如下性质:矩阵中不大于mid的数全部分布在矩阵的左上角,大于mid的数全部分布在矩阵的右下角。
下面借用一下LeetCode官方题解给出的图来帮助理解,取mid = 8,则矩阵被一条锯齿线分为左上和右下两个部分。
在这里插入图片描述
将起始位置设置为matrix[n-1][0],初始化计数变量num = 0,设当前位置为matrix[i][j]。若matrix[i][j] ≤ mid,则将当前所在列的不大于mid的数的数量(即i + 1)累加到计数变量num中,并向右移动(即j自加1);否则,向上移动(即i自减1)。这样,只需要自下而上地沿着锯齿线走一遍,即可统计出矩阵中有多少个数字不大于mid了。
计算矩阵中有多少数不大于mid,若数量不小于k,则所要查找的元素x不大于mid;若数量小于k,则所要查找的元素x大于mid。二分查找结束后,即可找出x

LeetCode 697. 数组的度

难度 简单

给定一个非空且只包含非负数的整数数组nums,数组的度的定义是指数组里任一元素出现频数的最大值。
你的任务是找到与nums拥有相同大小的度的最短连续子数组,返回其长度。

示例1

输入: [1, 2, 2, 3, 1]
输出: 2
解释: 
输入数组的度是2,因为元素1和2的出现频数最大,均为2.
连续子数组里面拥有相同度的有如下所示:
[1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [2, 2]
最短连续子数组[2, 2]的长度为2,所以返回2.

示例2

输入: [1,2,2,3,1,4,2]
输出: 6

注意
nums.length在1到50,000区间范围内。
nums[i]是一个在0到49,999范围内的整数。


解法 HashMap
语言:C

int findShortestSubArray(int* nums, int numsSize) {
    int count[50000] = {0};
    int left[50000] = {0};
    int right[50000] = {0};
    int degree = 0;                             /* 初始化数组的度 */
    int min = numsSize;                         /* 初始化最短连续子数组的长度 */
    for (int i = 0; i < numsSize; i++) {
        count[nums[i]]++;
        if (count[nums[i]] == 1)                /* 首次出现记录左值 */
            left[nums[i]] = i;
        right[nums[i]] = i;                     /* 记录右值 */
        if (count[nums[i]] > degree)            /* 记录数组的度 */
            degree = count[nums[i]];
    }
    for (int i = 0; i < numsSize; i++)
        if (count[nums[i]] == degree)
            min = right[nums[i]] - left[nums[i]] + 1 < min ? (right[nums[i]] - left[nums[i]] + 1) : min;
    return min;
}

首先定义三个数组count, left, right。其中count的作用是计数,以给定数组nums的元素nums[i]作为下标,其对应的元素值即为nums[i]的值在数组nums中出现的次数。leftright分别记录nums[i]的值第一次出现和最后一次出现时所对应的下标i,我将其称之为元素nums[i]对应的的左值和右值。接下来初始化数组的度degree = 0,初始化最短连续子数组的长度min = numsSize
nums数组进行遍历,记录每个元素出现的次数和其对应的左右值。在记录左右值的时候要注意,如果某个元素是第一次记录,则要记录左值和右值;否则,只刷新右值即可,左值不需要更改。每遍历完一个元素,刷新一次数组的度,即如果count[nums[i]] > degree,令degree = count[nums[i]]
最后,遍历count数组,如果count[nums[i]]degree相等,则取right[nums[i]] - left[nums[i]] + 1min的较小者赋值给min。这样,完全遍历count之后得到的min即为本题所求。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值