LeetCode刷题记录 解题思路-解题代码-持续更新

LeetCode刷题记录 解题思路-解题代码-持续更新

    • 目录
    • 两数之和 - twoSum Ⅰ - - - - - 2019/09/24
    • 两数之和Ⅱ - twoSum Ⅱ- - - - - 2019/09/25
    • 两数相加 - addTwoNumbers- - - - - 2019/09/27
    • 无重复字符的最长子串 lengthOfLongestSubstring- - - - - 2019/09/28
    • 独一无二的出现次数(156周竞赛题) uniqueOccurrences- - - 2019/09/29
    • 寻找两个有序数组的中位数(困难题) findMedianSortedArrays- - - 2019/09/30
        • 难点
    • 组合两个表 sql- - - 2019/09/30
    • 第二高的薪水 sql- - - 2019/09/30
    • 第N高的薪水 sql- - - 2019/10/01

目录

两数之和 - 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;
    }
}

结果:
LeetCode刷题记录 解题思路-解题代码-持续更新_第1张图片

两数之和Ⅱ - 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;
}

结果:
LeetCode刷题记录 解题思路-解题代码-持续更新_第2张图片

两数相加 - 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 的新结点。
返回哑结点的下一个结点。
请注意,我们使用哑结点来简化代码。如果没有哑结点,则必须编写额外的条件语句来初始化表头的值。

提交结果:
LeetCode刷题记录 解题思路-解题代码-持续更新_第3张图片

无重复字符的最长子串 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;
   }

提交结果:
LeetCode刷题记录 解题思路-解题代码-持续更新_第4张图片

独一无二的出现次数(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[] = 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;
        }
    }

执行结果:
LeetCode刷题记录 解题思路-解题代码-持续更新_第5张图片

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

C1 = 0 —— 数组1整体都在右边了,所以都比中值大,中值在数组2中,简单的说就是数组1割后的左边是空了,所以我们可以假定LMax1 =
INT_MIN

C1 =2n —— 数组1整体都在左边了,所以都比中值小,中值在数组2中 ,简单的说就是数组1割后的右边是空了,所以我们可以假定RMin1=
INT_MAX,来保证LMax2

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;
}

执行结果:
LeetCode刷题记录 解题思路-解题代码-持续更新_第6张图片

明显看出,暴力解法消耗的资源和时间是比较多的

组合两个表 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 );

结果:
LeetCode刷题记录 解题思路-解题代码-持续更新_第7张图片

第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

运行结果:
LeetCode刷题记录 解题思路-解题代码-持续更新_第8张图片

你可能感兴趣的:(LeetCode刷题记录 解题思路-解题代码-持续更新)