lc新手半个月的42道题 (带注解

2023-10-05 17:24:45 浏览数 (2)

前言

在刷了这42题后,感觉简单题都很ok了,现在开始死磕中等题。。

学习目的

按章节来刷 先理解了会写就好,不用管什么复杂度最优解

数组/字符串

删除有序数组中的重复项

快慢指针

代码语言:java复制
public static int removeDuplicates(int[] nums) {
        int n = nums.length;
        if (n == 0) {
            return 0;
        }
        int fast = 1, slow = 1;
        while (fast < n) {//* 注意这种长度的都是while
            if (nums[fast] != nums[fast - 1]) {
                nums[slow] = nums[fast];
                  slow;
            }
              fast;
        }
        return slow;
    }

合并区间 -中

排序

代码语言:java复制
public int[][] merge(int[][] intervals) {
        if (intervals.length == 0) {//非空判断
            return new int[0][2];
        }
        Arrays.sort(intervals, (v1, v2) -> v1[0] - v2[0]);//升序排序

        List<int[]> merged = new ArrayList<int[]>(); //二维数组
        for (int i = 0; i < intervals.length;   i) {
            int L = intervals[i][0], R = intervals[i][1];//定义左右边界
            //前元素右边界 与 后元素左边界 比较
            if (merged.size() == 0 || merged.get(merged.size() - 1)[1] < L) {//第一轮循环 || 边界不重叠
                merged.add(new int[]{L, R});//* 书写方式
            } else {//边界重叠
                merged.get(merged.size() - 1)[1] = Math.max(merged.get(merged.size() - 1)[1], R);
            }
        }
        return merged.toArray(new int[merged.size()][]);//* 书写方式
    }

大数相减

代码语言:java复制
package bs;

/**
 * [4399-大数相减]
 * 大数相减
 * 以字符串的形式读入两个数字a,b(a>=b),编写一个函数计算它们的差(a-b),以字符串形式返回。
 * 数据范围:每个数字的长度均为1<=len<=10000
 * 要求:时间复杂度O(n)
 * 示例输入:
 * 1001
 * 输出:
 * 99
 */
import java.util.Arrays;

public class SubtractLargeNumbers {

    public static String subtract(String a, String b) {
        // 将两个字符串用字符数组表示,反转方便从低位到高位相减
        //1.使用StringBuilder反转、转字符数组
        char[] num1 = new StringBuilder(a).reverse().toString().toCharArray();
        char[] num2 = new StringBuilder(b).reverse().toString().toCharArray();

        //2.初始化各长度和最大长度
        int len1 = num1.length;
        int len2 = num2.length;
        int maxLen = Math.max(len1, len2);

        //3.初始化result数组和borrow
        char[] result = new char[maxLen];
        int borrow = 0; // 初始化借位为0

        //4.对每位上数做减法
        for (int i = 0; i < maxLen; i  ) {
            //取每位上数,并转为int
            int digit1 = (i < len1) ? (num1[i] - '0') : 0;
            int digit2 = (i < len2) ? (num2[i] - '0') : 0;

            //减法
            int diff = digit1 - digit2 - borrow;
            //判断是否借位
            if (diff < 0) {
                diff  = 10; // 借位
                borrow = 1;
            } else {//不可能存在>10情况
                borrow = 0;
            }

            //再由int转char
            result[i] = (char) (diff   '0');
        }

        //5.去掉结果中前导零
        int leadingZeros = 0;
        for (int i = maxLen - 1; i >= 0; i--) {
            if (result[i] == '0') {
                leadingZeros  ;
            } else {
                break;
            }
        }
        // 如果结果全是零,则返回 "0"
        if (leadingZeros == maxLen) {
            return "0";
        }
        // 构建最终结果字符串,删去高位上的0
        char[] finalResult = Arrays.copyOf(result, maxLen - leadingZeros);
        // 最后再反转回去,构造成string
        return new StringBuilder(String.valueOf(finalResult)).reverse().toString();
    }

    public static void main(String[] args) {
        String a = "1001";
        String b = "99";
        String result = subtract(a, b);
        System.out.println(result);
    }
}

无重复字符的最长子串 -中

代码语言:java复制
package common.string;

import java.util.HashSet;
import java.util.Set;

/**
 * 滑动窗口 -debug就懂了
 */
public class T1 {
    public static void main(String[] args) {
        int length = lengthOfLongestSubstring("abcabcbb");
        System.out.println(length);
    }
    public static int lengthOfLongestSubstring(String s) {
        // 哈希集合,记录每个字符是否出现过
        Set<Character> occ = new HashSet<Character>();
        int n = s.length();
        // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
        int rk = -1;
        //最长长度计步器
        int ans = 0;
        for (int i = 0; i < n;   i) {
            if (i != 0) {
                // 左指针i向右移动一格,移除一个字符
                occ.remove(s.charAt(i - 1));
            }
            while (rk   1 < n && !occ.contains(s.charAt(rk   1))) {
                // 不断地移动右指针
                occ.add(s.charAt(rk   1));
                  rk;//* 别忘了移动rk
            }
            // 第 i 到 rk 个字符是一个极长的无重复字符子串
            ans = Math.max(ans, rk - i   1);
        }
        return ans;
    }
}

两数之和

代码语言:java复制
public static int[] twoSum(int[] nums, int target) {
        //1.创建哈希表
        Map<Integer, Integer> hashMap = new HashMap<Integer, Integer>(); //* ()注意别忘了
        //2.开始遍历
        for (int i = 0; i < nums.length; i  ) {
            //* 注意:要先判断再存储
            //开始判断
            if (hashMap.containsKey(target - nums[i])){
                return new int[]{hashMap.get(target - nums[i]), i};//* new别忘了
            }

            //先数组转哈希表
            hashMap.put(nums[i], i);
        }
        return new int[0];
    }

三数之和

代码语言:txt复制

寻找文件副本

set集合

代码语言:java复制
class Solution {
    public int findRepeatDocument(int[] documents) {
        Set<Integer> hmap = new HashSet<>();
        for(int doc : documents) {
            if(hmap.contains(doc)) return doc;
            hmap.add(doc);
        }
        return -1;
    }
}

杨辉三角

代码语言:java复制
public static List<List<Integer>> generate(int numRows) {
        //1.建立杨辉三角数组
        List<List<Integer>> ret = new ArrayList<List<Integer>>();
        //2.去生成每层中的元素
        for (int i = 0; i < numRows;   i) {//i:行    j:列
            List<Integer> row = new ArrayList<Integer>();
            for (int j = 0; j <= i;   j) {
                if (j == 0 || j == i) {
                    row.add(1);
                } else {
                    row.add(ret.get(i - 1).get(j - 1)   ret.get(i - 1).get(j));//* add对象是ret
                }
            }
            ret.add(row);
        }
        return ret;
    }

合并两个有序数组

代码语言:java复制
for (int i = 0; i != n;   i) {
            nums1[m   i] = nums2[i];
        }
        Arrays.sort(nums1);

移动零

代码语言:java复制
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int n = nums.size(), left = 0, right = 0;
        while (right < n) {
            if (nums[right]) {
                swap(nums[left], nums[right]);
                left  ;
            }
            right  ;
        }
    }
};

字符串相加

代码语言:java复制
public static String addStrings(String num1, String num2) {
        //1.初始化num1、num2的最大下标,还有借位add,和构造器ans
        int i = num1.length() - 1, j = num2.length() - 1, add = 0;
        StringBuffer ans = new StringBuffer();
        //2.开始相加
        while (i >= 0 || j >= 0 || add != 0) {
            //分别取数 *对于无数的位上置零
            int x = i >= 0 ? num1.charAt(i) - '0' : 0;
            int y = j >= 0 ? num2.charAt(j) - '0' : 0;
            //相加
            int result = x   y   add;
            //就算ans和add
            ans.append(result % 10);
            add = result / 10;
            //i、j分别--
            i--;
            j--;
        }
        // 计算完以后的答案需要翻转过来
        ans.reverse();
        
        return ans.toString();
    }

两个数组的交集

代码语言:java复制
    public static int[] intersection(int[] nums1, int[] nums2) {
        //1.声明set1、set2、list
        Set<Integer> set1 = new HashSet<>(),set2 = new HashSet<>();
        List<Integer> list = new ArrayList<>();
        //2.添加元素到set中
        for(int i:nums1){
            set1.add(i);
        }
        for(int i:nums2){
            set2.add(i);
        }
        //2.做保留操作
//        set1.retainAll(set2);
        Set<Integer> intersectionSet = new HashSet<Integer>();
        for (int num : set1) {
            if (set2.contains(num)) {
                intersectionSet.add(num);
            }
        }
        //3.转化为int数组返回
        return intersectionSet.stream().mapToInt(i->i).toArray();
    }

数学/位运算

x 的平方根

方法一:袖珍计算器算法

由于计算机无法存储浮点数的精确值(浮点数的存储方法可以参考 IEEE 754,这里不再赘述),因此在得到结果的整数部分 anstextit{ans}ans 后,我们应当找出 anstextit{ans}ans 与 ans 1textit{ans} 1ans 1 中哪一个是真正的答案。

代码语言:java复制
    public static int mySqrt(int x) {
        //减少算力
        if (x == 0) {
            return 0;
        }
        //换底公式
        //* 注意要转为int类型
        int ans = (int) Math.exp(0.5 * Math.log(x));
        //判断返回ans还是ans 1
        //* 注意返回的是long类型
        return (long) (ans   1) * (ans   1) <= x ? ans   1 : ans;
    }

递归/回溯

当递归函数执行到达递归基(即传递给函数的字符串满足某个终止条件)时,就会开始返回。 递归过程可以看成是入栈的过程,返回的过程就是出栈的过程。

动态规划

动态规划是一种更加系统和高效的问题解决方法,特别适用于那些可以分解为子问题并且有最优子结构性质的问题

买卖股票的最佳时机

一次遍历

代码语言:java复制
public static int maxProfit(int prices[]) {
        //1.初始化
        int minprice = Integer.MAX_VALUE;
        int maxprofit = 0;
        //2.开始计算
        for (int i = 0; i < prices.length; i  ) {
            //小:获取minprice
            if (prices[i] < minprice) {
                minprice = prices[i];

            //大:获取maxprofit
            } else if (prices[i] - minprice > maxprofit) {
                maxprofit = prices[i] - minprice;
            }
        }
        return maxprofit;
    }

买卖股票的最佳时机 II

代码语言:txt复制

使用最小花费爬楼梯

原始版本

代码语言:java复制
class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int n = cost.length;
        int[] dp = new int[n   1];
        dp[0] = dp[1] = 0;
        for (int i = 2; i <= n; i  ) {
            dp[i] = Math.min(dp[i - 1]   cost[i - 1], dp[i - 2]   cost[i - 2]);
        }
        return dp[n];
    }
}

使用滚动数组思想,将空间复杂度优化到 O(1)O(1)O(1)

代码语言:java复制
//1.初始化n、prev、curr
        int n = cost.length;
        int prev = 0,curr = 0;
        //2.n>2的情况
        for (int i = 2; i <= n; i  ) {
            int next = Math.min(curr   cost[i-1],prev   cost[i-2]);
            prev = curr;
            curr = next;
        }
        //3.遍历完返回curr值
        return curr;

最大子数组和

f(i)=max{f(i−1) numsi,numsi}

代码语言:java复制
public static int maxSubArray(int[] nums) {
        int max = nums[0]; //* 设置第一个元素为最大值,减少一次遍历
        int pre = 0;
        for (int x : nums) {
            pre = Math.max(pre   x,x);
            max = Math.max(max,pre);
        }
        return max;
    }

爬楼梯

f(x)=f(x−1) f(x−2) 滚动数组的方法

代码语言:java复制
public static int climbStairs(int n) {
        int pre=0,cur=0,next=1;
        while (n-- != 0){
            pre = cur;
            cur = next;
            next = pre   cur;
        }
        return next;
    }

链表

合并两个有序链表

迭代法

代码语言:java复制
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode prehead = new ListNode(-1);

        ListNode prev = prehead;
        while (l1 != null && l2 != null) { //* 用while
            if (l1.val <= l2.val) {
                prev.next = l1;
                l1 = l1.next;
            } else {
                prev.next = l2;
                l2 = l2.next;
            }
            prev = prev.next;
        }

        // *合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
        prev.next = l1 == null ? l2 : l1;

        return prehead.next;
    }
}

回文链表

双指针法

代码语言:java复制
class Solution {
    public boolean isPalindrome(ListNode head) {
        List<Integer> vals = new ArrayList<Integer>();

        // 将链表的值复制到数组中
        ListNode currentNode = head;
        while (currentNode != null) {//* 用while
            vals.add(currentNode.val);
            currentNode = currentNode.next;
        }

        // 使用双指针判断是否回文
        int front = 0;
        int back = vals.size() - 1;
        while (front < back) {
            if (!vals.get(front).equals(vals.get(back))) {
                return false;
            }
            front  ;
            back--;
        }
        return true;
    }
}

反转链表

思路

代码语言:java复制
 /**
     * 整体思路:每次迭代将当前节点的next指针指向前一个节点,然后更新指针的位置,最终实现链表的反转
     * @param head
     * @return
     */
    public static ListNode reverseList(ListNode head) {
        //先一个指向头一个指向尾
        ListNode prev = null; //反转要指向的节点
        ListNode curr = head; //当前遍历节点
        while (curr != null) {
            //这里和数组交换完全没有关系
            ListNode next = curr.next; //先保存好curr的next
            curr.next = prev; //核心:实现当前节点的反转
            prev = curr; //将反转后部分 连接上完整的反转链表
            curr = next; //进行遍历下一个节点
        }
        return prev;
    }

环形链表

哈希表

代码语言:java复制
 public boolean hasCycle(ListNode head) {
        Set<ListNode> seen = new HashSet<ListNode>();//* 存的类型是ListNode
        while (head != null) {
            if (!seen.add(head)) {
                return true;
            }
            head = head.next;//* 要后移啊
        }
        return false;
    }

链表中倒数第k个节点

自己写出来

代码语言:java复制
public static ListNode getKthFromEnd(ListNode head, int k) {
        //倒数改成正数 - 正数n=len-倒数k
        //1.计算len
        int len = 1;
        ListNode pre = head;//记录头结点
        while (pre!=null){
            len  ;
            pre = pre.next;
        }
        int n = len - k;

        pre = head;//再次指向头结点
        while (n-- != 1){
            pre = pre.next;
        }
        return pre;
    }

相交链表

自己写出来

代码语言:java复制
//有相同节点且第一个相同节点就是答案
        HashSet<ListNode> hashSet = new HashSet<>();

        //把两个链表分别加入hashset中,第一个加入不成功的节点就是答案
        //1.先加入headA
        while (headA!=null){
            hashSet.add(headA);
            headA = headA.next;
        }

        while (headB!=null){
            if (!hashSet.add(headB)){
//                return headB;
                break;
            }
            headB = headB.next;
        }
        return headB;

环形链表 II -中

代码语言:java复制
public ListNode detectCycle(ListNode head) {
        ListNode pos = head;
        Set<ListNode> visited = new HashSet<ListNode>();
        while (pos != null) {
            if (visited.contains(pos)) {
                return pos;
            } else {
                visited.add(pos);
            }
            pos = pos.next;
        }
        return null;
    }

复制带随机指针的链表 -中

哈希表

代码语言:java复制
if(head == null){
            return null;
        }
        Node cur = head;
        HashMap<Node,Node> map = new HashMap<>();
        while(cur!=null){//新链表节点的构建
            map.put(cur,new Node(cur.val));
            cur = cur.next;
        }
        cur=head;
        while(cur!=null){//新链表指向的构建
            map.get(cur).next=map.get(cur.next);
            map.get(cur).random=map.get(cur.random);
            cur=cur.next;
        }
        return map.get(head);//* 注意返回值

合并 K 个升序链表 -难

顺序合并

代码语言:java复制
public static ListNode mergeKLists(ListNode[] lists) {
        ListNode ans = null;
        for (int i = 0; i < lists.length;   i) {
            ans = mergeTwoLists(ans, lists[i]);
        }
        return ans;
    }

    public static ListNode mergeTwoLists(ListNode a, ListNode b) {
        ListNode prehead = new ListNode(-1); //初始化头指针
        ListNode prev = prehead;//用于遍历的指针
        while (a!=null && b!=null){
            if (a.val >= b.val){
                prev.next = b;
                b = b.next;
            } else {
                prev.next = a;
                a = a.next;
            }
            prev = prev.next;
        }
        // *合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
        prev.next = a == null ? b : a;

        return prehead.next;

    }

K 个一组翻转链表 -难

代码语言:java复制
public ListNode reverseKGroup(ListNode head, int k) {
    ListNode dummy = new ListNode(0);
        dummy.next = head; //* 忘记

        ListNode pre = dummy;
        ListNode end = dummy;
        
        while (end.next != null){
            //1.设置
            for (int i=0;i<k && end!=null;i  ) end = end.next;
            if (end == null) break; //* 老是忘记写
            ListNode start = pre.next;//开始翻转部分
            ListNode next = end.next;//未翻转部分

            //2.开始翻转
            end.next = null; pre.next = reverse(start);

            //3.跳跃到下一个分组
            start.next = next; //* 跳跃对象是next

            //4.重置
            pre = start;
            end = pre;
        }
        return dummy.next;
}

private ListNode reverse(ListNode head) {
    ListNode pre = null;
    ListNode curr = head;
    while (curr != null) {
        ListNode next = curr.next;
        curr.next = pre;
        pre = curr;
        curr = next;
    }
    return pre;
}

LRU 缓存 -中

有效的括号

代码语言:java复制
class Solution {
    public boolean isValid(String s) {
        int n = s.length();
        if(n % 2 == 1){
            return  false;
        }
        Map<Character, Character> map = new HashMap<Character, Character>() {{
            // 将 })] 作为key
            put('}', '{');
            put(']', '[');
            put(')', '(');
        }};
        // 新建一个栈
        Stack<Character> stack = new Stack<>();

        for (int i = 0; i < n; i  ) {
            char c = s.charAt(i);
            // 如果c是 })], 则判断, 否则说明是({[ , 直接入栈
            if(map.containsKey(c)){
                // stack.peek() 获取栈顶元素
                if(stack.isEmpty() || stack.peek() != map.get(c)){
                    return false;
                }
                else{// 将栈顶移除(先进后出,栈顶是最接近 c 的左括号)
                stack.pop();
                }
            }else{
                // 说明c是({[ , 直接入栈
            stack.push(c);
        }
        }
        return stack.isEmpty();
    }
}

综合题

反转字符串

反转字符串有多种方式可以实现,以下是几种常见的方式:

1.使用StringBuilder或StringBuffer的reverse方法:

代码语言:java复制
  String str = "Hello, World!";
   StringBuilder sb = new StringBuilder(str);
   String reversedString = sb.reverse().toString();
   System.out.println(reversedString);

2.使用字符数组:

代码语言:java复制
  String str = "Hello, World!";
   char[] charArray = str.toCharArray();
   int left = 0;
   int right = charArray.length - 1;
   while (left &lt; right) {
       char temp = charArray[left];
       charArray[left] = charArray[right];
       charArray[right] = temp;
       left  ;
       right--;
   }
   String reversedString = new String(charArray);
   System.out.println(reversedString);

3.使用递归:

代码语言:java复制
 public String reverseString(String str) {
       if (str.isEmpty()) {
           return str;
       }
       return reverseString(str.substring(1))   str.charAt(0);
   }

   String str = "Hello, World!";
   String reversedString = reverseString(str);
   System.out.println(reversedString);

4.使用栈:

代码语言:java复制
  import java.util.Stack;

   String str = "Hello, World!";
   Stack&lt;Character&gt; stack = new Stack&lt;&gt;();
   for (char c : str.toCharArray()) {
       stack.push(c);
   }
   StringBuilder sb = new StringBuilder();
   while (!stack.isEmpty()) {
       sb.append(stack.pop());
   }
   String reversedString = sb.toString();
   System.out.println(reversedString);

这些只是一些常见的反转字符串的方式,实际上还有其他一些方式,例如使用递归和位操作等。选择哪种方式取决于具体的需求和场景。

我正在参与2023腾讯技术创作特训营第二期有奖征文,瓜分万元奖池和键盘手表

0 人点赞