LeetCode刷題記錄 解題思路-解題代碼-持續更新

目錄

兩數之和 - twoSum Ⅰ - - - - - 2019/09/24

題目描述:

給定一個整數數組 nums 和一個目標值 target,請你在該數組中找出和爲目標值的那 兩個 整數,並返回他們的數組下標。
你可以假設每種輸入只會對應一個答案。但是,你不能重複利用這個數組中同樣的元素。
給定 nums = [2, 7, 11, 15], target = 9

因爲 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

思路分析:
1.暴力循環,每個元素嵌套循環相加得到答案
此處寫的簡易代碼

for(int i = 0; i < nums.length; i++){
	for((int j = 0; j < nums.length; i++){
	}
}

2.參照一遍哈希表
複雜度分析:

時間複雜度:O(n)O(n),
我們只遍歷了包含有 nn 個元素的列表一次。在表中進行的每次查找只花費 O(1)O(1) 的時間。

空間複雜度:O(n)O(n),
所需的額外空間取決於哈希表中存儲的元素數量,該表最多需要存儲 nn 個元素。

代碼示例:

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] res = new int[2];
        Map<Integer,Integer> m = new HashMap<>();
        for (int i = 0; i < nums.length; i++){
            int j = target - nums[i];
            if(m.containsKey(nums[i])){
                res[0] =  m.get(nums[i]);
                res[1] =i;
            }
            m.put(j,i);
        }
        return res;
    }
}

結果:
在這裏插入圖片描述

兩數之和Ⅱ - twoSum Ⅱ- - - - - 2019/09/25

題目描述:

給定一個已按照升序排列 的有序數組,找到兩個數使得它們相加之和等於目標數。

函數應該返回這兩個下標值 index1 和 index2,其中 index1 必須小於 index2。

說明:

返回的下標值(index1 和 index2)不是從零開始的。
你可以假設每個輸入只對應唯一的答案,而且你不可以重複使用相同的元素。
示例:

輸入: numbers = [2, 7, 11, 15], target = 9
輸出: [1,2]
解釋: 2 與 7 之和等於目標數 9 。因此 index1 = 1, index2 = 2 。

思路分析:
一開始我想採用上面的寫法或者是暴力計算。
一開始的代碼如下

public int[] twoSum(int[] numbers, int target) {
	int[] res = new int[2];
	Map<Integer,Integer> m = new HashMap<>();
	for (int i = numbers.length-1 ;i >0; i-- ){
		int j = target - numbers[i];
		if(m.containsKey(numbers[i])){
			res[0] =  m.get(numbers[i]);
			res[1] =i;
			break;
		}
		m.put(j,i);
	}
	return res;

}

後來根據網上的思路點撥,發現採用指針碰撞應該是最優解

代碼示例:

public int[] twoSum(int[] numbers, int target) {
	int[] res = new int[2];
	int i = 0;
	int j = numbers.length - 1;
	while (i < j) {
		if (numbers[i] + numbers[j] == target) {
			res[0] = i + 1;
			res[1] = j + 1;
			break;
		} else {
			if (numbers[i] + numbers[j] < target) {
				i++;
			} else {
				j--;
			}
		}
	}
	return res;
}

結果:
在這裏插入圖片描述

兩數相加 - addTwoNumbers- - - - - 2019/09/27

題目描述:

給出兩個 非空 的鏈表用來表示兩個非負的整數。其中,它們各自的位數是按照 逆序 的方式存儲的,並且它們的每個節點只能存儲 一位 數字。

如果,我們將這兩個數相加起來,則會返回一個新的鏈表來表示它們的和。

您可以假設除了數字 0 之外,這兩個數都不會以 0 開頭。

示例:

輸入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
輸出:7 -> 0 -> 8
原因:342 + 465 = 807

解體思路:
1.轉爲數字相加後在轉爲鏈表(這個方法未實現,應該可行)
2.遞歸鏈表

   public ListNode addTwoNumbers1(ListNode l1, ListNode l2) {
        int i = l1.getVal() + l2.getVal();
        int j = 0;
        int k = i - 10;
        if(k >= 0){
            j=1;
        }else{
            k = i;
        }
        ListNode listNode  = new ListNode(k);
        this.aa(l1,l2,listNode,j);
        return listNode;
    }

    private void aa(ListNode l1, ListNode l2,ListNode l3,int i1){
        if(l1.getNext() != null || l2.getNext() != null){
            int i = l1.getNext().getVal() + l2.getNext().getVal() +i1;
            int j = 0;
            int k = i - 10;
            if(k >= 0){
                j=1;
            }else{
                k = i;
            }
            ListNode listNode  = new ListNode(j);
            l3.setNext(listNode);
            this.aa(l1.getNext(),l2.getNext(),listNode,j);
        }
    }

此處的代碼有錯誤,僅僅作爲思路參考.

官方解題的仿照代碼:

public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
    ListNode listNode = new ListNode(0);
    ListNode cur =listNode;
    int car = 0;
    while (l1 != null || l2!=null){
        int x = (l1 != null) ? l1.val : 0;
        int y = (l2 != null) ? l2.val : 0;

        int total = x + y + car;
        car = total / 10;

        cur.next = new ListNode(total%10);
        cur =  cur.next;

        if (l1 != null){
            l1 = l1.next;
        }
        if (l2 != null){
            l2 = l2.next;
        }
        if(car > 0){
            cur.next = new ListNode(car);
        }
    }

    return listNode.next;
}

說明:
在思想上中間數和官方思路一樣,最大的差異實在對鏈表的操作上,其實我的遞歸方法和他的while 沒有什麼不一樣.本質上個人認爲,是自己對於鏈表的不熟悉纔會使用集合的方式來操作鏈表,導致解題思路的誤解

就像你在紙上計算兩個數字的和那樣,我們首先從最低有效位也就是列表 l1l1 和 l2l2 的表頭開始相加。由於每位數字都應當處於 0 \ldots 909 的範圍內,我們計算兩個數字的和時可能會出現 “溢出”。
例如,5 + 7 = 125+7=12。在這種情況下,我們會將當前位的數值設置爲 22,並將進位 carry = 1carry=1 帶入下一次迭代。
進位 carrycarry 必定是 0011,這是因爲兩個數字相加(考慮到進位)可能出現的最大和爲 9 + 9 + 1 = 199+9+1=19。

僞代碼如下:

將當前結點初始化爲返回列表的啞結點。
將進位 carrycarry 初始化爲 00。
將 pp 和 qq 分別初始化爲列表 l1l1 和 l2l2 的頭部。
遍歷列表 l1l1 和 l2l2 直至到達它們的尾端。
將 xx 設爲結點 pp 的值。如果 pp 已經到達 l1l1 的末尾,則將其值設置爲 00。
將 yy 設爲結點 qq 的值。如果 qq 已經到達 l2l2 的末尾,則將其值設置爲 00。
設定 sum = x + y + carrysum=x+y+carry。
更新進位的值,carry = sum / 10carry=sum/10。
創建一個數值爲 (sum \bmod 10)(summod10) 的新結點,並將其設置爲當前結點的下一個結點,然後將當前結點前進到下一個結點。
同時,將 pp 和 qq 前進到下一個結點。
檢查 carry = 1carry=1 是否成立,如果成立,則向返回列表追加一個含有數字 11 的新結點。
返回啞結點的下一個結點。
請注意,我們使用啞結點來簡化代碼。如果沒有啞結點,則必須編寫額外的條件語句來初始化表頭的值。

提交結果:
在這裏插入圖片描述

無重複字符的最長子串 lengthOfLongestSubstring- - - - - 2019/09/28

給定一個字符串,請你找出其中不含有重複字符的 最長子串 的長度。

示例 1:

輸入: "abcabcbb"
輸出: 3 
解釋: 因爲無重複字符的最長子串是 "abc",所以其長度爲 3。
示例 2:

輸入: "bbbbb"
輸出: 1
解釋: 因爲無重複字符的最長子串是 "b",所以其長度爲 1。
示例 3:

輸入: "pwwkew"
輸出: 3
解釋: 因爲無重複字符的最長子串是 "wke",所以其長度爲 3。
     請注意,你的答案必須是 子串 的長度,"pwke" 是一個子序列,不是子串。

自己的解題思路:
寫map判斷是否有重複的,用下標來計算長度.
後在操作中發現下標應該注意+1;
自己寫的代碼如下:

public static int lengthOfLongestSubstring(String s) {
	char[] chars = s.toCharArray();
	Map<String, Integer> map = new HashMap<>();
	int most = 0;
	int cur = 0;
	for (int i = 0; i < chars.length; i++) {
		if(map.containsKey(String.valueOf(chars[i]))){
			Integer integer = map.get(String.valueOf(chars[i]));
			int i1 = i - integer;
			if(i1 > cur){
				most = i1 -1;
			}
			map.put(String.valueOf(chars[i]),i);
			cur = 0;
		}else{
			map.put(String.valueOf(chars[i]),i);
			cur++;
			if(most < cur){
				most = cur;
			}
		}

	}

	return most;
}

思路說明:

通過使用 HashSet 作爲滑動窗口,我們可以用 O(1)O(1) 的時間來完成對字符是否在當前的子字符串中的檢查。

滑動窗口是數組/字符串問題中常用的抽象概念。 窗口通常是在數組/字符串中由開始和結束索引定義的一系列元素的集合,
即 [i, j)[i,j)(左閉,右開)。
而滑動窗口是可以將兩個邊界向某一方向“滑動”的窗口。例如,我們將 [i, j)[i,j) 向右滑動 11 個元素,
則它將變爲 [i+1, j+1)[i+1,j+1)(左閉,右開)。

回到我們的問題,我們使用 HashSet 將字符存儲在當前窗口 [i, j)[i,j)(最初 j = ij=i)中。
 然後我們向右側滑動索引 jj,如果它不在 HashSet 中,我們會繼續滑動 jj。
直到 s[j] 已經存在於 HashSet 中。此時,我們找到的沒有重複字符的最長子字符串將會以索引 ii 開頭。
如果我們對所有的 ii 這樣做,就可以得到答案。

我一直被卡在下標計算上…後面參考了標準答案發現…是我的中位數沒有利用好
以下是修改過的代碼

    public int lengthOfLongestSubstring(String s) {
		char[] chars = s.toCharArray();
		Map<String, Integer> map = new HashMap<>();
		int most = 0;
		int cur = 0;
		for (int i = 0; i < chars.length; i++) {
			if(map.containsKey(String.valueOf(chars[i]))){
				cur = Math.max(map.get(String.valueOf(chars[i])), cur);
			}
			most = Math.max(most, i - cur + 1);
			map.put(String.valueOf(chars[i]),i+1);
		}
		return most;
   }

提交結果:
在這裏插入圖片描述

獨一無二的出現次數(156周競賽題) uniqueOccurrences- - - 2019/09/29

給你一個整數數組 arr,請你幫忙統計數組中每個數的出現次數。

如果每個數的出現次數都是獨一無二的,就返回 true;否則返回 false。

 

示例 1:

輸入:arr = [1,2,2,1,1,3]
輸出:true
解釋:在該數組中,1 出現了 3 次,2 出現了 2 次,3 只出現了 1 次。沒有兩個數的出現次數相同。
示例 2:

輸入:arr = [1,2]
輸出:false
示例 3:

輸入:arr = [-3,0,1,-3,1,1,1,-3,10,0]
輸出:true
 

提示:

1 <= arr.length <= 1000
-1000 <= arr[i] <= 1000

思路:這道題很簡單.這裏說明一下,可以用排重的方法也可以用排序的方法做.
因爲當時是趕時間做的,所以下面的自己寫的代碼會有一些不足和沒有考慮到的地方.
但是這個代碼是可以通過的,這裏貼下代碼,更優秀的解法希望大家能夠自己試一下.

自己的代碼:

	public boolean uniqueOccurrences(int[] arr) {
		Map<Integer, Integer> map = new HashMap<>();
		Set<Integer> set = new HashSet<>();
		for (int i = 0; i < arr.length; i++) {
			if (map.containsKey(arr[i])) {
				int j = map.get(arr[i]) + 1;
				map.put(arr[i], j);
			} else {
				map.put(arr[i], 1);
			}
		}
		AtomicReference<Boolean> f = new AtomicReference<>(true);
		map.forEach((k, v) -> {
			if (set.contains(v)) {
				f.set(false);
				return;
			} else {
				set.add(v);
			}
		});
		return f.get();
	}

此題競賽第一名的實現代碼:

    bool uniqueOccurrences(vector<int>& arr) {
        int s[2005];
        memset(s,0,sizeof(s));
        for(auto i:arr)s[i+1000]++;
        sort(s,s+2005);
        for(int i=0;i<2004;i++)if(s[i]&&s[i+1]==s[i])return 0;
        return 1;
    }

尋找兩個有序數組的中位數(困難題) findMedianSortedArrays- - - 2019/09/30

題目描述:
給定兩個大小爲 m 和 n 的有序數組 nums1 和 nums2。

請你找出這兩個有序數組的中位數,並且要求算法的時間複雜度爲 O(log(m + n))。

你可以假設 nums1 和 nums2 不會同時爲空。

示例 1:

nums1 = [1, 3]
nums2 = [2]

則中位數是 2.0
示例 2:

nums1 = [1, 2]
nums2 = [3, 4]

則中位數是 (2 + 3)/2 = 2.5

難點

時間複雜度是log(m+n),什麼意思呢,意思這裏起碼要用二分法來達到這個複雜度要求

以下爲集中解題思路:

1.暴力解題:
兩個數組合併爲一個,然後排序求出中位數.

時間複雜度:遍歷全部數組 (m+n)(m+n)

空間複雜度:開闢了一個數組,保存合併後的兩個數組 O(m+n)O(m+n)
代碼如下,代碼中其實還要是遍歷的,只不過使用了了工具類

public double findMedianSortedArrays(int[] nums1, int[] nums2) {
		int nums3[]=null;
		nums3 =new int[nums1.length+nums2.length];
		System.arraycopy(nums1,0,nums3,0,nums1.length);
		System.arraycopy(nums2,0,nums3,nums1.length,nums2.length);
		Arrays.sort(nums3);
		double i = 0;
		if(nums3.length%2  > 0){
			 i = nums3[nums3.length / 2];
		}else {
			i = (nums3[nums3.length / 2] + nums3[nums3.length / 2 -1])/ 2.0;
		}
		return i;
	}

下面是java中的快速排序寫法.粗略的看了一下似乎是滑窗排序非極端情況應該小於(m+n)

  /**
     * Sorts the specified range of the array using the given
     * workspace array slice if possible for merging
     *
     * @param a the array to be sorted
     * @param left the index of the first element, inclusive, to be sorted
     * @param right the index of the last element, inclusive, to be sorted
     * @param work a workspace array (slice)
     * @param workBase origin of usable space in work array
     * @param workLen usable size of work array
     */
    static void sort(int[] a, int left, int right,
                     int[] work, int workBase, int workLen) {
        // Use Quicksort on small arrays
        if (right - left < QUICKSORT_THRESHOLD) {
            sort(a, left, right, true);
            return;
        }

        /*
         * Index run[i] is the start of i-th run
         * (ascending or descending sequence).
         */
        int[] run = new int[MAX_RUN_COUNT + 1];
        int count = 0; run[0] = left;

        // Check if the array is nearly sorted
        for (int k = left; k < right; run[count] = k) {
            // Equal items in the beginning of the sequence
            while (k < right && a[k] == a[k + 1])
                k++;
            if (k == right) break;  // Sequence finishes with equal items
            if (a[k] < a[k + 1]) { // ascending
                while (++k <= right && a[k - 1] <= a[k]);
            } else if (a[k] > a[k + 1]) { // descending
                while (++k <= right && a[k - 1] >= a[k]);
                // Transform into an ascending sequence
                for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) {
                    int t = a[lo]; a[lo] = a[hi]; a[hi] = t;
                }
            }

            // Merge a transformed descending sequence followed by an
            // ascending sequence
            if (run[count] > left && a[run[count]] >= a[run[count] - 1]) {
                count--;
            }

            /*
             * The array is not highly structured,
             * use Quicksort instead of merge sort.
             */
            if (++count == MAX_RUN_COUNT) {
                sort(a, left, right, true);
                return;
            }
        }

        // These invariants should hold true:
        //    run[0] = 0
        //    run[<last>] = right + 1; (terminator)

        if (count == 0) {
            // A single equal run
            return;
        } else if (count == 1 && run[count] > right) {
            // Either a single ascending or a transformed descending run.
            // Always check that a final run is a proper terminator, otherwise
            // we have an unterminated trailing run, to handle downstream.
            return;
        }
        right++;
        if (run[count] < right) {
            // Corner case: the final run is not a terminator. This may happen
            // if a final run is an equals run, or there is a single-element run
            // at the end. Fix up by adding a proper terminator at the end.
            // Note that we terminate with (right + 1), incremented earlier.
            run[++count] = right;
        }

        // Determine alternation base for merge
        byte odd = 0;
        for (int n = 1; (n <<= 1) < count; odd ^= 1);

        // Use or create temporary array b for merging
        int[] b;                 // temp array; alternates with a
        int ao, bo;              // array offsets from 'left'
        int blen = right - left; // space needed for b
        if (work == null || workLen < blen || workBase + blen > work.length) {
            work = new int[blen];
            workBase = 0;
        }
        if (odd == 0) {
            System.arraycopy(a, left, work, workBase, blen);
            b = a;
            bo = 0;
            a = work;
            ao = workBase - left;
        } else {
            b = work;
            ao = 0;
            bo = workBase - left;
        }

        // Merging
        for (int last; count > 1; count = last) {
            for (int k = (last = 0) + 2; k <= count; k += 2) {
                int hi = run[k], mi = run[k - 1];
                for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) {
                    if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) {
                        b[i + bo] = a[p++ + ao];
                    } else {
                        b[i + bo] = a[q++ + ao];
                    }
                }
                run[++last] = hi;
            }
            if ((count & 1) != 0) {
                for (int i = right, lo = run[count - 1]; --i >= lo;
                    b[i + bo] = a[i + ao]
                );
                run[++last] = right;
            }
            int[] t = a; a = b; b = t;
            int o = ao; ao = bo; bo = o;
        }
    }

執行結果:
在這裏插入圖片描述

2.割和第k個元素

一個數組 對於一個有序數組,對於數組A,如果在k的位置割(Cut)一下(不是割(Cut)在兩數中間),那麼 LMax = RMin =
A[k],

兩個數組 也就是我們題目的狀態,我們要求得兩個數組合併成一個有序數組時,第k位的元素

我們設: Ci爲第i個數組的割。

LMaxi爲第i個數組割後的左元素.

RMini爲第i個數組割後的右元素。

首先,LMax1<=RMin1,LMax2<=RMin2
這是肯定的,因爲數組是有序的,左邊肯定小於右邊!,而如果割(Cut)在某個數上,則左右相等。

其次,如果我們讓LMax1<=RMin2,LMax2<=RMin1 呢

那麼如果左半邊全小於右半邊,如果左邊的元素個數相加剛好等於k, 那麼第k個元素就是Max(LMax1, LMax2),這個比較好理解的,因爲Max(LMax1,
LMax2)肯定是左邊k個元素的最大值,因爲合併後的數組是有序,第k個元素肯定前面k個元素中最大的那個。

那麼如果
LMax1>RMin2,說明數組1的左邊元素太大(多),我們把C1減小,C2=k-C1也就相應的增大。LMax2>RMin1同理,把C2減小,C1=k-C2也就相應的增大。

假設k=3

對於

[2 3 5]

[1 4 7 9] 設C1 = 1, 那麼C2 = k - C1 = 2

[2 / 3 5]

[1 4 / 7 9]

這時LMax1 =2, RMin1 = 3, LMax2=4, RMin2=7,

從而有LMax2 > RMin1,依據前面的推論,我們要將C1增大,所以我們讓C1 = 2,如下:

[2 3 /5]

[1 / 4 7 9]

這時LMax1 =3, RMin1 = 5, LMax2=1, RMin2=4, 滿足 LMax1 < RMin2 且 LMax2 <
RMin1 , 所以第3個元素爲Max(LMax1,LMax2) = 3

兩個數組的最大問題是,它們合併後,m+n總數可能爲奇, 也可能爲偶,所以我們得想法讓m+n總是爲偶數

通過虛擬加入‘#’,我們讓m轉換成2m+1 ,n轉換成2n+1, 兩數之和就變成了2m+2n+2,恆爲偶數。

注意是虛擬加,其實根本沒這一步,通過下面的轉換,我們可以保證虛擬加後每個元素跟原來的元素一一對應

這麼虛擬加後,每個位置可以通過/2得到原來元素的位置:

比如 2,原來在0位,現在是1位,1/2=0

比如 3,原來在1位,現在是3位,3/2=1

比如 5,原來在2位,現在是5位,5/2=2

比如 9,原來在3位,現在是7位,7/2=3

而對於割(Cut),如果割在‘#’上等於割在2個元素之間,割在數字上等於把數字劃到2個部分,總是有以下成立:

LMaxi = (Ci-1)/2 位置上的元素 RMini = Ci/2 位置上的元素

例如:

割在3上,C = 3,LMax=a[(3-1)/2]=A[1],RMin=a[3/2] =A[1],剛好都是3的位置!

割在4/7之間‘#’,C = 4,LMax=A[(4-1)/2]=A[1]=4 ,RMin=A[4/2]=A[2]=7

剩下的事情就好辦了,把2個數組看做一個虛擬的數組A,A有2m+2n+2個元素,割在m+n+1處,所以我們只需找到m+n+1位置的元素和m+n+2位置的元素就行了。

左邊:A[m+n+1] = Max(LMax1,LMax2)

右邊:A[m+n+2] = Min(RMin1,RMin2)

==>Mid = (A[m+n+1]+A[m+n+2])/2 = (Max(LMax1,LMax2) + Min(RMin1,RMin2) )/2

最快的割(Cut)是使用二分法,

有2個數組,我們對哪個做二分呢?
根據之前的分析,我們知道了,只要C1或C2確定,另外一個也就確定了。這裏,爲了效率,我們肯定是選長度較短的做二分,假設爲C1。

LMax1>RMin2,把C1減小,C2增大。—> C1向左二分

LMax2>RMin1,把C1增大,C2減小。—> C1向右二分

如果C1或C2已經到頭了怎麼辦?

這種情況出現在:如果有個數組完全小於或大於中值。假定n<m, 可能有4種情況:

C1 = 0 —— 數組1整體都在右邊了,所以都比中值大,中值在數組2中,簡單的說就是數組1割後的左邊是空了,所以我們可以假定LMax1 =
INT_MIN

C1 =2n —— 數組1整體都在左邊了,所以都比中值小,中值在數組2中 ,簡單的說就是數組1割後的右邊是空了,所以我們可以假定RMin1=
INT_MAX,來保證LMax2<RMin1恆成立

C2 = 0 —— 數組2整體在右邊了,所以都比中值大,中值在數組1中 ,簡單的說就是數組2割後的左邊是空了,所以我們可以假定LMax2 =
INT_MIN

C2 = 2m —— 數組2整體在左邊了,所以都比中值小,中值在數組1中, 簡單的說就是數組2割後的右邊是空了,爲了讓LMax1 <
RMin2 恆成立,我們可以假定RMin2 = INT_MAX

以上是一些數學概念和思路.按我按個人理解就是
int j = (m + n + 1) / 2 - i;
這個公式能獲取到一個數組的中位下標.
那麼我只需要匹配到符合這個中位下標的計算方式的值,那麼就是中位數,然後在通過總長度來計算中位數

以下是代碼:

public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    if (nums1.length > nums2.length) {
        return findMedianSortedArrays(nums2, nums1);
    }
    int m = nums1.length;
    int n = nums2.length;
    int iMin = 0, iMax = nums1.length;
    while (iMin <= iMax) {
        int i = (iMin + iMax) / 2;
        int j = (m + n + 1) / 2 - i;
        if (j != 0 && i != m && nums2[j - 1] > nums1[i]) { // i 需要增大
            iMin = i + 1;
        } else if (i != 0 && j != n && nums1[i - 1] > nums2[j]) { // i 需要減小
            iMax = i - 1;
        } else { // 達到要求,並且將邊界條件列出來單獨考慮
            int maxLeft = 0;
            if (i == 0) {
                maxLeft = nums2[j - 1];
            } else if (j == 0) {
                maxLeft = nums1[i - 1];
            } else {
                maxLeft = Math.max(nums1[i - 1], nums2[j - 1]);
            }
            if ((m + n) % 2 == 1) {
                return maxLeft;
            } // 奇數的話不需要考慮右半部分

            int minRight = 0;
            if (i == m) {
                minRight = nums2[j];
            } else if (j == n) {
                minRight = nums1[i];
            } else {
                minRight = Math.min(nums2[j], nums1[i]);
            }

            return (maxLeft + minRight) / 2.0; //如果是偶數的話返回結果
        }
    }
    return 1f;
}

執行結果:
在這裏插入圖片描述

明顯看出,暴力解法消耗的資源和時間是比較多的

組合兩個表 sql- - - 2019/09/30

表1: Person

+-------------+---------+
| 列名         | 類型     |
+-------------+---------+
| PersonId    | int     |
| FirstName   | varchar |
| LastName    | varchar |
+-------------+---------+
PersonId 是上表主鍵
表2: Address

+-------------+---------+
| 列名         | 類型    |
+-------------+---------+
| AddressId   | int     |
| PersonId    | int     |
| City        | varchar |
| State       | varchar |
+-------------+---------+
AddressId 是上表主鍵
 

編寫一個 SQL 查詢,滿足條件:無論 person 是否有地址信息,都需要基於上述兩表提供 person 的以下信息:

 FirstName, LastName, City, State

自己寫的sql:

select p.FirstName,p.LastName,a.City,a.State from Person p left join Address a on a.PersonId  = p.PersonId

結果:
在這裏插入圖片描述

第二高的薪水 sql- - - 2019/09/30

編寫一個 SQL 查詢,獲取 Employee 表中第二高的薪水(Salary) 。

+----+--------+
| Id | Salary |
+----+--------+
| 1  | 100    |
| 2  | 200    |
| 3  | 300    |
+----+--------+
例如上述 Employee 表,SQL查詢應該返回 200 作爲第二高的薪水。如果不存在第二高的薪水,那麼查詢應返回 null。

+---------------------+
| SecondHighestSalary |
+---------------------+
| 200                 |
+---------------------+

代碼

select max(Salary) SecondHighestSalary from Employee where Salary <>(select max(Salary) from Employee );

結果:
在這裏插入圖片描述

第N高的薪水 sql- - - 2019/10/01

題目描述:

編寫一個 SQL 查詢,獲取 Employee 表中第 n 高的薪水(Salary)。

+----+--------+
| Id | Salary |
+----+--------+
| 1  | 100    |
| 2  | 200    |
| 3  | 300    |
+----+--------+
例如上述 Employee 表,n = 2 時,應返回第二高的薪水 200。如果不存在第 n 高的薪水,那麼查詢應返回 null。

+------------------------+
| getNthHighestSalary(2) |
+------------------------+
| 200                    |
+------------------------+

解題思路:
這一題的重點應該在於limit的取值範圍和null處理,那麼基本sql和上一題類似,那麼我們可以改造一下
以下是代碼:

CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
    DECLARE P1 INT; -- 第P1高的薪水
    DECLARE P2 INT; -- 取P1-1後的P2個值
    -- 當N<1時,P1會爲負數,採用IF調整爲0,另此時結果不存在,設置P2爲0
    IF (N<1)
      THEN SET P1 = 0, P2 = 0;
    ELSE SET P1 = N-1, P2 = 1;
    END IF;
  RETURN (
      # Write your MySQL query statement below.
      select IFNULL((
      select DISTINCT Salary as getNthHighestSalary from Employee
      order by Salary desc limit P1,P2),null)  
  );
END

運行結果:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章