HIGH高頻H2(11-20)

augfun發表於2020-12-16

HIGH.11 合併兩個排序的連結串列

輸入兩個遞增排序的連結串列,合併這兩個連結串列並使新連結串列中的節點仍然是遞增排序的。

示例1:

輸入:1->2->4, 1->3->4
輸出:1->1->2->3->4->4
限制:

0 <= 連結串列長度 <= 1000

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/he-bing-liang-ge-pai-xu-de-lian-biao-lcof
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

迭代

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode dummyHead = new ListNode(-1), pre = dummyHead;
        while (l1 != null && l2 != null) {
            if (l1.val <= l2.val) {
                pre.next = l1;
                pre = pre.next;
                l1 = l1.next;
            } else {
                pre.next = l2;
                pre = pre.next;
                l2 = l2.next;
            }
        }
        if (l1 != null) {
            pre.next = l1;
        }
        if (l2 != null) {
            pre.next = l2;
        }

        return dummyHead.next;
    }
}

遞迴

class Solution {
    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;
        }
    }
}

HIGH.12 合併K 個排序連結串列

給你一個連結串列陣列,每個連結串列都已經按升序排列。

請你將所有連結串列合併到一個升序連結串列中,返回合併後的連結串列。

 

示例 1:

輸入:lists = [[1,4,5],[1,3,4],[2,6]]
輸出:[1,1,2,3,4,4,5,6]
解釋:連結串列陣列如下:
[
  1->4->5,
  1->3->4,
  2->6
]
將它們合併到一個有序連結串列中得到。
1->1->2->3->4->4->5->6
示例 2:

輸入:lists = []
輸出:[]
示例 3:

輸入:lists = [[]]
輸出:[]

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/merge-k-sorted-lists
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

思路巨簡單,難得一次都不用除錯!!!迴圈直接搞定,什麼分治大法我不懂!

用容量為K的最小堆優先佇列,把連結串列的頭結點都放進去,然後出隊當前優先佇列中最小的,掛上連結串列,,然後讓出隊的那個節點的下一個入隊,再出隊當前優先佇列中最小的,直到優先佇列為空。

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {

        if (lists.length == 0) {
            return null;
        }

        ListNode dummyHead = new ListNode(0);
        ListNode curr = dummyHead;
        PriorityQueue<ListNode> pq = new PriorityQueue<>(new Comparator<ListNode>() {
            @Override
            public int compare(ListNode o1, ListNode o2) {
                return o1.val - o2.val;
            }
        });

        for (ListNode list : lists) {
            if (list == null) {
                continue;
            }
            pq.add(list);
        }

        while (!pq.isEmpty()) {
            ListNode nextNode = pq.poll();
            curr.next = nextNode;
            curr = curr.next;
            if (nextNode.next != null) {
                pq.add(nextNode.next);
            }
        }
        return dummyHead.next;
    }
}

HIGH.13 買賣股票的最佳時機

給定一個陣列,它的第 i 個元素是一支給定股票第 i 天的價格。

如果你最多隻允許完成一筆交易(即買入和賣出一支股票一次),設計一個演算法來計算你所能獲取的最大利潤。

注意:你不能在買入股票前賣出股票。

 

示例 1:

輸入: [7,1,5,3,6,4]
輸出: 5
解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 5 天(股票價格 = 6)的時候賣出,最大利潤 = 6-1 = 5 。
     注意利潤不能是 7-1 = 6, 因為賣出價格需要大於買入價格;同時,你不能在買入前賣出股票。
示例 2:

輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒有交易完成, 所以最大利潤為 0。

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

思路還是挺清晰的,還是DP思想:

記錄【今天之前買入的最小值】
計算【今天之前最小值買入,今天賣出的獲利】,也即【今天賣出的最大獲利】
比較【每天的最大獲利】,取最大值即可

class Solution {
    public int maxProfit(int[] prices) {
        if(prices.length <= 1)
            return 0;
        int min = prices[0], max = 0;
        for(int i = 1; i < prices.length; i++) {
            max = Math.max(max, prices[i] - min);
            min = Math.min(min, prices[i]);
        }
        return max;
    }
}

HIGH.14 買賣股票的最佳時機II

給定一個陣列,它的第 i 個元素是一支給定股票第 i 天的價格。

設計一個演算法來計算你所能獲取的最大利潤。你可以儘可能地完成更多的交易(多次買賣一支股票)。

注意:你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。

 

示例 1:

輸入: [7,1,5,3,6,4]
輸出: 7
解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 3 天(股票價格 = 5)的時候賣出, 這筆交易所能獲得利潤 = 5-1 = 4 。
     隨後,在第 4 天(股票價格 = 3)的時候買入,在第 5 天(股票價格 = 6)的時候賣出, 這筆交易所能獲得利潤 = 6-3 = 3 。
示例 2:

輸入: [1,2,3,4,5]
輸出: 4
解釋: 在第 1 天(股票價格 = 1)的時候買入,在第 5 天 (股票價格 = 5)的時候賣出, 這筆交易所能獲得利潤 = 5-1 = 4 。
     注意你不能在第 1 天和第 2 天接連購買股票,之後再將它們賣出。
     因為這樣屬於同時參與了多筆交易,你必須在再次購買前出售掉之前的股票。
示例 3:

輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒有交易完成, 所以最大利潤為 0。

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

演算法題(×) 腦筋急轉彎題( √ )

掃描一遍 只要後一天比前一天大 就把這兩天的差值加一下

class Solution {
    public int maxProfit(int[] prices) {
        int ans=0;
        for(int i=1;i<=prices.length-1;i++)
        {
            if(prices[i]>prices[i-1])
            {
                ans+=prices[i]-prices[i-1];
            }
        }
        return ans;
    }
}

HIGH.15 最大子序和

給定一個整數陣列 nums ,找到一個具有最大和的連續子陣列(子陣列最少包含一個元素),返回其最大和。

示例:

輸入: [-2,1,-3,4,-1,2,1,-5,4]
輸出: 6
解釋: 連續子陣列 [4,-1,2,1] 的和最大,為 6。
進階:

如果你已經實現複雜度為 O(n) 的解法,嘗試使用更為精妙的分治法求解。

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/maximum-subarray
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

class Solution {
    public int maxSubArray(int[] nums) {
        int res = nums[0];
        int sum = 0;
        for (int num : nums) {
            if (sum > 0)
                sum += num;
            else
                sum = num;
            res = Math.max(res, sum);
        }
        return res;
    }
}

HIGH.16 最小棧

設計一個支援 push ,pop ,top 操作,並能在常數時間內檢索到最小元素的棧。

push(x) —— 將元素 x 推入棧中。
pop() —— 刪除棧頂的元素。
top() —— 獲取棧頂元素。
getMin() —— 檢索棧中的最小元素。
 

示例:

輸入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]

輸出:
[null,null,null,null,-3,null,0,-2]

解釋:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.getMin();   --> 返回 -2.
 

提示:

pop、top 和 getMin 操作總是在 非空棧 上呼叫。

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/min-stack
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

class MinStack {
    private Node head;
    
    public void push(int x) {
        if(head == null) 
            head = new Node(x, x);
        else 
            head = new Node(x, Math.min(x, head.min), head);
    }

    public void pop() {
        head = head.next;
    }

    public int top() {
        return head.val;
    }

    public int getMin() {
        return head.min;
    }
    
    private class Node {
        int val;
        int min;
        Node next;
        
        private Node(int val, int min) {
            this(val, min, null);
        }
        
        private Node(int val, int min, Node next) {
            this.val = val;
            this.min = min;
            this.next = next;
        }
    }
}

HIGH.17 LRU 快取機制

運用你所掌握的資料結構,設計和實現一個  LRU (最近最少使用) 快取機制 。
實現 LRUCache 類:

LRUCache(int capacity) 以正整數作為容量 capacity 初始化 LRU 快取
int get(int key) 如果關鍵字 key 存在於快取中,則返回關鍵字的值,否則返回 -1 。
void put(int key, int value) 如果關鍵字已經存在,則變更其資料值;如果關鍵字不存在,則插入該組「關鍵字-值」。當快取容量達到上限時,它應該在寫入新資料之前刪除最久未使用的資料值,從而為新的資料值留出空間。
 

進階:你是否可以在 O(1) 時間複雜度內完成這兩種操作?

 

示例:

輸入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
輸出
[null, null, null, 1, null, -1, null, -1, 3, 4]

解釋
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 快取是 {1=1}
lRUCache.put(2, 2); // 快取是 {1=1, 2=2}
lRUCache.get(1);    // 返回 1
lRUCache.put(3, 3); // 該操作會使得關鍵字 2 作廢,快取是 {1=1, 3=3}
lRUCache.get(2);    // 返回 -1 (未找到)
lRUCache.put(4, 4); // 該操作會使得關鍵字 1 作廢,快取是 {4=4, 3=3}
lRUCache.get(1);    // 返回 -1 (未找到)
lRUCache.get(3);    // 返回 3
lRUCache.get(4);    // 返回 4
 

提示:

1 <= capacity <= 3000
0 <= key <= 3000
0 <= value <= 104
最多呼叫 3 * 104 次 get 和 put

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/lru-cache
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

class LRUCache {

        private int cap;
	private Map<Integer, Integer> map = new LinkedHashMap<>();  // 保持插入順序

	public LRUCache(int capacity) {
		this.cap = capacity;
	}

	public int get(int key) {
		if (map.keySet().contains(key)) {
			int value = map.get(key);
			map.remove(key);
                       // 保證每次查詢後,都在末尾
			map.put(key, value);
			return value;
		}
		return -1;
	}

	public void put(int key, int value) {
		if (map.keySet().contains(key)) {
			map.remove(key);
		} else if (map.size() == cap) {
			Iterator<Map.E***y<Integer, Integer>> iterator = map.e***ySet().iterator();
			iterator.next();
			iterator.remove();

			// int firstKey = map.e***ySet().iterator().next().getValue();
			// map.remove(firstKey);
		}
		map.put(key, value);
	}
}

HIGH.18 尋找兩個有序陣列的中位數

給定兩個大小為 m 和 n 的正序(從小到大)陣列 nums1 和 nums2。請你找出並返回這兩個正序陣列的中位數。

進階:你能設計一個時間複雜度為 O(log (m+n)) 的演算法解決此問題嗎?

 

示例 1:

輸入:nums1 = [1,3], nums2 = [2]
輸出:2.00000
解釋:合併陣列 = [1,2,3] ,中位數 2
示例 2:

輸入:nums1 = [1,2], nums2 = [3,4]
輸出:2.50000
解釋:合併陣列 = [1,2,3,4] ,中位數 (2 + 3) / 2 = 2.5
示例 3:

輸入:nums1 = [0,0], nums2 = [0,0]
輸出:0.00000
示例 4:

輸入:nums1 = [], nums2 = [1]
輸出:1.00000
示例 5:

輸入:nums1 = [2], nums2 = []
輸出:2.00000
 

提示:

nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/median-of-two-sorted-arrays
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

這道題讓我們求兩個有序陣列的中位數,而且限制了時間複雜度為O(log (m+n)),看到這個時間複雜度,自然而然的想到了應該使用二分查詢法來求解。那麼回顧一下中位數的定義,如果某個有序陣列長度是奇數,那麼其中位數就是最中間那個,如果是偶數,那麼就是最中間兩個數字的平均值。這裡對於兩個有序陣列也是一樣的,假設兩個有序陣列的長度分別為m和n,由於兩個陣列長度之和 m+n 的奇偶不確定,因此需要分情況來討論,對於奇數的情況,直接找到最中間的數即可,偶數的話需要求最中間兩個數的平均值。為了簡化程式碼,不分情況討論,我們使用一個小trick,我們分別找第 (m+n+1) / 2 個,和 (m+n+2) / 2 個,然後求其平均值即可,這對奇偶數均適用。加入 m+n 為奇數的話,那麼其實 (m+n+1) / 2 和 (m+n+2) / 2 的值相等,相當於兩個相同的數字相加再除以2,還是其本身。

這裡我們需要定義一個函式來在兩個有序陣列中找到第K個元素,下面重點來看如何實現找到第K個元素。首先,為了避免產生新的陣列從而增加時間複雜度,我們使用兩個變數i和j分別來標記陣列nums1和nums2的起始位置。然後來處理一些邊界問題,比如當某一個陣列的起始位置大於等於其陣列長度時,說明其所有數字均已經被淘汰了,相當於一個空陣列了,那麼實際上就變成了在另一個陣列中找數字,直接就可以找出來了。還有就是如果K=1的話,那麼我們只要比較nums1和nums2的起始位置i和j上的數字就可以了。難點就在於一般的情況怎麼處理?因為我們需要在兩個有序陣列中找到第K個元素,為了加快搜尋的速度,我們要使用二分法,對K二分,意思是我們需要分別在nums1和nums2中查詢第K/2個元素,注意這裡由於兩個陣列的長度不定,所以有可能某個陣列沒有第K/2個數字,所以我們需要先檢查一下,陣列中到底存不存在第K/2個數字,如果存在就取出來,否則就賦值上一個整型最大值。如果某個陣列沒有第K/2個數字,那麼我們就淘汰另一個數字的前K/2個數字即可。有沒有可能兩個陣列都不存在第K/2個數字呢,這道題裡是不可能的,因為我們的K不是任意給的,而是給的m+n的中間值,所以必定至少會有一個陣列是存在第K/2個數字的。最後就是二分法的核心啦,比較這兩個陣列的第K/2小的數字midVal1和midVal2的大小,如果第一個陣列的第K/2個數字小的話,那麼說明我們要找的數字肯定不在nums1中的前K/2個數字,所以我們可以將其淘汰,將nums1的起始位置向後移動K/2個,並且此時的K也自減去K/2,呼叫遞迴。反之,我們淘汰nums2中的前K/2個數字,並將nums2的起始位置向後移動K/2個,並且此時的K也自減去K/2,呼叫遞迴即可。

class Solution {
  public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m = nums1.length;
        int n = nums2.length;
        int left = (m + n + 1) / 2;
        int right = (m + n + 2) / 2;
        return (findKth(nums1, 0, nums2, 0, left) + findKth(nums1, 0, nums2, 0, right)) / 2.0;
    }
    //i: nums1的起始位置 j: nums2的起始位置
    public int findKth(int[] nums1, int i, int[] nums2, int j, int k){
        if( i >= nums1.length) return nums2[j + k - 1];//nums1為空陣列
        if( j >= nums2.length) return nums1[i + k - 1];//nums2為空陣列
        if(k == 1){
            return Math.min(nums1[i], nums2[j]);
        }
        int midVal1 = (i + k / 2 - 1 < nums1.length) ? nums1[i + k / 2 - 1] : Integer.MAX_VALUE;
        int midVal2 = (j + k / 2 - 1 < nums2.length) ? nums2[j + k / 2 - 1] : Integer.MAX_VALUE;
        if(midVal1 < midVal2){
            return findKth(nums1, i + k / 2, nums2, j , k - k / 2);
        }else{
            return findKth(nums1, i, nums2, j + k / 2 , k - k / 2);
        }        
    }
}

HIGH.19 最長迴文子串

給定一個字串 s,找到 s 中最長的迴文子串。你可以假設 s 的最大長度為 1000。

示例 1:

輸入: "babad"
輸出: "bab"
注意: "aba" 也是一個有效答案。
示例 2:

輸入: "cbbd"
輸出: "bb"

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/longest-palindromic-substring
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

class Solution {
    public String longestPalindrome(String s) {
        if (s == null || s.length() == 0) {
            return "";
        }
//         儲存起始位置,測試了用陣列似乎能比全域性變數稍快一點
        int[] range = new int[2];
        char[] str = s.toCharArray();
        for (int i = 0; i < s.length(); i++) {
//             把迴文看成中間的部分全是同一字元,左右部分相對稱
//             找到下一個與當前字元不同的字元
            i = findLongest(str, i, range);
        }
        return s.substring(range[0], range[1] + 1);
    }
    
    public static int findLongest(char[] str, int low, int[] range) {
//         查詢中間部分
        int high = low;
        while (high < str.length - 1 && str[high + 1] == str[low]) {
            high++;
        }
//         定位中間部分的最後一個字元
        int ans = high;
//         從中間向左右擴散
        while (low > 0 && high < str.length - 1 && str[low - 1] == str[high + 1]) {
            low--;
            high++;
        }
//         記錄最大長度
        if (high - low > range[1] - range[0]) {
            range[0] = low;
            range[1] = high;
        }
        return ans;
    }
}

HIGH.20 合併兩個有序陣列

給你兩個有序整數陣列 nums1 和 nums2,請你將 nums2 合併到 nums1 中,使 nums1 成為一個有序陣列。

 

說明:

初始化 nums1 和 nums2 的元素數量分別為 m 和 n 。
你可以假設 nums1 有足夠的空間(空間大小大於或等於 m + n)來儲存 nums2 中的元素。
 

示例:

輸入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6],       n = 3

輸出:[1,2,2,3,5,6]
 

提示:

-10^9 <= nums1[i], nums2[i] <= 10^9
nums1.length == m + n
nums2.length == n

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/merge-sorted-array
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int p = m-- + n-- - 1;
        while (m >= 0 && n >= 0) {
            nums1[p--] = nums1[m] > nums2[n] ? nums1[m--] : nums2[n--];
        }
        
        while (n >= 0) {
            nums1[p--] = nums2[n--];
        }
    }
}

 

 

 

 

 

 

相關文章