LeetCode刷题记录——经典100题


LC01:两数之和

 可以采用暴力解法和哈希表

class Solution {
    public int[] twoSum(int[] nums, int target) {
    //哈希表法
    //建立哈希表
    Map<Integer, Integer> hashtable = new HashMap<Integer, Integer>();
        for (int i = 0; i < nums.length; ++i) {
            //如果有target - 自己 的差
            if (hashtable.containsKey(target - nums[i])) {
                //如果找得到另一个加数则返回两数的下标
                return new int[]{hashtable.get(target - nums[i]), i};
            }
            //如果没找到,就把当前的key-value存入map
            hashtable.put(nums[i], i);
        }
        //无结果返回空数组
        return new int[0];
    }
}

LC02:两数相加

相当于反过来的加法,采用迭代和递归两种算法

迭代法
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        int next = 0;
        int total = 0;
        ListNode asm = new ListNode();
        ListNode cur = asm;
        while(l1 != null && l2 != null){
            total = l1.val + l2.val + next;
            cur.next = new ListNode(total % 10);
            next = total / 10;
            l1 = l1.next;
            l2 = l2.next;
            cur = cur.next;
        }
        while(l1 != null){
            total = l1.val + next;
            cur.next = new ListNode(total % 10);
            next = total / 10;
            l1 = l1.next;
            cur = cur.next;
        }
         while(l2 != null){
            total = l2.val + next;
            cur.next = new ListNode(total % 10);
            next = total / 10;
            l2 = l2.next;
            cur = cur.next;
        }
        while(next != 0){
            cur.next = new ListNode(next);
        }
        return asm.next;
    }
}

//递归法
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        int total = l1.val + l2.val;
        //表示进位是多少
        int next = total / 10;
        ListNode res = new ListNode(total % 10);
        if(l1.next != null || l2.next != null || next != 0){
            l1 = l1.next != null ? l1.next : new ListNode(0);
            l2 = l2.next != null ? l2.next : new ListNode(0);
            l1.val += next;
            res.next = addTwoNumbers(l1,l2);
        }
        return res;
    }
}

 


LC03:无重复字符的最长子串

采用滑动窗口

class Solution {
    public int lengthOfLongestSubstring(String s) {
        //滑动窗口
        if(s.length() == 0) return 0;
        HashMap<Character,Integer> map = new HashMap<Character,Integer>();
        int max = 0;
        int left = 0;
        for(int i = 0; i < s.length(); i++){
            if(map.containsKey(s.charAt(i))){
                left = Math.max(left,map.get(s.charAt(i))+1);
            }
            map.put(s.charAt(i), i);
            max = Math.max(max, i - left +1);
            //max为重复元素下标 - 之前原本拥有的重复元素的下标 +1 
            //(即两者之间的距离,即最长子串)
        }
        return max;
    }
}

LC04:寻找两个正序数组的中位数

采用将两个数组中的所有元素排序的方法

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    //建立一个新数组存储两个里的元素
    int[] nums;
    int m = nums1.length;
    int n = nums2.length;
    nums = new int[m + n];

//1.先将两个数组合并排序

    //如果某个数组为空
    if (m == 0) {
        //奇偶数的中位数所在位置不同
        if (n % 2 == 0) {
            return (nums2[n / 2 - 1] + nums2[n / 2]) / 2.0;
        } else {
            return nums2[n / 2];
        }
    }
    if (n == 0) {
        if (m % 2 == 0) {
            return (nums1[m / 2 - 1] + nums1[m / 2]) / 2.0;
        } else {
            return nums1[m / 2];
        }
    }

    //两者都不为空
    int count = 0;
    int i = 0, j = 0;
    while (count != (m + n)) {
        //数组一到顶
        if (i == m) {
            while (j != n) {
                nums[count++] = nums2[j++];
            }
            break;
        }
        //数组二到顶
        if (j == n) {
            while (i != m) {
                nums[count++] = nums1[i++];
            }
            break;
        }
        if (nums1[i] < nums2[j]) {
            nums[count++] = nums1[i++];
        } else {
            nums[count++] = nums2[j++];
        }
    }

//2.再计算中位数位置
    if (count % 2 == 0) {
        return (nums[count / 2 - 1] + nums[count / 2]) / 2.0;
    } else {
        return nums[count / 2];
    }
    }
}

LC05:最长回文子串

暴力法但是会超出时间限制

class Solution {
    public String longestPalindrome(String s) {
        int len = s.length();
        String ans = "";
        int max = 0;
        for(int i = 0; i < len; i++){
            for(int j = i + 1; j <= len; j++){
                //s.substring表示截取s的下标第 i 到第 j-1 个字符
                String test = s.substring(i,j);
                if(isPalindrome(test) && test.length() > max){
                    ans = s.substring(i, j);
                    max = Math.max(ans.length(), max);
                }
            }
        }
        return ans;
    }

    //定义一个函数判断字符串是否回文
    public boolean isPalindrome(String s){
        int length = s.length();
        //只需检查一半长度即可
        for(int i = 0; i < length/2; i++){
            //如果有一个两边的对应位置字符不同,则不是回文
            if(s.charAt(i) != s.charAt(length-i-1)){
                return false;
            }
        }
        return true;
    }
}

LC10: 正则表达式匹配

不会


LC11:盛最多水的容器

双指针两头向中间的解法

class Solution {
    public int maxArea(int[] height) {
        //从两边向中间求解
        //每次只需要移动较短的板
        //因为当移动长板时,底会变小,而高会变小或不变,面积一定变小
        //但是移动短板就可能会使面积变大
        int len = height.length;
        int i = 0, j = len - 1, res = 0;
        while(i < j){
            res = height[i] < height[j] ?
                //j - i需要放在height[i++]前面是因为
                //如果height[i++]在前面那么i j的值就会先发生改变
                Math.max(res, (j - i) * height[i++]):
                Math.max(res, (j - i) * height[j--]);
        }
        return res;
    }
}

LC15:三数之和

排序+双指针

class Solution {
    //二维数组List<List<template>>
    public List<List<Integer>> threeSum(int[] nums) {
        //先对数组进行排序然后采用双指针
        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();
        //建立k,为三个数中的最小值
        for(int k = 0; k < nums.length - 2; k++){
            //如果k大于0那么三个数之和不可能为0
            if(nums[k] > 0) break;
            //如果num[k]和num[k+1]重复,那么跳到下一个值
            if(k > 0 && nums[k] == nums[k-1]) continue;
            //i从k+1开始,j从末尾开始
            int i = k + 1, j = nums.length - 1;
            while(i < j){
                int sum = nums[k] + nums[i] + nums[j];
                if(sum > 0){
                     //这么写容易出错,不要乱学
                    while(i < j && nums[j] == nums[--j]);
                }
                else if(sum < 0){
                    while(i < j && nums[i] == nums[++i]);
                }
                else{
                    //sum为0的时候加入这三个数到List里
                    //自己写一遍加深印象
//res.add(new ArrayList<Integer>(Arrays.asList(nums[k],nums[i],nums[j])));
                    res.add(new ArrayList<Integer>(Arrays.asList(nums[k], nums[i], nums[j])));
                    //i增j减,并跳过所有重复的i, j
                    while(i < j && nums[i] == nums[++i]);
                    while(i < j && nums[j] == nums[--j]);
                }
            }
        }
        return res;
    }
}

LC17:电话号码的字母组合

没看懂


LC19:删除链表的倒数第 N 个结点

双链表法和栈(复杂度较高)

//双链表
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        //表示per.next = head;而pre为空指针
        ListNode pre = new ListNode(0, head);
        ListNode start = pre, end = pre;
        //start先往前n个节点
        while(n != 0) {
            start = start.next;
            n--;
        }
        //然后一起往后移动直到start的next指向空
        while(start.next != null) {
            start = start.next;
            end = end.next;
        }
        end.next = end.next.next;
        return pre.next;

        //
        ListNode dummy = new ListNode(0, head);
        //建立一个栈Deque
        Deque<ListNode> stack = new LinkedList<ListNode>();
        ListNode cur = dummy;
        //全部入栈
        while (cur != null) {
            stack.push(cur);
            cur = cur.next;
        }
        //出栈n个,栈顶的数就是倒数第 n+1 个
        for (int i = 0; i < n; ++i) {
            stack.pop();
        }
        //stack.peek()指向栈顶元素
        ListNode prev = stack.peek();
        //栈顶元素的next即为倒数第n项
        prev.next = prev.next.next;
        ListNode ans = dummy.next;
        return ans;
    }
}
LC20:有效的括号

 

采用栈

class Solution {
    public boolean isValid(String s) {
        //如果是奇数则直接false
        if(s.length() % 2 == 1) return false;
        //建立哈希表
        Map<Character, Character> pairs = new HashMap<Character, Character>() {{
            put('(', ')');
            put('[', ']');
            put('{', '}');
        }};
        //如果最开始是右括号直接结束
        if(pairs.containsValue(s.charAt(0))) return false;
        //建立栈
        Deque<Character> stack = new LinkedList<Character>();
        //遍历
        for(Character c : s.toCharArray()){
            //如果是左括号则入栈
            if(pairs.containsKey(c)) stack.push(c);
            //如果是右括号且栈顶是其对应的左括号则左括号出栈
            else if(pairs.get(stack.peek()) == c) stack.pop();
            //否则false
            else {return false;}
        }
        //最后队列如果不为空,则false
        return stack.isEmpty();
    }
}

LC21:合并两个有序链表

递归and迭代

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        //迭代法
        ListNode pre = new ListNode(0);
        ListNode prev = pre;
        while(l1 != null && l2!= null){
            //Java的链表值是val不是value
            if(l1.val <= l2.val){
                //哪个val小指向哪个,且链表next
                prev.next = l1;
                l1 = l1.next;
            }else{
                prev.next = l2;
                l2 = l2.next;
            }
            //不要记得把prev也next
            prev = prev.next;
        }
        prev.next = l1 ==null? l2 : l1;
        return pre.next;

        //递归法
        if(l1 == null){
            return l2;
        } else if(l2 == null){
            return l1;
        } else if(l1.val <= l2.val){
            l1.next = mergeTwoLists(l1.next,l2);
            return l1;
        } else{
            l2.next = mergeTwoLists(l1,l2.next);
            return l2;
        }
    }
}

LC22: 括号生成

这是一个满二叉树,要做的是DFS所有节点

class Solution {
    List<String> res = new ArrayList<>();
    public List<String> generateParenthesis(int n) {
        //如果n < 0则null
        if (n <= 0) return null;
        //最开始paths是空的,left,right都赋为0
        def("", 0, 0, n);
        return res;
        }

    //遍历二叉树的函数
    public void def(String paths, int left, int right, int n) {
        //left或者right的数量不大于n即可
        if (left > n || right > left) return;
        //下面这行错误,因为必须现有左括号才有右括号,所有左必先比右达到n
        // if (right > n || left > right) return;
        
        //如果有满足条件的2n长度的paths则添加进res里
        //只有当最后一次迭代才会进这个if
        if (paths.length() == n * 2) {
            res.add(paths);
            return;
        }
        //继续进行,生成一个paths就加一个左或右括号,知道left/right到达max值n
        def(paths + "(", left + 1, right, n);
        def(paths + ")", left, right + 1, n);
    }
}

LC23:合并K个升序链表

分治成单个链表,然后逐一比较每个链表的头结点的val

class Solution {
    //merge,英文为合并的意思
    public ListNode mergeKLists(ListNode[] lists) {
        return merge(lists, 0, lists.length - 1);
    }

    //分治,不断地寻找ListNodep[]数组里的lists
    public ListNode merge(ListNode[] lists, int l, int r) {
        if (l == r) {
            return lists[l];
        }
        if (l > r) {
            return null;
        }
        int mid = (l + r) >> 1;
        //直到最终分割成单独的list,来merge排序
        return mergeTwoLists(merge(lists, l, mid), merge(lists, mid + 1, r));
    }

    //合并两个链表的迭代算法
    public ListNode mergeTwoLists(ListNode a, ListNode b) {
        if (a == null || b == null) {
            return a != null ? a : b;
        }
        ListNode head = new ListNode(0);
        ListNode tail = head, aPtr = a, bPtr = b;
        while (aPtr != null && bPtr != null) {
            if (aPtr.val < bPtr.val) {
                tail.next = aPtr;
                aPtr = aPtr.next;
            } else {
                tail.next = bPtr;
                bPtr = bPtr.next;
            }
            tail = tail.next;
        }
        tail.next = aPtr == null ? bPtr : aPtr;
        return head.next;
    }
}

LC31: 下一个排列

//在尽可能靠右的低位进行交换,需要从后向前查找
//将一个尽可能小的「大数」与前面的「小数」交换。
//比如 123465,下一个排列应该把 5 和 4 交换而不是把 6 和 4 交换
//将「大数」换到前面后,需要将「大数」后面的所有数重置为升序
//升序排列就是最小的排列    
//以 123465 为例:首先按照上一步,交换 5 和 4,得到 123564;
//然后需要将 5 之后的数重置为升序,得到 123546。
//显然 123546 比 123564 更小,123546 就是 123465 的下一个排列
class Solution {
    public void nextPermutation(int[] nums) {
        int i = nums.length - 2;
        //按照从后往前,寻找前面比后面大的【小数】(可交换)
        while (i >= 0 && nums[i] >= nums[i + 1]) {
            i--;
        }
        //如果还没到数组头
        if (i >= 0) {
            int j = nums.length - 1;
            //尽可能往后寻找【大数】与刚刚找到的【小数】交换
            while (j >= 0 && nums[i] >= nums[j]) {
                j--;
            }
            //交换大小数
            swap(nums, i, j);
        }
        //如果没有找到可以交换的,则重排序整个数组(即升序排列)
        reverse(nums, i + 1);
    }

    public void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

    public void reverse(int[] nums, int start) {
        int left = start, right = nums.length - 1;
        while (left < right) {
            swap(nums, left, right);
            left++;
            right--;
        }
    }
}

// class Solution {
//     public void nextPermutation(int[] nums) {
//         int len = nums.length;
//         for (int i = len - 1; i > 0; i--) {
//             //从后往前先找出第一个相邻的后一个大于前一个情况
//             //此时的i-1位置就是需要交换的位置
//             if (nums[i] > nums[i - 1]) {
//                 Arrays.sort(nums, i, len);
//                 //对i自己和之后的元素排序,[i,len)从小到大
//                 for (int j = i; j <len; j++) {
//                     //第一个大于i-1位置的进行交换,那么就是下一个排列
//                     if (nums[j] > nums[i - 1]) {
//                         int temp = nums[j];
//                         nums[j] = nums[i - 1];
//                         nums[i - 1] = temp;
//                         return;
//                     }
//                 }
//             }
//         }
//         Arrays.sort(nums);
//         //最后3,2,1情况的下一个就是1,2,3要重新排列成最小的
//         //这种情况上面的交换执行不了
//         return;  
//     }
// }

LC32:最长有效括号

//建立一个栈,利用找到一对()就出栈的原则
//利用当前位置减去栈顶位置可得到pop出的括号数
class Solution {
    public int longestValidParentheses(String s) {
        //如果为空
        if (s == null || s.length() == 0) return 0;
        //建立一个栈
        Deque<Integer> stack = new ArrayDeque<>();
        //这一步可以防止当第一个Character是')'的时候发生越界异常
        stack.push(-1);
        //System.out.println(stack);
        //可以看到stack是[-1]
        int res = 0;
        //遍历栈找寻合适的左右括号
        for (int i = 0; i < s.length(); i++) {
            //如果找到左括号则入栈,为寻找对应右括号做铺垫
            if (s.charAt(i) == '(') stack.push(i);
            else {
                //如果是右括号则出栈
                stack.pop();
                //但是如果栈是空的话还是得(单身的)把右括号放进来
                if (stack.isEmpty()) stack.push(i);
                else {
                    //当前全部人数减去剩余无法配对的人数(单身)即res
                    res = Math.max(res, i - stack.peek());
                }
            }
        }
        return res;
    }
}

LC33:搜索旋转排序数组

 

//二分查找
class Solution {
    public int search(int[] nums, int target) {
        int n = nums.length;
        if (n == 0) {
            return -1;
        }
        if (n == 1) {
            return nums[0] == target ? 0 : -1;
        }
        int l = 0, r = n - 1;
        while (l <= r) {
            int mid = (l + r) / 2;
            if (nums[mid] == target) {
                return mid;
            }
            if (nums[0] <= nums[mid]) {
                if (nums[0] <= target && target < nums[mid]) {
                    r = mid - 1;
                } else {
                    l = mid + 1;
                }
            } else {
                if (nums[mid] < target && target <= nums[n - 1]) {
                    l = mid + 1;
                } else {
                    r = mid - 1;
                }
            }
        }
        return -1;
    }
}

LC34:在排序数组中查找元素的第一个和最后一个位置

class Solution {
    public int[] searchRange(int[] nums, int target) {
        if(nums.length == 0) return new int[]{-1,-1};
        //二分范围
        int l = 0, r = nums.length - 1; 
        //查找元素的开始位置
        while(l < r){
            //向下取整
            int mid = (l + r)/2;
            if(nums[mid] >= target) r = mid;
            else l = mid + 1;
        }
        //查找失败
        if(nums[r] != target) return new int[]{-1,-1};
        //记录左界
        int LEFT = r;

        //二分范围
        l = 0; r = nums.length - 1;
        //查找元素的结束位置
        while(l < r){
            //向上取整,防止循环r = l + 1
            int mid = (l + r + 1)/2;
            if(nums[mid] <= target) l = mid;
            else r = mid - 1;
        }
        //记录右界
        int RIGHT = r;

        return new int[]{LEFT,RIGHT};
    }
}

LC39:组合总和

树形图深度遍历 产生重复
class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        //为空
        if (candidates == null)  return res;
        //path是一个栈,用来存储从根结点到叶子结点的路径
        Deque<Integer> path = new ArrayDeque<>();
        //深搜
        dfs(candidates, 0, candidates.length, target, path, res);
        return res;
    }

    void dfs(int[] candidates, int begin, int len, int target, Deque<Integer> path, List<List<Integer>> res) {
        //符合要求(已剪枝)
        if (target == 0){
            res.add(new ArrayList<>(path));
            return;
        }
        //从 begin 开始搜索
        for (int i = begin; i < len; i++) {
            //如果减数大于目标值,则差为负数,不符合结果
            if (candidates[i]<=target){
                //addLast表示向栈尾添加元素,传值不能为空
                path.addLast(candidates[i]);
                //由于每一个元素可以重复使用,下一轮搜索的起点依然是 i,这里非常容易弄错
                dfs(candidates,i,candidates.length,target - candidates[i],path,res);
                //每次回溯将最后一次加入的让 target 等于 0 或者 负数 的元素删除
                path.removeLast();
                递归之前 => [2],剩余 = 5
            }
        }
    }
}

//可以看到removeLast的作用
// 递归之前 => [2, 2],剩余 = 3
// 递归之前 => [2, 2, 2],剩余 = 1
// 递归之后 => [2, 2]
// 递归之前 => [2, 2, 3],剩余 = 0
// 递归之后 => [2, 2]
// 递归之后 => [2]
// 递归之前 => [2, 3],剩余 = 2
// 递归之后 => [2]
// 递归之后 => []
// 递归之前 => [3],剩余 = 4
// 递归之前 => [3, 3],剩余 = 1
// 递归之后 => [3]
// 递归之后 => []
// 递归之前 => [6],剩余 = 1
// 递归之后 => []
// 递归之前 => [7],剩余 = 0
// 递归之后 => []
// 输出 => [[2, 2, 3], [7]]

LC42:接雨水

/* 一行一行进行求解
 * 求第 i 层的水,遍历每个位置
 * 如果当前的高度小于 i,并且两边有高度大于等于 i 的
 * 说明这个地方一定有水,水就可以加 1
 */
//超出时间限制
class Solution {
    public int trap(int[] height) {
        int res = 0;
        int H = getMax(height);
        //一层一层求解
        for(int i = 0; i <= max; i++){
            //是否更新temp_sum
            boolean isStart = false;
            //一层有的积水初始为0(可理解为低洼块)
            int temp_sum = 0;
            for(int j = 0; j < height.length; j++){
                //当temp_sum要开始更新,并且当前高度小于i
                //设置这个if表示只有当出现低洼(小于i)时,才会在遇到高于i的柱子时加水s
                if(isStart && height[j] < i){
                    //之后如果有高于 i 的必定可以加1,所以先改变temp_sum
                    temp_sum++;
                }
                //如果两边高度大于等于i
                if(height[j] >= i){
                    sum = sum + temp_sum;  //有水,sum可以加1
                    temp_sum = 0;          //重置temp_sum
                    isStart = true;        //重置更新状态
                }
            }
        }
        return sum;
    }

    private int getMax(int[] height){
        int max = 0;
        for(int i = 0; i < height.length; i++){
            if(height[i] > max) max = height[i];
        }
        return max;
    } 
}
栈解法

class Solution {
    public int trap(int[] walls) {
        if (walls == null || walls.length <= 2) {
            return 0;
        }

        //单调不增栈,walls元素作为右墙依次入栈
        //出现入栈元素(右墙)比栈顶大时,说明在右墙左侧形成了低洼处
        //低洼处出栈并结算该低洼处能接的雨水

        int water = 0;
        Stack<Integer> stack = new Stack<>();
        for (int right=0; right<walls.length; right++) {
            //栈不为空,且当前元素(右墙)比栈顶(右墙的左侧)大:说明形成低洼处了
            while (!stack.isEmpty() && walls[right]>walls[stack.peek()]) {
                //低洼处弹出,尝试结算此低洼处能积攒的雨水
                int bottom = stack.pop();
                //看左墙是否存在
                //有右墙+有低洼+没有左墙=白搭
                if (stack.isEmpty()) {
                    break;
                }

                //左墙位置以及左墙、右墙、低洼处的高度
                int left = stack.peek();
                int leftHeight = walls[left];
                int rightHeight = walls[right];
                int bottomHeight = walls[bottom];

                // 能积攒的水=(右墙位置-左墙位置-1) * (min(右墙高度, 左墙高度)-低洼处高度)
                water += (right-left-1) * (Math.min(leftHeight, rightHeight)-bottomHeight);
            }

            // 上面的pop循环结束后再push,保证stack是单调不增(保证之后栈顶pop的元素存在)
            stack.push(right);
        }

        return water;
    }
}

LC46:全排列

回溯算法公式

backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
    
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

代码:

class Solution {
    List<List<Integer>> rlc = new ArrayList<>();  //存储返回结果
    List<Integer> rls = new ArrayList<>(3);   //存储当前列表中元素

    public List<List<Integer>> permute(int[] nums) {
        backtacking(nums,0);
        //返回最终的结果集
        return rlc;
    }

    void backtacking(int[] nums , int startIndex){
        if(rls.size() == nums.length){
            //将rls结果集加入进来
            rlc.add(new ArrayList<>(rls));
            return;
        }
        //排列从0开始
        for(int i = 0; i < nums.length; i++){
            //如果集合中存在相同的元素则不会把nums[i]加入进去
            if(rls.contains(nums[i]) == false){
                rls.add(nums[i]);
                backtacking(nums,i + 1);
                //撤销处理结点,确保rls.size()-1不会发生数组下标越界的情况
                rls.remove(rls.size()-1);
            }
        }
    }
}

LC48:旋转图像

class Solution {
    public void rotate(int[][] matrix) {
        int n = matrix.length;
        // 水平翻转
        for (int i = 0; i < n / 2; ++i) {
            for (int j = 0; j < n; ++j) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[n - i - 1][j];
                matrix[n - i - 1][j] = temp;
            }
        }
        // 主对角线翻转
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = temp;
            }
        }
    }
}

LC49:字母异位词分组

class Solution {
     public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, ArrayList<String>> map = new HashMap<>();
        for (int i = 0; i < strs.length ; i++) {
            //toCharArray将字符转换为字符数组
            char[] chars = strs[i].toCharArray();
            //这里将char都变成了有序的,如“aet”
            Arrays.sort(chars);
            //String.valueOf(char[] data):将char数组data转换成字符串 
            //通过有序的字母寻找
            String key = String.valueOf(chars);
            if (!map.containsKey(key)) {
                //没有key则加一个
                map.put(key, new ArrayList<>());
            }
            //根据对应key放入string
            map.get(key).add(strs[i]);
        }
        return new ArrayList<>(map.values());
    }
}

LC53:最大子序和

动态规划

class Solution {
    public int maxSubArray(int[] nums) {
        int pre = 0, maxAns = nums[0];
        for (int x : nums) {
            pre = Math.max(pre + x, x);
            maxAns = Math.max(maxAns, pre);
        }
        return maxAns;
    }
}

LC55: 跳跃游戏

贪心算法

class Solution {
    public boolean canJump(int[] nums) {
        //如果某一个作为起跳点的格子可以跳跃的距离是 3,那么表示后面 3 个格子都可以作为起跳点
        //可以对每一个能作为起跳点的格子都尝试跳一次,把能跳到最远的距离不断更新
        //如果可以一直跳到最后,就成功了
        int n = nums.length;
        int k = 0;
        if(nums.length == 1) return true;
        else
            if(nums[0] == 0) return false;
        for (int i = 0; i < n; i++) {
            //当前的距离比最远能跳到的距离大则无法成功
            if (i > k) return false;
            k = Math.max(k, i + nums[i]);
        }
        return true;
    }
}

LC56:合并区间

import java.util.Arrays;

public class LC_56 {
    public int[][] merge(int[][] intervals) {
        //先按照区间起始位置排序
        //假设传来两个值,v1 与 v2,那么他们的先后顺序以 v1[0] 比 v2[0] 的结果为准,即:若 v1[0] < v2[0] 则 v1 < v2,若 = 则 =,若 > 则 >
        //即按照vn[0]升序排列
        Arrays.sort(intervals, (v1, v2) -> v1[0] - v2[0]);
        int[][] res = new int[intervals.length][2];
        int idx = -1;
        //遍历区间
        for (int[] interval: intervals) {
            //如果结果数组是空的,或者当前区间的起始位置 > 结果数组中最后区间的终止位置,
            //则不合并,直接将当前区间加入结果数组。
            if (idx == -1 || interval[0] > res[idx][1]) {
                res[++idx] = interval;
            } else {
                //反之将当前区间合并至结果数组的最后区间
                res[idx][1] = Math.max(res[idx][1], interval[1]);
            }
        }
        //Arrays的copyOf()方法传回的数组是新的数组对象,改变传回数组中的元素值,不会影响原来的数组
        //copyOf()的第二个自变量指定要建立的新数组长度,如果新数组的长度超过原数组的长度,则保留数组默认值
        //如:
        //int[] arr1 = {1, 2, 3, 4, 5}; 
        //int[] arr2 = Arrays.copyOf(arr1, 10);
        //输出arr2为:
        //1 2 3 4 5 0 0 0 0 0
        return Arrays.copyOf(res, idx + 1);
    }

    public static void main(String[] args) {
        int[][] intervals = new int[][] {{1,3},{2,6},{8,10},{15,18}};
        LC_56 lc = new LC_56();
        //首先:定义一个date数组for循环这个数组
        for (int[] data : lc.merge(intervals))
            //直接用Arrays.toString(date)方法
            System.out.println(Arrays.toString(data));
    }
}

LC62:不同路径

public class LC_62 {
    public int uniquePaths(int m, int n) {
        //dp为到达该点的最多路径数
        int[][] dp = new int[m][n];
        //最左边一行和最上面一行的dp只能是1,因为只可能从上面或者左边来
        for (int i = 0; i < n; i++) dp[0][i] = 1;
        for (int i = 0; i < m; i++) dp[i][0] = 1;

        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
        return dp[m - 1][n - 1];
    }

    public static void main(String[] args) {
        LC_62 lc = new LC_62();
        int m = 3, n = 7;
        System.out.print(lc.uniquePaths(m, n));
    }
}

 



 

更一下基础知识:

哈希表:

哈希表里的Key不允许存放基本数据类型(char,boolean,byte,short,int,long,float,double)

HashSet只有Key,HashMap里存在Key --> Value的映射关系。

HashSet<String> set = new HashSet<>();

HashMap<Integer,String> map = new HashMap<>();

哈希表不管数据量多大,增删改查时间复杂度都是O(1)。

当哈希表里存放入基础数据类型时,内部按值传递,占用空间大小与数据本身有关;

当不是基础类型时,内部按引用传递,内存占用是地址大小。

 

有序表:

Set、Map同理

有序表不管数据量多大,增删改查时间复杂度都是O(logN)。

当有序表里存放入基础数据类型时,内部按值传递,占用空间大小与数据本身有关;

当不是基础类型时,必须提供比较器,内部按引用传递,内存占用是地址大小。

 

二叉树的先、中、后序遍历来由:(递归实现)

分别是由递归序每个数第一(第一次来到)、第二(遍历完左树)、第三次(遍历完右树)出现为统计打印的结果。

 

遍历的非递归:

前序遍历

后序遍历:

中序遍历:

 

特别的:宽度优先遍历

先左再右

求得宽度

 

 

 示例:

 

 

编辑于 2021-12-03 18:01

 

LC39:组合总和


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM