Leetcode精选TOP面试题-题目1-5

题目1-5

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

你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

class Solution {
    public int[] twoSum(int[] nums, int target) {
        
        for (int i = 0; i < nums.length; i++) {  //遍历
            for (int j = i + 1; j < nums.length; j++) {  //从当前的下一个继续双重遍历
                if (nums[j] == target - nums[i]) {  //如果刚好满足条件,那么输出
                    return new int[] { i, j };
                }
            }
        }
        throw new IllegalArgumentException("No two sum solution");

    }
}

2.两数相加

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        if(l1==null) return l2;
        if(l2 == null) return l1;
        ListNode dummy = new ListNode(0); //dunmmy指向头结点
        ListNode current = dummy;  //current是当前指针
        int carry = 0;   //carry是进位

        while(l1 != null && l2 != null){
            int dig = l1.val+l2.val+carry;  //数字一+数字二+进位
            int val =dig %10;          //取余得到本位结果
            carry = dig /10;           //除10得到下位进位
            ListNode newnode = new ListNode(val);   //new 新节点
            current.next = newnode;   
            current = current.next;  //current后移
            l1 = l1.next;          //l1后移
            l2 = l2.next;          //l2后移   
        }

         while(l1 != null){
            int dig = l1.val+carry;  //12已经空了
            int val =dig %10;
            carry = dig /10;
            ListNode newnode = new ListNode(val);
            current.next = newnode;
            current = current.next;
            l1 = l1.next;
        }

        while(l2 != null){
            int dig = l2.val+carry;   //l1已经空了
            int val =dig %10;
            carry = dig /10;
            ListNode newnode = new ListNode(val);
            current.next = newnode;
            current = current.next;
            l2 = l2.next;
        }

        if(carry != 0) current.next = new ListNode(carry);  //若进位不是0,就new 一个新的节点当做进位后的首位
        return dummy.next; //dummy肯定是空的了,dummy的下一个才是有值的
    }
}

3.无重复字符的最长子串

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:

输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:

输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

class Solution {
   public int lengthOfLongestSubstring(String s) {
    int maxLength = 0;   //定义最大长度
    char[] chars = s.toCharArray();  //字符串转为char数组
    int leftIndex = 0;     //定义左索引
    for (int j = 0; j < chars.length; j++) {  //遍历
      for (int innerIndex = leftIndex; innerIndex < j; innerIndex++) {  
      //内索引(从左索引起)遍历至j
        if (chars[innerIndex] == chars[j]) {  //若出现相同字符
          maxLength = Math.max(maxLength, j - leftIndex); 
          //重新定义最大长度:max{当前最大长度,j-左索引}
          leftIndex = innerIndex + 1;  //左索引更新为 当前(出现相同字符时)内索引+1
          break;
        }
      }
    }
    return Math.max(chars.length - leftIndex, maxLength);  
  }
}

4.寻找两个有序数组的中位数

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。

请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

你可以假设 nums1 和 nums2 不会同时为空。

示例 1:

nums1 = [1, 3]
nums2 = [2]

则中位数是 2.0
示例 2:

nums1 = [1, 2]
nums2 = [3, 4]

则中位数是 (2 + 3)/2 = 2.5

class Solution {
/*
用 len 表示合并后数组的长度,如果是奇数,我们需要知道第 (len+1)/2 个数就可以了,
如果遍历的话需要遍历 int(len/2 ) + 1 次。
如果是偶数,我们需要知道第 len/2和 len/2+1 个数,也是需要遍历 len/2+1 次。
所以遍历的话,奇数和偶数都是 len/2+1 次。

返回中位数的话,奇数需要最后一次遍历的结果就可以了,偶数需要最后一次和上一次遍历的结果。
所以我们用两个变量 left 和 right,right 保存当前循环的结果,
在每次循环前将 right 的值赋给 left。这样在最后一次循环的时候,left 将得到 right 的值,
也就是上一次循环的结果,接下来 right 更新为最后一次的结果。

循环中该怎么写,什么时候 A 数组后移,什么时候 B 数组后移。
用 aStart 和 bStart 分别表示当前指向 A 数组和 B 数组的位置。
如果 aStart 还没有到最后并且此时 A 位置的数字小于 B 位置的数组,那么就可以后移了。
也就是aStart<m&&A[aStart]< B[bStart]。

但如果 B 数组此刻已经没有数字了,继续取数字 B[ bStart ],则会越界,所以判断下 bStart 是否大于数组长度了,这样 || 后边的就不会执行了,也就不会导致错误了,所以增加为 aStart<m&&(bStart) >= n||A[aStart]
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    //不妨认为nums1为A   nums2为B
        int m = nums1.length;   //A数组的长度
        int n = nums2.length;   //B数组的长度
        int len = m + n;    //合并后数组的长度
        int left = -1, right = -1;    //right 保存当前循环的结果
        int aStart = 0, bStart = 0;   
        //aStart 和 bStart 分别表示当前指向 A 数组和 B 数组的位置
        
        for (int i = 0; i <= len / 2; i++) {  
        //遍历的话,奇数和偶数都是 len/2+1 【0---len/2】
            left = right;  //每次循环前将 right 的值赋给 left
            //这样在最后一次循环的时候,left 将得到 right 的值
            //也就是上一次循环的结果,接下来 right 更新为最后一次的结果
            if (aStart < m && (bStart >= n || nums1[aStart] < nums2[bStart])) {
            //如果 aStart 还没有到最后, B[ bStart ]不会越界
            //并且此时 A 位置的数字小于 B 位置的数字
            //那么就可以后移了
                right = nums1[aStart++];
            } else {
                right = nums2[bStart++];
            }
        }
        if ((len % 2) == 0)
            return (left + right) / 2.0;
        else
           return right;
    }
}

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:

输入: “cbbd”
输出: “bb”

class Solution {
    public String longestPalindrome(String s) {
        int len = s.length();
        if (len <= 1) {
            return s;
        }
        int longestPalindrome = 1;
        String longestPalindromeStr = s.substring(0, 1);
        boolean[][] dp = new boolean[len][len];
        // abcdedcba
        //   l   r
        // 如果 dp[l, r] = true 那么 dp[l + 1, r - 1] 也一定为 true
        // 关键在这里:[l + 1, r - 1] 一定至少有 2 个元素才有判断的必要
        // 因为如果 [l + 1, r - 1] 只有一个元素,不用判断,一定是回文串
        // 如果 [l + 1, r - 1] 表示的区间为空,不用判断,也一定是回文串
        // [l + 1, r - 1] 一定至少有 2 个元素 等价于 l + 1 < r - 1,即 r - l >  2

        // 写代码的时候这样写:如果 [l + 1, r - 1]  的元素小于等于 1 个,即 r - l <=  2 ,就不用做判断了

        // 因为只有 1 个字符的情况在最开始做了判断
        // 左边界一定要比右边界小,因此右边界从 1 开始
        for (int r = 1; r < len; r++) {
            for (int l = 0; l < r; l++) {
                // 区间应该慢慢放大
                // 状态转移方程:如果头尾字符相等并且中间也是回文
                // 在头尾字符相等的前提下,如果收缩以后不构成区间(最多只有 1 个元素),直接返回 True 即可
                // 否则要继续看收缩以后的区间的回文性
                // 重点理解 or 的短路性质在这里的作用
                if (s.charAt(l) == s.charAt(r) && (r - l <= 2 || dp[l + 1][r - 1])) {
                    dp[l][r] = true;
                    if (r - l + 1 > longestPalindrome) {
                        longestPalindrome = r - l + 1;
                        longestPalindromeStr = s.substring(l, r + 1);
                    }
                }
            }
        }
        return longestPalindromeStr;
    }
}

你可能感兴趣的:(精选TOP面试题)