【LeetCode】 21 - 30题解

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,sb + "(");
        if(r > 0) dfs(l,r - 1,sb + ")");
    }
}

使用StringBuilder,需要状态重置。

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

        if(l > r) return;
        if(l > 0){
            sb.append("(");
            dfs(l - 1,sb);
            sb.deleteCharAt(sb.length() -1);
        } 
        if(r > 0) {
            sb.append(")");
            dfs(l,sb);
            sb.deleteCharAt(sb.length() -1);
        }
    }
}

23. 合并K个升序链表

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

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

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,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,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.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,2],函数应该返回新的长度 2,并且原数组 nums 的前两个元素被修改为 1,2。 

你不需要考虑数组中超出新长度后面的元素。

示例 2:

给定 nums = [0,函数应该返回新的长度 5,并且原数组 nums 的前五个元素被修改为 0,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,3],val = 3,并且 nums 中的前两个元素均为 2。

你不需要考虑数组中超出新长度后面的元素。

示例 2:

给定 nums = [0,val = 2,并且 nums 中的前五个元素为 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;
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


输出自然数 1 到 n所有不重复的排列,即 n的全排列,要求所产生的任一数字序列中不允许出现重复的数字。
编写一个能够输出“`Hello,World!`”的程序,这个程序常常作为一个初学者接触一门新的编程语言所写的第一个程序,也经常用来测试开发、编译环境是否能够正常工作。&#xA;&#xA;提示:“`Hello,Worl
题目相关 【题目描述】 求10000以内n的阶乘。 【输入】 只有一行输入,整数n(0≤n≤10000)。 【输出】 一行,即n!的值。 【输入样例】 4 【输出样例】 24 分析 首先n的阶乘是从1
题目相关 原题链接:P5461 赦免战俘 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目背景 借助反作弊系统,一些在月赛有抄袭作弊行为的选手被抓出来了! 题目描述 现有 $2n
水仙花数 题目描述 输出所有的&amp;quot;水仙花数&amp;quot;.所谓&amp;quot;水仙花数&amp;quot;是指这样的一个三位数:其各位数字的立方和等于该数本身。例如:371是
题目描述 奖学金 某小学最近得到了一笔赞助,打算拿出其中一部分为学习成绩优秀的前5名学生发奖学金。期末,每个学生都有3门课的成绩:语文、数学、英语。先按总分从高到低排序,如果两个同学总分相同,再按语文
已知正整数k满足2≤k≤9,现给出长度最大为30位的十进制非负整数c,求所有能整除c的k。
题目相关 题目描述 我们要求找出具有下列性质数的个数(包含输入的正整数 n)。 先输入一个正整数 n(n ≤1000),然后对此正整数按照如下方法进行处理: 不作任何处理; 在它的左边加上一个正整数,
题目相关 题目描述 排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且 r ≤n),我们可以简单地将n个元素理解为自然数1,2,…,n从中任取r个数。 现要求你输出所有组合。
题目相关 题目描述 把 m个同样的苹果放在 n个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法。(5,1,1 和 1,1,5 是同一种方法) 输入格式 第一行是测试数据的数目 t,以下每行
题目相关 【题目描述】 求两个不超过200位的非负整数的和。 【输入】 有两行,每行是一个不超过200位的非负整数,可能有多余的前导0。 【输出】 一行,即相加后的结果。结果里不能有多余的前导0,即如
请你编一程序实现两种不同进制之间的数据转换。
题目相关 【题目描述】 求两个大的正整数相减的差。 【输入】 共2行,第1行是被减数a,第2行是减数b(a &amp;gt; b)。每个大整数不超过200位,不会有多余的前导零。 【输出】 一行,即所
题目描述 一个数如果恰好等于不包含它本身所有因子之和,这个数就称为&amp;quot;完数&amp;quot;。 例如,6的因子为1、2、3,而6=1ʲʳ,因此6是&amp;quot;完数&amp
题目相关 【题目描述】 任意给定一个正整数N(N≤100),计算2的n次方的值。 【输入】 输入一个正整数N。 【输出】 输出2的N次方的值。 【输入样例】 5 【输出样例】 32 分析 本题考察的是
题目相关 【题目描述】 输入三个整数,整数之间由一个空格分隔,整数是32位有符号整数。把第二个输入的整数输出。 【输入】 只有一行,共三个整数,整数之间由一个空格分隔。整数是32位有符号整数。 【输出
79. 单词搜索 给定一个二维网格和一个单词,找出该单词是否存在于网格中。 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母
[toc] 707.设计双向链表 链表简介 链表(LinkedList) 是一种线性表,但不是顺序表,因为它是通过节点直接的相互引用相互联系起来的。 由于不必按顺序存储, 链表在插入和删除的时候可以达
题目描述 784. 字母大小写全排列 给定一个字符串S,通过将字符串S中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合。 示例: 输入:S = &amp;quot;a1
[toc] Leetcode动态规划【简单题】 动态规划 (Dynamic programming,简称DP),是一种把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划相较于递归,拥有更