刷题记录13---数组中的第K个最大元素+除自身以外数组的乘积+搜索二维矩阵Ⅱ+寻找重复数+最长递增子序列

前言

所有题目均来自力扣题库中的hot 100,之所以要记录在这里,只是方便后续复习

215.数组中的第K个最大元素

题目:
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
解题思路:
【排序】思路比较简单,就是先对数组进行排序,根据排序结果返回对应位置的值即可。至于怎么排序就看个人实现,这里用了快速排序和冒泡排序;快速排序的思想大致是:随机选取一个中间值,将比中间值小的值都放到中间值的左侧,大的值都放到中间值的右侧,然后通过递归将左侧和右侧分别进行排序
代码(python):

class Solution(object):
    def findKthLargest(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        # 快排
        self.quickSort(nums, 0, len(nums) - 1)
        # 返回第k大的值
        return nums[len(nums) - k]

    def quickSort(self, nums, left, right):
        # 左边界大于了有边界,直接返回
        if left > right:
            return
        # 获取中间值 即左边界的值
        center_val = nums[left]
        # 定义两个指针分别在两个边界
        i = left
        j = right
        # 将 比中间值大的值全放到右侧,小的值全放到左侧 即进行分类
        # 只要左指针 和 右指针没有相遇
        while i < j:
            # 右指针开始滑动,直接找到一个 比 中间值小的值 主要要时刻保持,左指针小于右指针
            while nums[j] >= center_val and j > i:
                j -= 1
            # 左指针开始滑动,直接找到一个 比 中间值大的值 主要要时刻保持,左指针小于右指针
            while nums[i] <= center_val and j > i:
                i += 1
            # 如果找到的两个值,符合 左指针 小于右指针  交换两个值,这样就完成了一次 大小值的左右分类
            if j > i:
                nums[i], nums[j] = nums[j], nums[i]
        # 最后分类结束一定是两个指针相遇了,此时指针左面的 都小于等于中间值, 右边都大于等于中间值 
        # 即此时指针位置就是中间值所应该处于的位置  我们将中间值 和 指针位置的值 交换 
        nums[left], nums[i] = nums[i], nums[left]
        # 用递归 将左侧 和右侧的数组分别进行排序
        self.quickSort(nums, left, i - 1)
        self.quickSort(nums, i + 1, right)

代码(java):

class Solution {
    public int findKthLargest(int[] nums, int k) {
        mySort(nums);
        return nums[k - 1];
    }

    private void mySort(int[] nums){
        for(int i = 0; i < nums.length ; i++){
            for(int j = 0; j < nums.length - i - 1; j ++){
                if (nums[j] < nums[j + 1]){
                    int k = nums[j];
                    nums[j] = nums[j + 1];
                    nums[j + 1] = k;
                }
            }
        }
    }
}

知识点:

原题链接:数组中的第K个最大元素

238.除自身以外数组的乘积

题目:
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。
题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请不要使用除法,且在 O(n) 时间复杂度内完成此题。
示例 1:
输入: nums = [1,2,3,4]
输出: [24,12,8,6]
示例 2:
输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]
解题思路:
【数组遍历】我们可以将每个位置的值都作为一个分隔符,除去该位置的数组乘积也就是左侧部分的乘积乘右侧部分的乘积,我们可以选择遍历数组,在每个位置分别去计算左右侧乘积然后相乘,但是这样时间复杂度度是On2的;某个位置的左侧乘积等于左侧位置的左侧部分乘积乘左侧位置的值,知道这个关系后我们可以定义一个左侧列表,每个位置代表着该位置左侧部分的乘积,从左到右递推出每个位置的值,而索引0位置的值默认就是1,因为其没有左侧的值,同样的方式我们可以得到右侧部分乘积的列表,最后我们将两个列表对应位置相乘就得到了对应位置的左右侧乘积也就是结果值;刚刚的方式的空间复杂度是On,那怎么用O1呢,我们只能不用左右列表存储左右侧乘积,可以先用结果列表存储左侧乘积,而在递推右侧乘积时直接更新结果列表对应位置的值,也就是直接求得结果值放到对应位置,这个过程用一个变量存储右侧乘积,并实时更新用于下一个位置的递推
代码(python):

class Solution(object):
    def productExceptSelf(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        #定义结果数组
        result = [1] * len(nums)
        # 数组存储  每个位置左侧元素的乘积
        # 索引0位置没有左侧元素,所以值为 1
        result[0] = 1
        # 从左到右递推每个位置  左侧元素的乘积 = 上个位置左侧元素的乘积 * 上个位置的值
        for i in range(1 , len(result)):
            result[i] = result[i - 1] * nums[i - 1]
        # 索引 length -1位置 没有右侧元素,所以值为1
        right_result = 1
        # 从右向左递推每个位置  结果值 = 左侧元素乘积(结果数组此位置的值) * 右侧元素乘积
        for i in range(len(result) - 1, -1, -1):
            result[i] = result[i] * right_result
            # 更新右侧元素乘积,用于后续的递推 右侧元素的乘积 = 上一个位置右侧元素的乘积 * 上一个位置的值
            right_result = right_result * nums[i]
        
        return result

代码(java):

class Solution {
    public int[] productExceptSelf(int[] nums) {
        int len = nums.length;

        int[] res = new int[len];

        for(int i = 0; i < len; i++){
            if (i == 0){
                res[i] = 1;
            }else{
                res[i] = res[i - 1] * nums[i - 1];
            }
        }

        int rVal = 0;
        for(int i = len - 1; i >= 0; i--){
            if(i == len -1){
                rVal = 1;
            }else{
                rVal = rVal * nums[i + 1];
                res[i] = res[i] * rVal;
            }
        }
        return res;
    }
}

知识点:

原题链接:除自身以外数组的乘积

搜索二维矩阵Ⅱ

题目:
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
示例 1:
搜索二维矩阵Ⅱ

输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
输出:true
示例 2:
输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 20
输出:false
解题思路:
【Z字形搜索】正常思路是从头到尾遍历一遍,这需要Omn时间复杂度;因为是有序的所以联想到二分查找,我们可以对每行都进行二分查找,这需要Omlogn时间复杂度;我们可以从网格的右上角开始搜索,如果当前值和目标值相等就直接返回,如果当前值大于目标值,就向左移动,如果当前值小于目标值,就向下移动(因为索引只能左移和下移,同行左移才会变小,同列下移才会变大),直到找到该值或者超出有效范围
代码(python):

class Solution(object):
    def searchMatrix(self, matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        # 定义起始位置  右上角
        i = 0
        j = len(matrix[0]) - 1
        # 只要i 和 j 符合范围
        while i < len(matrix) and j >=0:
            # 如果当前值 和 目标值相等 则返回true
            if matrix[i][j] == target:
                return True
            # 如果当前值 大于 目标值 就向左移动
            elif matrix[i][j] > target:
                j -= 1
            # 如果当前值 小于 目标值 就向下移动
            else:
                i += 1
        # 如果i 或j 超出了范围还未返回 则返回False 
        return False

代码(java):

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int x = 0;
        int y = matrix[0].length - 1;

        while(x < matrix.length && y >= 0){
            if(matrix[x][y] == target){
                return  true;
            }else if(matrix[x][y] > target){
                y --;
            }else{
                x ++;
            }
        }
        return false;
    }
}

知识点:

原题链接:搜索二维矩阵Ⅱ

287.寻找重复数

题目:
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。
你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。
示例 1:
输入:nums = [1,3,4,2,2]
输出:2
示例 2:
输入:nums = [3,1,3,4,2]
输出:3
解题思路:
【快慢指针】如果我们把每个值都看出一个节点,他们可以组成一个链表关系,其指向的节点就是当前值,如果有两个一样的值,也就是说有两个指向某个节点,那么也就是说这个链表有环,这个题就变成了找到链表的成环处,成环节点就是重复值;找到链表的成环节点可以用双指针,快指针一次走两步,慢指针一次走一步,当两个指针相遇,让慢指针从头开始,慢指针一次走一步,快指针一次也走一步,再次相遇即是成环节点
代码(python):

class Solution(object):
    def findDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """

        slow = nums[0]
        fast = nums[nums[0]]

        while slow != fast:
             slow = nums[slow]
             fast = nums[nums[fast]]
        
        slow = 0

        while slow != fast:
            slow = nums[slow]
            fast = nums[fast]
        
        return slow

代码(java):

class Solution {
    public int findDuplicate(int[] nums) {
        // 定义快慢指针
        int slow = 0;
        int fast = 0;
        // 慢指针一次走一步,快指针一次走两步,走到两个指针相遇
        slow = nums[slow];
        fast = nums[nums[fast]];

        while(slow != fast){
            slow = nums[slow];
            fast = nums[nums[fast]];
        }
        // 让慢指针从头开始走,这次两个指针每次都走一步 直到再次相遇
        slow = 0;
        while(slow != fast){
            slow = nums[slow];
            fast = nums[fast];
        }
        //返回慢指针即成环处   注意是慢指针 而不是慢指针位置的值
        return slow;
    }
}

知识点:

原题链接:寻找重复数

300.最长递增子序列

题目:
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
示例 1:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:
输入:nums = [0,1,0,3,2,3]
输出:4
示例 3:
输入:nums = [7,7,7,7,7,7,7]
输出:1
解题思路:
【贪心+二分查找】容易想到的方法是动态规划,每个位置的最长递增子序列的值是他前面位置的序列种从后往前找到所有比他小的位置的最长递增子序列的值中的最大值再加上1,但这种需要On2的时间复杂度;另一个办法是我们假设维护一个递增序列,怎么让他最长?答案是让它每次增加的幅度最小即可(贪心思想),所以我们可以维护一个这样的序列,初始值序列里只有数组种第一个值,此时长度为1,遍历数组中后面的值,如果该值大于该序列的最后一个值就直接加入末尾,同时长度加1;如果不大于序列最后一个值,就找到最后一个比他小的值的位置,把这个位置后面一个位置的值替换成该值,换句话说找到第一个比他大的值(二分查找),将该值替换成当前值,这样就较少了原来序列增加的幅度,但这种情况长度不变,因为没有新追加值,只是减小的幅度,就这样数组遍历完成,最后得到的长度(序列的长度)就是最长递增子序列的长度(虽然我们此时维护的序列并不是最长递增子序列)
代码(python):

class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """

        res = []
        max_len = 1
        res.append(1)

        for i in range(1, len(nums)):
            cur_len = 1
            for j in range(0, i):
                if nums[i] > nums[j]:
                    val = res[j] + 1
                    cur_len = max(cur_len, val)
            res.append(cur_len)
            max_len = max(max_len, cur_len)
        
        return max_len

代码(java):

class Solution {
    public int lengthOfLIS(int[] nums) {
        // 如果数组长度为1 直接返回1
        if(nums.length == 1){
            return 1;
        }
        //定义结果值 初始为1
        int len = 1;
        // 定义序列,用来存放 最小幅度 递增序列
        int[] arr = new int[nums.length + 1];
        // 递增序列的位置1 初始值是 参数数组的索引0的值
        arr[len] = nums[0];
        //从1 开始遍历参数数组
        for(int i = 0; i < nums.length; i ++){
            // 如果当前值大于递增序列的最后一个值(结果值位置的值),就直接将其添加到末尾,并且结果值+1
            if(nums[i] > arr[len]){
                arr[len + 1] = nums[i];
                len ++;
            // 如果小于的话, 就通过二分查找 找到最后一个比当前值小的值,然后将其后面的值替换成当前值
            //               如果找不到比他小的值,那就是都比他大,那就假设比他小的值位置是0 ,从而替换1位置的值
            }else{
                int left = 1;
                int right = len;
                int pos = 0;
                // 注意二分查找的条件是 左指针 小于等有 右指针 
                while(left <= right){
                    int mid = (left + right) / 2;
                    if(arr[mid] < nums[i]){
                        pos = mid;
                        left = mid + 1;
                    }else{
                        right = mid - 1;
                    }    
                }

                arr[pos + 1] = nums[i];
            }
        } 
        //返回遍历后的 结果值
        return len;
    }
}

知识点:

原题链接:最长递增子序列

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值