题目描述:
给定一个整数数组 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;
}
}
题目描述:
给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 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;
}
题目描述:
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 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 90…9 的范围内,我们计算两个数字的和时可能会出现 “溢出”。
例如,5 + 7 = 125+7=12。在这种情况下,我们会将当前位的数值设置为 22,并将进位 carry = 1carry=1 带入下一次迭代。
进位 carrycarry 必定是 00 或 11,这是因为两个数字相加(考虑到进位)可能出现的最大和为 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 的新结点。
返回哑结点的下一个结点。
请注意,我们使用哑结点来简化代码。如果没有哑结点,则必须编写额外的条件语句来初始化表头的值。
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 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;
}
给你一个整数数组 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;
}
题目描述:
给定两个大小为 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;
}
}
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_MINC1 =2n —— 数组1整体都在左边了,所以都比中值小,中值在数组2中 ,简单的说就是数组1割后的右边是空了,所以我们可以假定RMin1=
INT_MAX,来保证LMax2C2 = 0 —— 数组2整体在右边了,所以都比中值大,中值在数组1中 ,简单的说就是数组2割后的左边是空了,所以我们可以假定LMax2 =
INT_MINC2 = 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;
}
明显看出,暴力解法消耗的资源和时间是比较多的
表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 查询,获取 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 );
题目描述:
编写一个 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