LeetCode 面试热题二

14.最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 “”。
解题思路:纵向扫描法,使用第一个字符串的每一个字符与其它所有的字符串依次比较。

public String longestCommonPrefix(String[] strs) {
	if(strs == null || strs.length == 0){
        return "";
    }
    int length = 0;
    int i,j;
    for(i = 0; i< strs[0].length(); i++){
    	// 用strs[0]的每一个字符,与其它字符串的每一位比较
        for(j = 1;j<strs.length;j++){
        	// 不相等,或其它字符串的长度不够,返回最大公共前缀
            if(i >= strs[j].length() || strs[j].charAt(i) != strs[0].charAt(i)){
                return strs[0].substring(0,length);
            }
        }
        length++;
    }
    return strs[0];
}

15.三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
解题思路:暴力解法,找出 a b c,然后排序,去重。
然后超时了,时间复杂度为 O(n3)

public List<List<Integer>> threeSum(int[] nums) {
    Set result = new HashSet();
    for(int i = 0;i < nums.length;i++){
        for(int j = 0;j < nums.length;j++){
            if(j != i) {
                for (int k = 0; k < nums.length; k++) {
                    if(k != j && k != i) {
                        if (nums[i] + nums[j] + nums[k] == 0) {
                            List list = new ArrayList();
                            list.add(nums[i]);
                            list.add(nums[j]);
                            list.add(nums[k]);
                            Collections.sort(list);
                            result.add(list);
                        }
                    }
                }
            }
        }
    }
    return new ArrayList<>(result);
}

方法二:排序 + 双指针
算法思想:固定一个值,求两数之和为固定值的负值,这样两数之和与固定值的和就是0。关键在于去重。先排序,将值从小到大排列,在遍历的时候,跳过重复的值。

public List<List<Integer>> threeSum(int[] nums) {     
	// 排序,从小到大
    Arrays.sort(nums);
    int target;
    int L,R,value;
    List<List<Integer>> results = new ArrayList<>();
    // 每次固定一个 nums[i] 值
    for(int i=0; i<nums.length; i++){
    	// 去除重复的值。
        if(i > 0 && nums[i] == nums[i - 1]){
            continue;
        }
        // 两数之和的目标值为固定值的负值。
        target = -nums[i];
        L = i + 1;
        R = nums.length - 1;
        // 从 i值的后面的所有元素查找
        while(R > L){
            value = nums[R] + nums[L];
            // 两数之和等于目标值
            if(value ==  target){
                ArrayList result = new ArrayList();
                result.add(nums[i]);
                result.add(nums[R]);
                result.add(nums[L]);
                // 放入结果集合
                results.add(result);
                // 继续寻找,排除重复的值
                while(R > L && nums[R - 1] == nums[R]){
                    R--;
                }
                R--;
                // 继续寻找,排除重复的值
                while(R > L && nums[L + 1] == nums[L]){
                    L++;
                }
                L++;
            }else if(value < target){
            	// 两数之和小于,则L指针右移。
                L++;
            }else{ 
            	// 两数之和大于,则R指针左移。
                R--;
            }
        }
    }
    return results;
}

17.电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。
2: abc
3: def
4:ghi
5:jkl
6:mno
7:pqrs
8:tuv
9:wxyz

解题思路:使用深度优先,遍历得到所有的组合。

public List<String> letterCombinations(String digits) {   
	// 保存结果  
    List<String> result = new ArrayList<>();
    if(digits.length() == 0){
    	// 为零直接返回空
        return result;
    }
    // 建立映射表
    HashMap<Character,String> map = new HashMap<Character,String>();
    map.put('2', "abc");
    map.put('3', "def");
    map.put('4', "ghi");
    map.put('5', "jkl");
    map.put('6', "mno");
    map.put('7', "pqrs");
    map.put('8', "tuv");
    map.put('9', "wxyz");
    // 深度优先遍历
    DFS(digits,0,map,result,new StringBuffer());
    return result;
}
// 参数说明
// digits存储数字的字符串,index为数字串的下标,map为 映射表,result为结果,t保持中间的结果
public void DFS(String digits, int index,Map map,List<String> result,StringBuffer t) {
    if(index == digits.length()){
    	// 深度优先到最深层,将中间结果t存入最终结果result中。
        result.add(t.toString());
        return;
    }
    // 得到数字对应的字母
    String s = (String) map.get(digits.charAt(index));
    for(int i = 0;i<s.length();i++){
    	// 首字母写入中间结果
        t.append(s.charAt(i));
        // 深度优先搜索
        DFS(digits,index+1,map,result,t);
        // 删除最后写入的值
        t.deleteCharAt(index);
    }
}

19.删除链表的倒数第 N 个结点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
方法一: 递归到链表尾部,然后逐层返回并计数,直到找到倒数第n个节点,返回。

static int t;
public static ListNode removeNthFromEnd(ListNode head, int n){
    if(head.next == null){
        t = 1;
        if(n == t){
            head = null;
        }
        return head;
    }
    head.next = removeNthFromEnd(head.next,n);
    t++;
    if(t == n){
        return head.next;
    }
    return head;
}

方法二:快慢指针,快指针比满指针快 n 个节点。这样当快指针到达链表尾部。慢指针恰好指向第n个节点。

public static ListNode removeNthFromEnd(ListNode head, int n){
    ListNode dummy = new ListNode(0, head);
    ListNode first = head;
    ListNode second = dummy;
    for(int i = 0; i < n;i++){
        first = first.next;
    }
    while(first != null){
        first = first.next;
        second = second.next;
    }
    second.next = second.next.next;
    return dummy.next;
}

20.有效的括号

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

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

解题思路:使用栈来解题,左括号入栈,右括号出栈。

public boolean isValid(String s) {
    if(s.length()%2 == 1){
        return false;
    }
    Stack<Character> stack = new Stack<>();
    HashMap<Character,Character> map = new HashMap<>();
    map.put('(',')');
    map.put('[',']');
    map.put('{','}');
    Character c;
    for(int i = 0;i < s.length();i++){
        c = s.charAt(i);
        if(map.containsKey(c)){
            stack.push(c);
        }else{
            if(stack.empty() ||  c != map.get(stack.pop())){ 
                return false;
            }
        }
    }
    return stack.isEmpty();
}

21.合并两个有序链表

将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
请添加图片描述
解题思路:没啥说的,干。

public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
    ListNode result = new ListNode();
    ListNode p = result;
    while(list1 != null && list2 != null) {
        if (list1.val <= list2.val) {
            p.next = list1;
            list1 = list1.next;
        } else {
            p.next = list2;
            list2 = list2.next;
        }
        p = p.next;
    }
    p.next = list1 == null?list2:list1;
    return result.next;
}

22.括号生成

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例:

输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
输入:n = 1
输出:["()"]

解题思路:回溯剪枝,剪枝条件:左括号的个数大于0,右括号的个数要小于左括号的个数才能放入。
如图:n为2的情况下的所有可能,进行剪枝操作。

在这里插入图片描述

public List<String> generateParenthesis(int n) {
    // 回溯剪枝
    List<String> results = new ArrayList<>();
    DFS(n,n,results,new StringBuffer());
    return results;
}
public void DFS(int left,int right,List<String> results,StringBuffer t){
    if(left == 0 && right == 0){
        results.add(t.toString());
        return;
    }
    if(left > 0){
        t.append("(");
        DFS(left-1,right,results,t);
        t.deleteCharAt(t.length()-1);
    }
    if( right >  left){
        t.append(")");
        DFS(left,right-1,results,t);
        t.deleteCharAt(t.length()-1);
    }
}

23.合并K个升序链表

给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

解题思路:分治算法,将大的问题转换成若干个子问题。

public ListNode mergeKLists(ListNode[] lists) {
    if(lists.length == 0){
        return null;
    }
    if(lists.length == 1){
        return lists[0];
    }
    ListNode list;
    list = divideAndConquer(lists,0,lists.length - 1);
    return list;
}
public ListNode divideAndConquer(ListNode[] lists,int start, int end){
    ListNode merge1 = null;
    ListNode merge2 = null;
    if(end - start > 1){
        int mid = start + (end-start)/2;
        merge1 = divideAndConquer(lists,start, mid);
        merge2 = divideAndConquer(lists,mid + 1,end);
    }else{
        if(end == start){
            merge1 = lists[start];
        }else{
            merge1 = mergeTwoLists(lists[start],lists[end]);
        }
    }
    return mergeTwoLists(merge1,merge2);
}
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
    ListNode result = new ListNode();
    ListNode p = result;
    while(list1 != null && list2 != null) {
        if (list1.val <= list2.val) {
            p.next = list1;
            list1 = list1.next;
        } else {
            p.next = list2;
            list2 = list2.next;
        }
        p = p.next;
    }
    p.next = list1 == null?list2:list1;
    return result.next;
}

26.删除有序数组中的重复项

给你一个有序数组 nums ,请你原地删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组 并在使用 O(1) 额外空间的条件下完成。
解题思路:使用快慢指针,快指针将第一次遇见的数,赋值给慢指针指向的位置。等待快指针遍历完整个数组,就知道数组中有多少不同的值。

public int removeDuplicates(int[] nums) {
    if(nums.length == 0){
        return 0;
    }
    if(nums.length == 1){
        return 1;
    }
    int first = 1,second = 1;
    while(first < nums.length){
        if(nums[first] != nums[first - 1]) {
            nums[second] = nums[first];
            ++second;
        }
        first++;
    }
    return second;
}

28.实现 strStr()

实现 strStr() 函数。
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
解题思路:KMP算法
next数组中的值就是子串的最大相同前缀和后缀的个数
参考视频

public int strStr(String haystack, String needle) {
    int needleLength = needle.length();
    // 当needle是空字符串时,返回0
    if (needleLength == 0) return 0;
    // 定义好next数组
    int[] next = new int[needleLength];
    // 定义好两个指针right与left
    // 在for循环中初始化指针right为1,left=0,开始计算next数组,right始终在left指针的后面
    for (int left = 0,right = 1; right < needleLength; right++) {
        // 如果不相等就让left指针回退,到0时就停止回退
        while (left > 0 && needle.charAt(left) != needle.charAt(right)) {
            //进行回退操作;
            left = next[left - 1];
        }
        if (needle.charAt(left) == needle.charAt(right)) {
            next[right] = ++left;
        }else{
            next[right] = 0;
        }
    }
    // 循环结束的时候,next数组就已经计算完毕了
    // 根据next数组进行串的匹配
    for (int i = 0,j=0; i <haystack.length() ; i++) {
        while (j > 0 && haystack.charAt(i) != needle.charAt(j)){
            j=next[j-1];
        }
        if (haystack.charAt(i)==needle.charAt(j)){
            j++;
        }
        if (j == needleLength) {
            return i - needleLength + 1;
        }
    }
    return -1;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值