基礎1: partition
Partition Array
Given an array nums
of integers and an int k
, partition the array (i.e move the elements in "nums") such that:
- All elements < k are moved to the left
- All elements >= k are moved to the right
Return the partitioning index, i.e the first index inums[i] >= k.
If nums = [3,2,2,1]
and k=2
, a valid answer is 1
.
如果控制條件是 i < j 的話, 那么在如下情況下會出錯
[7,7,9,8,6,6,8,7,9,8,6,6], 10
因為所有數字都小於10,那么i index 應該一直遞增到超過j
所以應該為 i <= j
我的代碼:

public class Solution { /** *@param nums: The integer array you should partition *@param k: As description *return: The index after partition */ public int partitionArray(int[] nums, int k) { //write your code here int i = 0, j = nums.length - 1; while (i <= j) { if (nums[i] < k && nums[j] < k) { i++; } else if (nums[i] < k && nums[j] >= k) { i++; j--; } else if (nums[i] >= k && nums[j] >= k) { j--; } else if (nums[i] >= k && nums[j] < k) { int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; i++; j--; } } return i; } }
其實當左邊index的值比k小的時候 i可以一直遞增
右邊index的值比k大的時候 j可以一直遞減
那么當兩遍都違背的時候,做一次sawp
簡潔到飛起來= =

public class Solution { /** *@param nums: The integer array you should partition *@param k: As description *return: The index after partition */ public int partitionArray(int[] nums, int k) { if(nums == null || nums.length == 0){ return 0; } int left = 0, right = nums.length - 1; while (left <= right) { while (left <= right && nums[left] < k) { left++; } while (left <= right && nums[right] >= k) { right--; } if (left <= right) { int temp = nums[left]; nums[left] = nums[right]; nums[right] = temp; left++; right--; } } return left; } }
----------------------------------------分割線-------------------------------
Kth Largest Element in an Array
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.
For example,
Given [3,2,1,5,6,4]
and k = 2, return 5.
這種算法的均攤復雜度是o(n)
T(n) = T(n/2) + o(n)
= T(n/4) + 0(n/2)+o(n)
=...
平均的時間復雜度是o(nlogn), 不理想的情況就是每次partition只排除掉1個點,那么時間復雜度會退化到o(n^2)
注意幾個細節:
1. helper函數是int,遞歸返回最后的結果,一直返回到最外面一層
2. 這里的partition和前面partition array不同, 以left節點做為pivot,然后從右邊開始走,把第一個不符合的放在左邊,然后左邊開始走,把不符合的放在右邊。最后把pivot放回去新的left節點的位置
3. 找第k個大的點 轉化為找第length - k + 1的小的點

public class Solution { public int findKthLargest(int[] nums, int k) { if (nums == null || nums.length == 0) { return -1; } return helper (nums, nums.length - k + 1, 0, nums.length - 1); //length - k + 1 convert the kth large to k'th small one } private int helper(int[] nums, int k, int start, int end) { if (start == end) { return nums[start]; } int pos = partition(nums, start, end); if (pos + 1 == k) { return nums[pos]; } else if (pos + 1 > k) { return helper (nums, k, start, pos - 1); } else { /*這個地方為什么是return 下一層遞歸的結果,因為需要的結果在下層/下下層遞歸中得到, 把這個值返回來交給最上面的一層*/ return helper (nums, k, pos + 1, end); } } public int partition(int[] nums, int l, int r) { // 初始化左右指針和pivot int left = l, right = r; int pivot = nums[left]; // 進行partition while (left < right) { while (left < right && nums[right] >= pivot) { right--; } nums[left] = nums[right]; while (left < right && nums[left] <= pivot) { left++; } nums[right] = nums[left]; } // 返還pivot點到數組里面 nums[left] = pivot; return left; } }
median
這道題是找第k大題目的子集
一樣的方法

public class Solution { /** * @param nums: A list of integers. * @return: An integer denotes the middle number of the array. */ public int median(int[] nums) { // write your code here if (nums == null || nums.length == 0) { return -1; } return helper(nums, 0, nums.length - 1, nums.length / 2 + nums.length %2); } private int helper(int[] nums, int left, int right, int k) { if (left == right) { return nums[left]; } int pos = partition(nums, left, right); if (pos + 1 == k) { return nums[pos]; } else if (pos + 1 > k) { return helper (nums, left, pos - 1, k); } else { return helper (nums, pos + 1, right, k); } } private int partition (int[] nums, int left, int right) { int pivot = nums[left]; while (left < right) { while (left < right && nums[right] >= pivot) { right--; } nums[left] = nums[right]; while (left < right && nums[left] <= pivot) { left++; } nums[right] = nums[left]; } nums[left] = pivot; return left; } }
-----------------------------分割線-------------------------------------
There are two sorted arrays nums1 and nums2 of size m and n respectively.
Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
Example 1:
nums1 = [1, 3] nums2 = [2] The median is 2.0
Example 2:
nums1 = [1, 2] nums2 = [3, 4] The median is (2 + 3)/2 = 2.5
暴力的方法: 每次取兩個array的頭一個元素進行比較,誰小誰出列,知道出列了總長度/2. 所以總的時間復雜度為o(k)
把線性時間復雜度再去做優化,只能是logn,如何把線性復雜度優化為logn呢?
涉及到binarysearch的第三層理解,每次做o(1)的操作之后,能保留一半解,也就是說把解的范圍縮小一半。
附:binary search 筆記 https://i.cnblogs.com/EditPosts.aspx?postid=5875846
利用binary search / quick select的其思想,一次扔掉一半。
代碼:

public class Solution { public double findMedianSortedArrays(int[] nums1, int[] nums2) { int size1 = nums1.length; int size2 = nums2.length; int total = size1 + size2; if (total % 2 == 0) { return 0.5 * (helper(nums1, 0, nums2, 0, (total / 2) ) + helper(nums1, 0, nums2, 0, (total / 2) + 1)); } else { return helper(nums1, 0, nums2, 0, (total / 2) + 1); } } private static int helper (int[] nums1, int start1, int[] nums2, int start2, int k) { //note k denotes the kth number, not the index!!!! eg total == 3 -> k == 2 // if (start1 == nums1.length){ if (start1 >= nums1.length){ return nums2[start2 + k - 1]; } else if (start2 >= nums2.length) { return nums1[start1 + k - 1]; } else if (k == 1) { return Math.min(nums1[start1], nums2[start2]); } int a_key = start1 + k/2 <= nums1.length ? nums1[start1 + k / 2 - 1] : Integer.MAX_VALUE; int b_key = start2 + k/2 <= nums2.length ? nums2[start2 + k / 2 - 1] : Integer.MAX_VALUE; if (a_key > b_key) { return helper (nums1, start1, nums2,start2 + k/2, k - k/2); } else { return helper (nums1, start1 + k/2, nums2, start2, k - k/2); } } }
好多好多坑:
1. 通過比較兩個數組 a, b的第k/2個數,判斷是丟掉a的k/2還是b的k/2。 每次丟掉k/2個數,通過o(1)的操作。總之,隨小丟誰
2.兩個數組,怎么來丟掉k/2的部分,對於數組來說,其實只要更新一下起始坐標就好了。記住從哪個位置開始是有效的。注意,標記的是起始的坐標,注意index與第xx個的轉換。
3. 當a數組非常小,不夠k/2個怎么辦?
我們不能確保解不在a里面,但是能確保解不在b的k/2里面
那么我們就要扔掉b的前k/2個數,使用了maxValue的小技巧!!
4. 關於奇偶性的問題,總長度為奇數時候,下次層去找k - k/2個,比如 總個數是3, 只能扔掉1個,下一層去找前2個。
5. 遞歸的出口問題,helper函數里面有3組參數,針對每一組參數都有極限情況
a. A沒有數了
b. b沒有數了
c. k == 1
如果用下面這個判斷的話,會出現下面的錯誤 if (start1 == nums1.length)
--------------------------分割線-----------------------