LeetCode分析与题解 21-30

Leetcode21 合并两个有序链表

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode l3 = new ListNode(-1),now = l3;
        
        while(l1 != null && l2 != null){
            if(l1.val > l2.val){
                now.next = new ListNode(l2.val);
                now = now.next;
                l2 = l2.next;
            }else{
                now.next = new ListNode(l1.val);
                now = now.next;
                l1 = l1.next;
            }
        }
        
        if(l1 == null){
            now.next = l2;
        }else{
            now.next = l1;
        }
        
        return l3.next;
    }
}

Leetcode22 括号生成

给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。
例如,给出 n = 3,生成结果为:
[
“((()))”,
“(()())”,
“(())()”,
“()(())”,
“()()()”
]

解析:我们用递归来解决这道题,首先第一个括号必为左括号,然后从第二个括号开始都只有左括号或者右括号两种可能。若当前得到字符串的左括号个数大于等于右括号数,可以增加一个左括号,若当前得到字符串的左括号个数仅仅大于右括号数,可以增加一个右括号,然后不断递归,注意边界条件即可。

class Solution {

    public List<String> generateParenthesis(int n) {
        List<String> list =  new ArrayList<String>();
        
        use(list,1,0,n,"(");
        return list;
    }
    
    //list为需要的链表,left为左括号的数量,right为右括号的数量,n为生成括号的对数,s为当前的字符串
    private void use(List<String> list,int left,int right,int n,String s){
        if(left > n || right > n){
            return ;
        }else if(left == n && right == n){
            list.add(s);
        }else if(left >= right){
            use(list,left+1,right,n,s+"(");
            
            if(left > right){
                use(list,left,right+1,n,s+")");
            }
        }
    }
}

Leetcode23 合并K个排序链表

合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。 示例: 输入: [ 1->4->5, 1->3->4,
2->6 ] 输出: 1->1->2->3->4->4->5->6

解析1:我们构建一个最小堆,里面存放的是链表元素。先把每个链表的首元素存放进最小堆中,然后依次取出最小堆中的最小值所在的链表元素,若不为空则加入排序链表,为空为清除出最小堆,然后一直向下遍历即可。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if(lists == null || lists.length == 0){
            return null;
        }
        ListNode myNode = new ListNode(-1),node = myNode,l;
        PriorityQueue<ListNode> queue = new PriorityQueue<ListNode>(new Comparator<ListNode>(){//最小堆
            @Override
            public int compare(ListNode i1,ListNode i2){
                return i1.val-i2.val;
            }
        });
        
        for(int i = 0;i < lists.length;i++){//将链表开头的元素插入最小堆
            if(lists[i] != null){
                queue.offer(lists[i]);
            }
        }
        
        while(queue.size() > 0){
            while(queue.peek() == null){
                queue.poll();
            }
            
            if((l = queue.peek()) != null){
                node.next = new ListNode(l.val);
                node = node.next;
                queue.poll();
                if(l.next != null){
                    queue.offer(l.next);
                }
            }
        }
        
        return myNode.next;
    }
}

解析2:使用分治法,分而治之。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if(lists == null || lists.length == 0){
            return null;
        }
        return merge(lists,0,lists.length-1);
    }
    
    private ListNode merge(ListNode[] list,int left,int right){
        if(left == right){
            return list[left];
        }
        
        int mid = (left + right) / 2;
        ListNode l1 = merge(list,left,mid);
        ListNode l2 = merge(list,mid+1,right);
        
        return mergeTwoLists(l1,l2);
    }
    
    private ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode l3 = new ListNode(-1),now = l3;
        while(l1 != null && l2 != null){
            if(l1.val > l2.val){
                now.next = new ListNode(l2.val);
                now = now.next;
                l2 = l2.next;
            }else{
                now.next = new ListNode(l1.val);
                now = now.next;
                l1 = l1.next;
            }
        }
        if(l1 == null){
            now.next = l2;
        }else{
            now.next = l1;
        }
        return l3.next;
    }
}

Leetcode24 两两交换链表中的节点

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例:
给定 1->2->3->4, 你应该返回 2->1->4->3.

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode swapPairs(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        
        ListNode per = null,left = head,right = head.next;
        while(right != null){
            left.next = right.next;
            right.next = left; 
            if(per != null){
                per.next = right;
                per = per.next.next;
            }
            if(head == left){
                head = right;
                per = left;
            }
            left = left.next;
            right = left == null ? null : left.next;
            
        }
        
        return head;
    }
}

Leetcode25 K 个一组翻转链表

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
示例 :
给定这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5
说明 :
你的算法只能使用常数的额外空间。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

解析:看到翻转顺序,直接想到使用栈。我们可以根据K的大小来决定入栈的链表节点数量,只需要注意最后一组链表节点很可能不是K的整数倍需要顺序插入即可。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        if(head == null || head.next == null){
            return head;
        }
        Stack<ListNode> stack = new Stack<ListNode>();
        ListNode give = new ListNode(-1),p = head,myNode = give,m;
        while(p != null){
            int i = 0;
            m = p;
            while(++i <= k && p != null){
                stack.push(p);
                p = p.next;
            }
            if(i-1 != k){//遍历完成
                give.next = m;
                break;
            }else{
                while(!stack.isEmpty()){
                    give.next = stack.pop();
                    give.next.next = null;
                    give = give.next;
                }
            }
        }
        return myNode.next;
    }
}

Leetcode26 删除排序数组中的重复项

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
你不需要考虑数组中超出新长度后面的元素。

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums == null || nums.length == 0) return 0;
        int num = 0;//当前修改的数组下标
        int nowNum = nums[0];//当前遍历的数字
        
        for(int i = 0;i < nums.length;i++){
            if(nums[i] == nowNum){
                continue;
            }else{
                nums[++num] = nums[i];
                nowNum = nums[i];
            }
        }
        return num+1;
    }
}

Leetcode27 移除元素

给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1:
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,1,2,2,3,0,4,2], val = 2,
函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。
注意这五个元素可为任意顺序。
你不需要考虑数组中超出新长度后面的元素。

class Solution {
    public int removeElement(int[] nums, int val) {
        if(nums == null || nums.length == 0){
            return 0;
        }
        int front = 0,end = nums.length - 1,p;
        while(front <= end){
            while(front <= end && nums[front] != val){
                front++;
            }
            while(front <= end && nums[end] == val){
                end--;
            }
            if(front <= end){
                p = nums[front];
                nums[front] = nums[end];
                nums[end] = p;
            }
        }
        return front;
    }
}

Leetcode28 实现 strStr()

实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例 1:
输入: haystack = “hello”, needle = “ll”
输出: 2
示例 2:
输入: haystack = “aaaaa”, needle = “bba”
输出: -1

解析:使用KMP算法解题即可

class Solution {
    public int strStr(String haystack, String needle) {
        if(needle.equals("")){
            return 0;
        }
        int[] next = getNextArray(needle);
        if(next == null){
            return -1;
        }
        char[] haystackArray = haystack.toCharArray();
        char[] needleArray = needle.toCharArray();
        int i = 0,j = 0;
        while(i < haystackArray.length){
            if(haystackArray[i] == needleArray[j]){
                i++;
                j++;
                if(j == needleArray.length){
                    return i - j;
                }
            }else{
                if(j == 0){
                    i++;
                }else{
                    j = next[j-1]+1;
                }
            }
        }
        return -1;
    }
    
    private int[] getNextArray(String needle){
        if(needle == null){
            return null;
        }
        int[] next = new int[needle.length()];
        char[] array = needle.toCharArray();
        next[0] = -1;
        for(int i = 1;i < needle.length();i++){
            int j = next[i-1];
            while(array[j+1] != array[i] && j >= 0){
                j = next[j];
            }
            if(array[j+1] == array[i]){
                next[i] = j + 1;
            }else{
                next[i] = -1;
            }
        }
        return next;
    }
}

Leetcode29 两数相除

给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。
返回被除数 dividend 除以除数 divisor 得到的商。
示例 1:
输入: dividend = 10, divisor = 3
输出: 3
示例 2:
输入: dividend = 7, divisor = -3
输出: -2
说明:
被除数和除数均为 32 位有符号整数。
除数不为 0。
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。本题中,如果除法结果溢出,则返回 231 − 1。

解析:记得以前上微机原理这门课的时候,老师提到过乘除法是能够转化为加减法的。就像今天这道题一样,名为除法,实为减法,原理便是被除数一直减除数,便可得到商。当然,在这道题中这么做会超时的。我们再想一想,有没有更快的方法呢?我们可以这么想,为什么一次只能减一个除数呢,如果我们能够一次性减去“多个”除数“,速度不就快了很多?本题解题过程就是在上述思路下完成的。

class Solution {
    public int divide(int dividend, int divisor) {
        if(dividend == Integer.MIN_VALUE && divisor == -1){
            return Integer.MAX_VALUE;
        }
        long myDividend = dividend;
        long myDivisor = divisor;
        boolean flag =  (myDividend > 0 && myDivisor > 0) || (myDividend < 0 && myDivisor < 0);
        myDividend = myDividend > 0 ? myDividend : -myDividend;
        myDivisor = myDivisor > 0 ? myDivisor : -myDivisor;
        int i = 31,end = 0;
        while(i >= 0){
            if(myDividend >> i >= myDivisor){
                end += 1 << i;
                myDividend -= myDivisor << i;
            }
            i--;
        }
        return flag ? end : -end;
    }
}

Leetcode30 串联所有单词的子串

给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。
示例 1:
输入:
s = “barfoothefoobarman”,
words = [“foo”,“bar”]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 “barfoo” 和 “foobar” 。
输出的顺序不重要, [9,0] 也是有效答案。
示例 2:
输入:
s = “wordgoodgoodgoodbestword”,
words = [“word”,“good”,“best”,“word”]
输出:[]

解析:我们使用滑动窗口解法,首先我们创建两个哈希表,分别对应初始字符串与给定的字符串数组。然后我们构建滑动窗口,从左往右滑,如果目前窗口对应的字符串在数组对应的哈希表中且个数未超,则个数递增,如果在哈希表中但个数超了,则需要向前找到在哈希表中与该字符串相同的首个位置并将窗口改为该位置之后,再次滑动。如果目前窗口对应的字符串不在数组对应的哈希表,那么直接清空字符串对应的哈希表,并将滑动窗口的位置移到该字符串之后的位置。将窗口一直移动至最后即可。

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> list = new ArrayList<Integer>();
        if(words == null || words.length == 0 || s == null || s.length() == 0){
            return list;
        }
        Map<String,Integer> wordsMap = new HashMap<String,Integer>();
        Map<String,Integer> sMap = new HashMap<String,Integer>();
        int step = words[0].length();
        
        for(String m:words){//初始化数组对应的哈希表
            if(wordsMap.containsKey(m)){
                wordsMap.put(m,wordsMap.get(m)+1);
            }else{
                wordsMap.put(m,1);
            }
        }
        
        for(int i = 0;i < step;i++){
            int left = i,end = i;
            sMap.clear();//清空字符串对应的哈希表,重新开始
            while(end <= s.length() - step){
                String get = s.substring(end,end + step);
                end += step;
                if(wordsMap.containsKey(get)){//目前截取的字符串在字符数组对应的哈希表能够找到
                    if(sMap.containsKey(get)){
                        sMap.put(get,sMap.get(get)+1);
                    }else{
                        sMap.put(get,1);
                    }
                    
                    if(sMap.get(get) > wordsMap.get(get)){//目前截取的字符串的个数超了
                        while(left <= end - step){
                            left += step;
                            if(s.substring(left - step,left).equals(get)){
                                sMap.put(get,wordsMap.get(get));
                                break;
                            }else{
                                if(sMap.get(s.substring(left - step,left)) == 1){
                                    sMap.remove(s.substring(left - step,left));
                                }else{
                                    sMap.put(s.substring(left - step,left),sMap.get(s.substring(left - step,left))-1);
                                }
                            }
                        }
                    }
                }else{//找不到,直接清空哈希表
                    left = end;
                    sMap.clear();
                }
                
                if(end - left == step * words.length){
                    list.add(left);
                    if(sMap.get(s.substring(left,left + step)) == 1){
                        sMap.remove(s.substring(left,left + step));
                    }else{
                        sMap.put(s.substring(left,left + step),sMap.get(s.substring(left,left + step))-1);
                    }
                    left += step;
                }
            }
        }
        return list;
    }
}
发布了113 篇原创文章 · 获赞 206 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Geffin/article/details/102887562