欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-04-12 09:46:39
...

题目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]<B[bStart]) 。

*/
    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;
    }
}