LeetCode 451根据字符出现的频率排序、LeetCode 228汇总区间、LeetCode 2两数相加、LeetCode 19删除链表的倒数第 N 个结点、LeetCode 22括号生成DFS

LeetCode 451根据字符出现的频率排序

题目描述:
给定一个字符串 s ,根据字符出现的 频率 对其进行 降序排序 。一个字符出现的 频率 是它出现在字符串中的次数。
返回 已排序的字符串 。如果有多个答案,返回其中任何一个。
示例 1:
输入: s = “tree”
输出: “eert”
解释: 'e’出现两次,'r’和’t’都只出现一次。
因此’e’必须出现在’r’和’t’之前。此外,"eetr"也是一个有效的答案。

示例 2:
输入: s = “cccaaa”
输出: “cccaaa”
解释: 'c’和’a’都出现三次。此外,"aaaccc"也是有效的答案。
注意"cacaca"是不正确的,因为相同的字母必须放在一起。

示例 3:
输入: s = “Aabb”
输出: “bbAa”
解释: 此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。
注意’A’和’a’被认为是两种不同的字符。

一、使用哈希表记录每个字符出现的频率,将字符去重后存入列表,再将列表中的字符按照频率list存字符并联系map对列表降序排序。最后使用线程安全的Stringbuffer提取字符和频率来存放答案,输出StringBuffer.toString()

  • 时间复杂度O(n+klogk)【将字符按照出现频率排序需要 O(k \log k)O(klogk) 的时间。】
  • 空间复杂度:O(n + k)O(n+k),其中 nn 是字符串 ss 的长度,kk 是字符串 ss 包含的不同字符的个数。空间复杂度主要取决于哈希表、列表和生成的排序后的字符串。

可通过完整代码:

public String frequencySort(String s) {
        Map<Character, Integer> map = new HashMap<>();
        int l = s.length();
        for (int i = 0; i < l; i++) {   // 使用map存出现次数
            char c = s.charAt(i);
            int fre = map.getOrDefault(c, 0) + 1;
            map.put(c, fre);
        }
        List<Character> list = new ArrayList<Character>(map.keySet());
        Collections.sort(list, (a, b) -> map.get(b) - map.get(a));   // 使用list存字符并自定义排序
        StringBuffer sb = new StringBuffer();   // 新建Stringbuffer提取字符和频率存放答案
        for (int i = 0; i < list.size(); i++) {
            char c = list.get(i);
            int fre = map.get(c);
            for (int j = 0; j < fre; j++) {
                sb.append(c);
            }
        }
        return sb.toString();
    }

二、桶排序使用StringBuffer[] buckets = new StringBuffer[maxFre + 1]次数和字符;【由于每个字符在字符串中出现的频率存在上限,因此可以使用桶排序的思想,根据出现次数生成排序后的字符串。具体做法如下:】

  • 遍历字符串,统计每个字符出现的频率,同时记录最高频率maxFreq;
  • 创建桶,存储从1到maxFreq 的每个出现频率的字符;
  • 按照出现频率从大到小的顺序遍历桶,对于每个出现频率,获得对应的字符,然后将每个字符按照出现频率拼接到排序后的字符串。

可通过完整代码:

public String buckets_frequencySort(String s) {
        Map<Character, Integer> map = new HashMap<>();
        int maxFre = 0;
        for (int i = 0; i < s.length(); i++) {   // 统计出最多频率
            char ch = s.charAt(i);
            int fre = map.getOrDefault(ch, 0) + 1;
            map.put(ch, fre);
            maxFre = Math.max(maxFre, fre);
        }
        StringBuffer[] buckets = new StringBuffer[maxFre + 1];   // 此处从0-maxFre省事,方便最后可以直接从maxFre-1遍历
        for (int i = 0; i <= maxFre; i++) {   // 初始化buckets
            buckets[i] = new StringBuffer();
        }
        for (Map.Entry<Character, Integer> entry : map.entrySet()) {   // 将从1到maxFre的字符装入桶中(entrySet不含重复key)
            char ch = entry.getKey();
            int fre = entry.getValue();
            buckets[fre].append(ch);
        }
        StringBuffer sb = new StringBuffer();
        for (int i = maxFre; i > 0; i--) {
            StringBuffer bucket = buckets[i];
            for (int j = 0; j < bucket.length(); j++) {   // 长度为i的bucket中可能不止一种字符
                for (int k = 0; k < i; k++) {   // 多次存放相同字符串的所有同一单个字符charAt(j)
                    sb.append(bucket.charAt(j));
                }
            }
        }
        return sb.toString();
    }

LeetCode 228汇总区间

题目描述:
给定一个 无重复元素 的 有序 整数数组 nums 。
返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表 。也就是说,nums 的每个元素都恰好被某个区间范围所覆盖,并且不存在属于某个范围但不属于 nums 的数字 x 。
列表中的每个区间范围 [a,b] 应该按如下格式输出:
“a->b” ,如果 a != b
“a” ,如果 a == b

示例 1:
输入:nums = [0,1,2,4,5,7]
输出:[“0->2”,“4->5”,“7”]
解释:区间范围是:
[0,2] --> “0->2”
[4,5] --> “4->5”
[7,7] --> “7”

示例 2:
输入:nums = [0,2,3,4,6,8,9]
输出:[“0”,“2->4”,“6”,“8->9”]
解释:区间范围是:
[0,0] --> “0”
[2,4] --> “2->4”
[6,6] --> “6”
[8,9] --> “8->9”

一、一次遍历,维护下标low和high记录区间的起点和终点,while循环i<n,其中low=i记录,i++判断i<n&&nums[i] == nums[i-1]+1;如果否,high=i-1,这样low==high不满足加"->"的条件了

可运行完整代码:

public List<String> summaryRanges(int[] nums) {
        List<String> ret = new ArrayList<>();
        int i = 0;
        int n = nums.length;
        while (i < n) {
            int low = i;
            i++;
            while (i < n && nums[i] == nums[i - 1] + 1) {   // 如果此处的i==i-1处+1,继续while
                i++;
            }
            int high = i - 1;
            StringBuffer temp = new StringBuffer(Integer.toString(nums[low]));   // 存low
            if (low < high) {
                temp.append("->");
                temp.append(Integer.toString(nums[high]));   // 存high
            }
            ret.add(temp.toString());   // StringBuffer有函数toString()可以转为String
        }
        return ret;
    }

LeetCode 2两数相加(低位在前,高位在后)

题目描述:
在这里插入图片描述
示例 3:
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

一、进位carry最开始设为0,之后计算sum = v1 + v2 + carry,然后更新carry=sum/10。每次while循环中第一次head==null,之后else;最后判断本次l1和l2是否为空来更新l1=l1.next

可通过完整代码:

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode head = null, tail = null;
        int carry = 0;   // 第一次进位为0
        while (l1 != null || l2 != null) {
            int n1 = l1 != null ? l1.val : 0;
            int n2 = l2 != null ? l2.val : 0;
            int sum = n1 + n2 + carry;   // 之后用carry = sum / 10查看是否有进位
            if (head == null) {
                head = tail = new ListNode(sum % 10);   // 第一个直接head=tail赋值
            } else {
                tail.next = new ListNode(sum % 10);
                tail = tail.next;   // 第二个移动tail的引用
            }
            carry = sum / 10;   // 0-9为0,10+输出进位1。每一次的进位都由sum/10再次得到
            if (l1 != null) {   // 每一轮结束后为null的一个listnode不动,移动不为null的一个
                l1 = l1.next;
            }
            if (l2 != null) {
                l2 = l2.next;
            }
        }
        if (carry > 0) {   // 进位>0,再接一个tail.next()
            tail.next = new ListNode(carry);
        }
        return head;
    }

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

题目描述:
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
在这里插入图片描述

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:
输入:head = [1], n = 1
输出:[]

示例 3:
输入:head = [1,2], n = 1
输出:[1]

一、我们首先从头节点开始对链表进行一次遍历,得到链表的长度 L。随后我们再从哑节点开始对链表进行一次遍历,当遍历走到第L-n个节点时,它的下一个节点就是我们需要删除的节点。

!!!要有哑节点,否则[1]1这种情况的时候就找不到1的前一个节点了,处理不了。

  • 时间复杂度:O(L),其中L是链表的长度
  • 空间复杂度:O(1)

可通过完整代码:

public ListNode removeNthFromEnd(ListNode head, int n) {
    ListNode dummy = new ListNode(0);
    dummy.next = head;
    ListNode fast = head;
    ListNode slow = dummy;
    for (int i = 0; i < n; i++) {
        fast = fast.next;
    }
    while (fast != null) {   // 因为slow在head的前一个,所以比如【1 2 3】 1 当fast到达null的时候,slow到达的3的前一个2这里
        slow = slow.next;
        fast = fast.next;
    }
    ListNode temp = slow.next;   // 1-->2-->3将1-->3之后,2和3之间的指向断开
    slow.next = slow.next.next;
    temp.next = null;
    return dummy.next;
}

在这里插入图片描述

LeetCode 22括号生成DFS

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

示例 1:
输入:n = 3
输出:[“((()))”,“(()())”,“(())()”,“()(())”,“()()()”]

示例 2:
输入:n = 1
输出:[“()”]

一、只有当左右字符数量一样多从左往右数右括号数量不能比左括号多的时候,括号的组合才是合法的,我们要记录的就是这种组合。

二、先写return条件,left>right,即目前cur使用的右括号比左括号数量多,然后判断left>0或者right>0(add条件是&&&)左右分支

在这里插入图片描述

可通过完整代码:

public List<String> generateParenthesis(int n) {
    List<String> list = new ArrayList<>();
    String cur = "";
    dfs(list, cur, n, n, n);
    return list;
}

private void dfs(List<String> list, String cur, int left, int right, int max) {   // 自顶向下分叉递归,刚开始的变量:list等都相当于函数中的全局变量
    if (cur.length() == 2 * max && left == 0 && right == 0) {   // left == 0 && right == 0的时候才会添加到list并return
        list.add(cur);
        return;
    }
    if (left > right) return;   // 当cur的右括号大于左括号就为无效字符串
    if (left > 0) dfs(list, cur + "(", left - 1, right, max);   // left和right不全为0的时候分别添加
    if (right > 0) dfs(list, cur + ")", left, right - 1, max);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值