每日一练(day01)

Posted 'or 1 or 不正经の泡泡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每日一练(day01)相关的知识,希望对你有一定的参考价值。

前言

今天是第一天,那咱门就来简单一点寄到题目来玩玩吧,不为别的就为了心情愉悦。
这里虽然是简单题目,但是没想到没注意几个变量愣是调了小半天。还是要细心一点呀。

两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个
整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9 输出:[0,1] 解释:因为 nums[0] + nums[1] ==
9 ,返回 [0, 1] 。 示例 2:

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

乍一看这个题目,其实还是挺简单的,最好想到的方法其实就是拿两个指针一前一后去扫描,然后把两个数字加起来查看是不是我们想要的结果。

class Solution  
public int[] twoSum(int[] nums, int target)
	  
	 	int n = nums.length;
	  	for (int i = 0; i < n; ++i)
	   		 
	   			for (int j = i + 1; j < n; ++j)
	    			 
	    				if (nums[i] + nums[j] == target)
	     					 
	     						return new int[]i, j; 
	     					 
	     			 
	    	 
	      return new int[0]; 
	      
	  

但是的话我们其实还是有更加nice方法的,那就是我们完全是可以使用哈希函数来降低时间复杂度,其实就是使用字典在java里面叫做map

class Solution 
    public int[] twoSum(int[] nums, int target) 
        Map<Integer, Integer> map = new HashMap<>();

        for (int i = 0; i < nums.length; ++i) 
            if (map.containsKey(target - nums[i])) 
                return new int[]map.get(target - nums[i]), i;
            
            map.put(nums[i], i);
        
        return new int[0];
    


我们可以直接把我们的数值元素和对应的下标存到 map 里面。然后你不是要求(假设求9 那么我直接遍历 9-元素等不等于 map里面的另一个元素)并且我们可以直接把我们的寻找和元素添加放在一起,因为知道找到了我们就没有必要再往下添加元素,然后扫描了。
为了便于理解你可以直接查看这个 python 代码

class Solution: 
	def twoSum(self, nums, target): 
		hashtable = 
		for i, num in enumerate(nums): 
			if target - num in hashtable: 
				return [hashtable[target - num], i] 
			hashtable[nums[i]] = i 
		return []

哦对了有必要说一下的是那个现在好像不少人使用的python语法是3.9x的,虽然没啥问题但是看起来怪怪的。

回文数

给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。

回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。例如,121 是回文,而 123 不是。

示例 1:

输入:x = 121 输出:true 示例 2:

输入:x = -121 输出:false 解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

这个其实很简单,最简单的就是说,你变成字符串嘛,然后去扫描从前面的到后面的两个指针(数组下标指向)去扫描。
但是我们其实可以优化一下,原理还是一样的,都是两头去扫描。但是区别是,前者我们要转化为字符串然后去扫描,这里会带来一些内存消耗,另一个则是使用取余数法,然后拼接数字,也是前后扫描。

class Solution 
    public boolean isPalindrome(int x) 
        if(x<0||x%10==0 && x!=0)
            //负数和一位数都不是回文
            return false;
        
        int rev = 0;
        //我们对半提取,个位拿出来然后依次
        while (x>rev)
            rev = rev * 10 + x%10;
            x /=10;
        
        return x==rev||x==rev/10;

    

举个例子 1221
我们对半 把最后面的 1 和 2拿出来 逆向组合不就是 12 嘛,如果这个12 和 前面的12 相等不是是回文嘛。

这里要注意的细节就是 12321 有些是奇数的,在我们代码里面是从后面组合的也就是 rev = 123 退出循环 x= 12 此时,所以 x == rev/10 才成立。

罗马数字转整数

罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。

字符 数值 I 1 V 5 X 10 L
50 C 100 D 500 M 1000 例如, 罗马数字 2
写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5
的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。 X 可以放在 L (50) 和 C (100) 的左边,来表示
40 和 90。 C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个罗马数字,将其转换成整数。

示例 1:

输入: s = “III” 输出: 3

关于这题就说明两点

第一点是:

注意一下就是罗马字符的那个规则,如果 字符A 表示的数字比字符 B 表示的数字小,那么我们 就要在整体加得的结果减去A表示的值,如果A表示的比B大就直接加入A表示的值。 A和B的关系为 AB

第二点是:

首先我们发现动态的去把罗马字符的元素加入map里面的速度反而比直接加入的速度要快,性能消耗还更少。

class Solution 
    public int romanToInt(String s) 
        Map<Character,Integer> map = new HashMap<>();
        String chars = "IVXLCDM";
        int j = 1;
        for(int i=1;i<=chars.length();i++)
            if(i!=1)
                if (i%2==0)
                    j*=5;
                else
                    j*=2;
            map.put(chars.charAt(i-1),j);
        
        j = 0;
        for(int i=0;i<s.length();i++)
            int value = map.get(s.charAt(i));
            if(i<s.length()-1 && value<map.get(s.charAt(i+1)))
                j-=value;
            
            else
                j+=value;

        
        return j;
    

有效括号

给定一个只包括 ‘(’,’)’,’’,’’,’[’,’]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 示例 1:

输入:s = “()” 输出:true 示例 2:

输入:s = “()[]” 输出:true

这个没啥好说的。栈呗。

class Solution 
    public boolean isValid(String s) 
        if(s == null || s.length() == 1)
            return false;
        
        Stack<Character> chars = new Stack<Character>();

        for(int i=0;i<s.length();i++)
            char c = s.charAt(i);
            if(c=='(' || c=='[' || c=='')
                chars.push(c);
            
            else 
                if(chars.isEmpty())
                    return false;
                if(c==')'&& chars.peek()=='(')
                    chars.pop();
                else if (c==']' && chars.peek()=='[')
                    chars.pop();
                
                else if (c=='' && chars.peek()=='')
                    chars.pop();
                
                else
                    return false;
            
        
        if(chars.isEmpty())
            return true;
        return false;
    


合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:

输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4] 示例 2:

输入:l1 = [], l2 = [] 输出:[] 示例 3:

输入:l1 = [], l2 = [0] 输出:[0]

这里要注意的是那个

/**
 * Definition for singly-linked list.
 * public class ListNode 
 *     int val;
 *     ListNode next;
 *     ListNode() 
 *     ListNode(int val)  this.val = val; 
 *     ListNode(int val, ListNode next)  this.val = val; this.next = next; 
 * 
 */

这个是他们给的那个链表的定义,一定要先给一个元素值。

class Solution6 
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) 

        ListNode res = new ListNode(0);
        ListNode cur = res;
        while (list1!=null && list2!=null)
            if(list1.val > list2.val)
                cur.next = list2;
                list2 = list2.next;
                cur = cur.next;
            
            else 
                cur.next = list1;
                list1 = list1.next;
                cur = cur.next;
            
        
        if(list1==null)
            cur.next = list2;
        
        else 
            cur.next = list1;
        

        return res.next;

    

这边我们是采用了牺牲空间的策略,这里还是可以优化的,我们可以选择直接把两个直接合并到list1或者list2那边,直接对list1这边操作,但是不太好理解,可以直接使用递归,但是我觉得这个消耗也不少。

最长公共前缀

这个题目是挺简单的,但是我写的时候没注意,老是过不了,然后查了一下,但是发现那个给出的标准代码是有逻辑问题的,虽然能够通过测试集。

题目:

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 “”。

示例 1:

输入:strs = [“flower”,“flow”,“flight”] 输出:“fl” 示例 2:

输入:strs = [“dog”,“racecar”,“car”] 输出:"" 解释:输入不存在公共前缀。

我们就先来看看那个标准代码

class Solution 
 	public String longestCommonPrefix(String[] strs)
 	 
 	 	 if (strs == null || strs.length == 0)
  			  
  			 	return ""; 
  			 
    String prefix = strs[0]; 
    int count = strs.length;
    for (int i = 1; i < count; i++) 
    	 
    		prefix = longestCommonPrefix(prefix, strs[i]);
      		if (prefix.length() == 0)
	      		 
	      		 	 break; 
	      		  
      	 
      	return prefix; 
       
      public String longestCommonPrefix(String str1, String str2)  
      
      	int length = Math.min(str1.length(), str2.length());
      	int index = 0; 
      	while (index < length && str1.charAt(index)== str2.charAt(index)) 
       	 
       		index++; 
       	 
        return str1.substring(0, index);
       
   

发现没有,他是什么,他是拿出第一个单词,然后用这个第一个单词和剩下的单词去对比,也就是执行
longestCommonPrefix( ) 但是关于这个函数,你自己看看它做了什么,就是对比两个单词前面的共同点,然后返回两个单词之间的公共前缀,然后返回给prefix,问题来了假设有三个单词,第一个和第二个单词的前缀为 ab 第一个和第三个为 abc。执行第一次 strs[1] prefix为 ab 第二次 strs[2] prefix 为 abc 然后结束循环,此时prefix 为 abc 问题来了,显然三个单词里面的前缀应该是ab,而不是abc。但是我不知道为什么这代码能够过测试集!!!

ok 那么来看看我这边的代码


class Solution 
    public String longestCommonPrefix(String[] strs) 

        String firstworld = strs[0];
        String answer = "";
        int index = 0;
        boolean flag = true;
        out:for(int i = 0;i<firstworld.length();i++)
            char c = firstworld.charAt(i);
            for(int j=1;j<strs.length;j++)
                if(i>=strs[j].length())
                    break out;
                
                else 
                    if(c !=strs[j].charAt(i) )
                        flag = false;
                
            
            if(flag)
                index ++;
        
        answer = firstworld.substring(0,index);
        return answer;
    



思路也是一样的,拿出第一个单词,但是我是直接拿第一个单词的第一个字母去和剩下的单词对应位置的字母去比对,一旦返现比对的字母数字,超过了当前单词的长度,那么说明这个单词整个都是最长前缀,如果没有然后发现当前字母对不到了,那么前面的都是相同前缀。
当然你还可以换个方式来扫描,以单词为主。

以上是关于每日一练(day01)的主要内容,如果未能解决你的问题,请参考以下文章

每日一练(day02 手撕 KMP)

每日一练(day09补08,03,04)

每日一练(day05)

每日一练(day04)

每日一练(day03--动态规划dp)

每日一练(day10)