LeetCode刷题日记——初级算法

LeetCode刷题日记——初级算法

1.买卖股票的最佳时机

题目大意

给定一个数组 prices ,其中 prices[i] 是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例:
输入: prices = [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

动态规划解题

​ 建立一个二维数组,dp[i][0]代表第i天手中没有股票时的现有利润,dp[i][1] 代表第i天手中有股票时的最大利润,则递推公式如下:dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]),dp[i][1]=max(dp[i-1][0]-prices[i],dp[i-1][1])

public int maxProfit(int[] prices) {
        int len = prices.length;
        int[][] dp = new int[len][2];
        dp[0][0]=0;
        dp[0][1]=-prices[0];
        for(int i=1;i<len;i++){
            dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
            dp[i][1] = Math.max(dp[i-1][0]-prices[i],dp[i-1][1]);
        }
        return dp[len-1][0];
    }

贪心算法解题

​ 贪心算法,即局部最优,只需要在prices数组中从头开始遍历,找到开始上涨的最小值,然后这一上涨阶段的最大值,最小值买入,最大值卖出,就能获得最大利润

 public int maxProfit(int[] prices) {
        int len = prices.length;
        int ans=0;
        for (int i=1;i<len;i++){
           ans += Math.max(0,prices[i]-prices[i-1]);
        }
        return ans;
    }

2.旋转数组

题目大意

给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

示例:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]

题解

以题目示例来说,旋转之后,左边的4个元素整体和右边的3个元素整体将会交换位置,可以先将整体交换,即直接反转元素,然后在每个部分中再反转即可。

 public void rotate(int[] nums, int k) {  
        int len = nums.length;
        k %= len;
        reverse(nums,0,len-1);
        reverse(nums,0,k-1);
        reverse(nums,k,len-1);
        for (int num : nums) {
            System.out.println(num);
        }
    }

    public void reverse(int[] nums,int start, int end){
        while (start<end){
            int temp = nums[start];
            nums[start++]=nums[end];
            nums[end--]=temp;
        }
    }

3.存在重复元素

题目大意

给定一个整数数组,判断是否存在重复元素。
如果存在一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false 。

示例 1
输入: [1,2,3,1]
输出: true

解题思路—set

直接暴力求解的话效率非常低,可以使用set的add方法一个特性:当添加的对象在set中已经存在,会add失败返回false。

 public boolean containsDuplicate(int[] nums) {
         Set<Integer> set = new HashSet<>();
        for (int i : nums){
            if(!set.add(i)) return true;
        }
        return false;
    }

解题思路—排序后比较

可以使用java内置的排序函数,先进行排序,在用O(n)的时间复杂度进行比较,最后的时间复杂度为排序的时间复杂度nlogn,使用leetcode发现这一种方法所使用的时间和空间要优于使用set

4.只出现一次的数字

题目大意

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

解题思路—异或

利用位运算符异或,相同的数字异或为0,然后任何数与0异或为本身,所以直接将所有数字进行异或即可

public int singleNumber(int[] nums) {
        int ans=0;
        for (int i=0;i<nums.length;i++){
            ans=ans^nums[i];
        }
        return ans;
    }

解题思路—set

使用set的add特性,当添加失败时说明其为重复元素,直接将它移除,最后set只剩下一个不重复的元素,这个的效率不如上一种方法

public int singleNumber(int[] nums) {
         Set<Integer> set = new HashSet<>();
        for(int i=0;i<nums.length;i++){
            if(!set.add(nums[i])){
                set.remove(nums[i]);
            }
        }
        return (int)set.toArray()[0];
    }

5.字符串中的第一个唯一字符

题目大意

给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。

示例:
s = "leetcode"
返回 0

s = "loveleetcode"
返回 2

解题思路

关于字符串的问题要善于运用一个一维数组a[26],来记录每一个字符的出现次数。遍历给定字符串的每一个字符,出现一次便在数组中加一,最后按顺序遍历找出值为一的元素索引返回,不存在即返回-1。

public static int firstUniqChar(String s) {
       int a[] = new int[26];
       for(int i=0;i<s.length();i++){
           a[s.charAt(i)-'a']++;
       }
       for(int i=0;i<s.length();i++){
           if(a[s.charAt(i)-'a']==1) return i;
       }
       return -1;
    }

6.最长公共前缀

题目大意

编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 ""。

示例 1:
输入:strs = ["flower","flow","flight"]
输出:"fl"

示例 2:
输入:strs = ["dog","racecar","car"]
输出:""

解题思路

一种简单的思路就是暴力从0开始枚举所有字符串判断,但效率较低,一种优化的解法则是利用IndexOf函数来判断,不断截取公共字符串

 public String longestCommonPrefix(String[] strs) {
        if (strs == null || strs.length == 0)
            return "";
        String pre = strs[0];
     	if(strs.leng == 1) return pre;
        int i = 1;
        while (i < strs.length) {
            while (strs[i].indexOf(pre) != 0)
                pre = pre.substring(0, pre.length() - 1);
            i++;
        }
        return pre;
    }

7.验证二叉搜索树

题目大意

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。

解题思路

我最初写的错误的解法如下,因为只考虑到局部是否符合,而没考虑到全局

public boolean isValidBST(TreeNode root) {
        if(root == null) return true;
        if(root.left!=null&&root.left.val>root.val 
           || root.right!=null&&root.right.val<root.val) return false;
        return isValidBST(root.left) && isValidBST(root.right);
    }

正确的解题如下:

public boolean isValidBST(TreeNode root) {
    return isValidBST(root,Long.MIN_VALUE, Long.MAX_VALUE);
}

public boolean isValidBST(TreeNode root, long min , long max){
    if(root ==null) return true;
    if(root.val<=min || root.val>=max) return false;
    return isValidBST(root.left, min, root.val) && isValidBST(root.right, root.val, max);
}

8.二叉树的层序遍历

题目大意

给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
 
示例:
二叉树:[3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
        
返回其层序遍历结果:
[
  [3],
  [9,20],
  [15,7]
]

解题思路

关于二叉树的层次遍历应该是比较基础的,即利用队列实现即可,但是这道题要求划分每一个层次来输出二叉树的节点值,就要用到一点小技巧了,如下所示

public List<List<Integer>> levelOrder(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        if(root==null) return new ArrayList<>();
        queue.add(root);
        List<List<Integer>> list = new ArrayList<>();
        while (!queue.isEmpty()){
            //利用size来划分层次
            int size = queue.size();
            List<Integer> sublist = new ArrayList<>();
            for(int i=0;i<size;i++) {
                TreeNode node = queue.poll();
                if(node.left!=null) queue.add(node.left);
                if(node.right!=null) queue.add(node.right);
                sublist.add(node.val);
            }
            list.add(sublist);
        }
        return list;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值