zoukankan      html  css  js  c++  java
  • 【LeetCode】 21

    21. 合并两个有序链表

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

    示例:

    输入:1->2->4, 1->3->4
    输出:1->1->2->3->4->4
    

    归并思想

        public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
            ListNode dummy = new ListNode(0);
            ListNode curr = dummy;
    
            while(l1 != null && l2 != null){
                if(l1.val <= l2.val){
                    curr.next = l1;
                    l1 = l1.next;
                }else{
                    curr.next = l2;
                    l2 = l2.next;
                }
                curr = curr.next;
            }
            curr.next = (l1 != null) ? l1 : l2;
            return dummy.next;
        }
    

    递归

        public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
            if(l1 == null) return l2;
            if(l2 == null) return l1;
            if(l1.val < l2.val){
                l1.next = mergeTwoLists(l1.next, l2);
                return l1;
            }else{
                l2.next = mergeTwoLists(l1, l2.next);
            	return l2; 
            }
        }
    

    22. 括号生成

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

    示例:

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

    使用String,每次状态变更都会创建新的对象,不需要状态重置。

    class Solution {
        public List<String> generateParenthesis(int n) {
            List<String> res = new ArrayList<>();
            String sb = "";
            dfs(n, n, res, sb);
            return res;
        }
    
        void dfs(int l,int r, List<String> res, String sb){
            if(l == 0 && r == 0){
                res.add(sb);
                return;
            }
    		//保证一旦右括号先出现就停止
            if(l > r) return;
            if(l > 0) dfs(l - 1, r, res, sb + "(");
            if(r > 0) dfs(l, r - 1, res, sb + ")");
        }
    }
    

    使用StringBuilder,需要状态重置。

    class Solution {
        public List<String> generateParenthesis(int n) {
            List<String> res = new ArrayList<>();
            StringBuilder sb = new StringBuilder();
            dfs(n, n, res, sb);
            return res;
        }
    
        void dfs(int l,int r, List<String> res, StringBuilder sb){
            if(l == 0 && r == 0){
                res.add(sb.toString());
                return;
            }
    
            if(l > r) return;
            if(l > 0){
                sb.append("(");
                dfs(l - 1, r, res, sb);
                sb.deleteCharAt(sb.length() -1);
            } 
            if(r > 0) {
                sb.append(")");
                dfs(l, r - 1, res, sb);
                sb.deleteCharAt(sb.length() -1);
            }
        }
    }
    

    23. 合并K个升序链表

    给你一个链表数组,每个链表都已经按升序排列。

    请你将所有链表合并到一个升序链表中,返回合并后的链表。

    示例 1:

    输入: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
    

    示例 2:

    输入:lists = []
    输出:[]
    

    示例 3:

    输入:lists = [[]]
    输出:[]
    

    提示:

    • k == lists.length
    • 0 <= k <= 10^4
    • 0 <= lists[i].length <= 500
    • -10^4 <= lists[i][j] <= 10^4
    • lists[i]升序 排列
    • lists[i].length 的总和不超过 10^4

    分治+ 归并

    class Solution {
        public ListNode mergeKLists(ListNode[] lists) {
            if(lists == null || lists.length == 0) return null;
            return merge(lists, 0 , lists.length - 1);
        }
    
        ListNode merge(ListNode[] lists, int l, int r){
            if(l == r) return lists[l];
            int mid = l + r >>> 1;
            ListNode l1 = merge(lists, l , mid);
            ListNode l2 = merge(lists, mid + 1, r);
            return mergeTwoLists(l1, l2);
        }
    
        ListNode mergeTwoLists(ListNode l1, ListNode l2){
            if(l1 == null) return l2;
            if(l2 == null) return l1;
            if(l1.val < l2.val){
                l1.next = mergeTwoLists(l1.next, l2);
                return l1;
            }else{
                l2.next = mergeTwoLists(l1,l2.next);
                return l2;
            }
        }
    }
    

    优先队列

    class Solution {
        public ListNode mergeKLists(ListNode[] lists) {
            Queue<ListNode> pq = new PriorityQueue<>((v1, v2) -> v1.val - v2.val);
            for (ListNode node: lists) {
                if (node != null) {
                    pq.offer(node);
                }
            }
    
            ListNode dummyHead = new ListNode(0);
            ListNode tail = dummyHead;
            while (!pq.isEmpty()) {
                ListNode minNode = pq.poll();
                tail.next = minNode;
                tail = minNode;
                if (minNode.next != null) {
                    pq.offer(minNode.next);
                }
            }
    
            return dummyHead.next;
        }
    }
    
    作者:sweetiee
    链接:https://leetcode-cn.com/problems/merge-k-sorted-lists/solution/4-chong-fang-fa-xiang-jie-bi-xu-miao-dong-by-sweet/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    

    24. 两两交换链表中的节点

    给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

    你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

    示例:

    给定 1->2->3->4, 你应该返回 2->1->4->3.
    

        public ListNode swapPairs(ListNode head) {
            ListNode dummy = new ListNode(-1);
            dummy.next = head;
    
            for(ListNode p = dummy; p.next != null && p.next.next != null; ){
                ListNode a = p.next,b = a.next;
                p.next = b;
                a.next = b.next;
                b.next = a;
                p = a;
            }
            return dummy.next;
        }
    

    25. K 个一组翻转链表

    给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。

    k 是一个正整数,它的值小于或等于链表的长度。

    如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

    示例:

    给你这个链表:1->2->3->4->5

    k = 2 时,应当返回: 2->1->4->3->5

    k = 3 时,应当返回: 3->2->1->4->5

    说明:

    • 你的算法只能使用常数的额外空间。
    • 你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
        public ListNode reverseKGroup(ListNode head, int k) {
            if (head == null) return null;
            // 区间 [a, b) 包含 k 个待反转元素
            ListNode a, b;
            a = b = head;
            for (int i = 0; i < k; i++) {
                // 不足 k 个,不需要反转,base case
                if (b == null) return head;
                b = b.next;
            }
            // 反转前 k 个元素, 返回的值就是反转之后的head
            ListNode newHead = reverse(a, b);
            // 递归反转后续链表并连接起来
            a.next = reverseKGroup(b, k);
            return newHead;
            
        }
    	//反转 [head, tail)
        ListNode reverse(ListNode head ,ListNode tail){
            ListNode p = head, pre = null, next = null;
            while(p != tail){
                next = p.next;
                p.next = pre;
                pre = p;
                p = next;
            }
            return pre;
        }
    

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

    给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

    不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 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。
    
    你不需要考虑数组中超出新长度后面的元素。
    
       public int removeDuplicates(int[] nums) {
           if( nums.length == 0) return 0;
           //慢指针
           int i = 0;
           //快指针
           for(int j = 1; j < nums.length; j ++){
               if(nums[j] != nums[i]) nums[++i] = nums[j];
           }
           //返回长度
           return i + 1;
       }
    

    27. 移除元素

    给你一个数组 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。
    
    注意这五个元素可为任意顺序。
    
    你不需要考虑数组中超出新长度后面的元素。
    
        public int removeElement(int[] nums, int val) {
            int k = 0;
            for(int i = 0; i < nums.length; i ++){
                if(nums[i] != val){
                    nums[k++] = nums[i];
                }
            }
            return k;
        }
    

    28. 实现 strStr()

    实现 strStr() 函数。

    给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1

    示例 1:

    输入: haystack = "hello", needle = "ll"
    输出: 2
    

    示例 2:

    输入: haystack = "aaaaa", needle = "bba"
    输出: -1
    

    说明:

    needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。

    对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。

    class Solution {
        public int strStr(String haystack, String needle) {
            int m = haystack.length(), n = needle.length();
            if (n == 0) return 0;
            for(int i = 0;i <= m - n ; i ++){
                //if(haystack.substring(i , i + n).equals(needle))  return i;
                if(check(haystack.substring(i , i + n), needle))  return i;
            }
            return -1;
        }
    	//相当于equals函数
        boolean check(String s1, String s2){
            int k = 0;
            while(k < s2.length()){
                if(s1.charAt(k) != s2.charAt(k)) return false;
                k ++;
            }
            return true;
        }
    }
    

    29. 两数相除

    给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。

    返回被除数 dividend 除以除数 divisor 得到的商。

    整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2

    示例 1:

    输入: dividend = 10, divisor = 3
    输出: 3
    解释: 10/3 = truncate(3.33333..) = truncate(3) = 3
    

    示例 2:

    输入: dividend = 7, divisor = -3
    输出: -2
    解释: 7/-3 = truncate(-2.33333..) = -2
    

    提示:

    • 被除数和除数均为 32 位有符号整数。
    • 除数不为 0。
    • 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。本题中,如果除法结果溢出,则返回 231 − 1。

    将被除数和除数都转成正数或负数进行计算,由于在Java中,当t=Integer.MIN_VALUE时(t取相反数依旧是它本身)此时可能存在越界问题,因此都用负数进行计算

        //O(N) 时间复杂度 超时
        public int divide(int a, int b) {
            //特殊情况
            if(a == Integer.MIN_VALUE && b == -1) return Integer.MAX_VALUE;
            //k标识a与b是否同号
            boolean k = (a > 0 && b > 0) || (a < 0 && b < 0);
            int res = 0;
            //转化为负数
            a = -Math.abs(a);
            b = -Math.abs(b);
            //循环减相当于除法操作
            while(a <= b){
                a -= b;
                res ++;
            }
            return k? res : -res;
        }
    

    使用二分法改善,a每次减去(2^n)个b,res每次加上(2^n)

        //O(log(n)) 
        public int divide(int a, int b) {
            //特殊情况
            if(a == Integer.MIN_VALUE && b == -1) return Integer.MAX_VALUE;
            //k标识a与b是否同号
            boolean k = (a > 0 && b > 0) || (a < 0 && b < 0);
            int res = 0;
            //转化为负数
            a = -Math.abs(a);
            b = -Math.abs(b);
            //循环减相当于除法操作
            while(a <= b){
                int temp = b;
                int c = 1;
                //每次减去2^n
                while(a - temp <= temp){
                    temp = temp << 1;
                    c = c << 1;
                }
                a -= temp;
                res += c;
            }
            return k? res : -res;
        }
    

    30. 串联所有单词的子串

    给定一个字符串 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"]
    输出:[]
    

    https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/

    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> res = new ArrayList<>();
        int wordNum = words.length;
        if(wordNum == 0) return res;
        int wordLen = words[0].length();
        // allWords存储所有单词
        HashMap<String, Integer> allWords = new HashMap<>();
        for(String w : words) allWords.put(w, allWords.getOrDefault(w, 0) + 1);
        // 遍历所有子串
        for(int i = 0; i < s.length() - wordNum * wordLen + 1; i ++){
            // hasWords存储当前扫描的字符串含有的单词
            HashMap<String, Integer> hasWords = new HashMap<>();
            int num = 0;
            // 判断该子串是否符合
            while(num < wordNum){
                //每次取一段
                String word = s.substring(i + num * wordLen, i + (num + 1) * wordLen);
                if(allWords.containsKey(word)){
                    hasWords.put(word, hasWords.getOrDefault(word, 0) + 1);
                    // 判断当前单词的value 和 allWords中该单词的value
                    if(hasWords.get(word) > allWords.get(word)) break;
                }else{
                    break;
                }
                num ++;
            }
            // 判断是不是所有的单词都符合条件
            if(num == wordNum) res.add(i);
        }
        return res;
    }
    
  • 相关阅读:
    express 的安全中间件 helmet 简介
    一个设置过期时间的方案
    vscode 中的 vue 格式化
    linux 中部署不同版本 node.js 并同时使用的方案
    webpack 多页面模式配置
    1.assert
    我是一个线程(转)
    Android FragmentTransactionExtended:使Fragment以多种样式动画切换
    Android ORM应用开发框架KJFrameForAndroid使用详解
    Android Studio插件推荐(PreIOC,GsonFormat)
  • 原文地址:https://www.cnblogs.com/summerday152/p/13768301.html
Copyright © 2011-2022 走看看