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