今天计划先完成昨天未完成的SQL在线编程题
由于在上一篇博客当中已经给出了所有表的创建语句,这里就不再重复说明了,我们就直接开始吧!
光速打脸现场,下面给出表的定义:
CREATE TABLE `departments` ( // 部门表
`dept_no` char(4) NOT NULL, // 部门号
`dept_name` varchar(40) NOT NULL, // 部门名
PRIMARY KEY (`dept_no`)); // 主键(部门号)
CREATE TABLE `dept_emp` ( // 部门_员工表
`emp_no` int(11) NOT NULL, // 员工号
`dept_no` char(4) NOT NULL, // 部门号
`from_date` date NOT NULL, // 起始日期
`to_date` date NOT NULL, // 结束日期
PRIMARY KEY (`emp_no`,`dept_no`)); // 主键(员工号, 部门号)
CREATE TABLE `employees` ( // 员工表
`emp_no` int(11) NOT NULL, // 员工号
`birth_date` date NOT NULL, // 出生日期
`first_name` varchar(14) NOT NULL, // 名
`last_name` varchar(16) NOT NULL, // 姓
`gender` char(1) NOT NULL, // 性别
`hire_date` date NOT NULL, // 入职日期
PRIMARY KEY (`emp_no`)); // 主键(员工号)
CREATE TABLE `salaries` ( // 薪水详情表
`emp_no` int(11) NOT NULL, // 员工号
`salary` int(11) NOT NULL, // 工资
`from_date` date NOT NULL, // 起始日期
`to_date` date NOT NULL, // 截止日期
PRIMARY KEY (`emp_no`,`from_date`));// 主键(员工号, 起始日期)
- 查找所有员工的last_name和first_name以及对应的dept_name,也包括暂时没有分配部门的员工
select employees.last_name, employees.first_name, departments.dept_name
from employees left join dept_emp on employees.emp_no = dept_emp.emp_no
left join departments on dept_emp.dept_no = departments.dept_no;
此题考察了left join左连接的用法,根据查询需求,包括暂时没有分配部门的员工,因此需要采用两次左连接才能满足需求,这道题是第一个三表关联查询,同样的理清思路即可。
- 查找员工编号emp_no为10001其自入职以来的薪水salary涨幅值growth
// 直观思路
select max(salary)-min(salary)
from salaries
where emp_no = 10001;
// 严谨思路
select
(select salary from salaries where emp_no = 10001 order by from_date desc limit 1) -
(select salary from salaries where emp_no = 10001 order by from_date asc limit 1)
这道题本身并不难,但是从中学习到的是,我们在分析一个查询需求的时候,一定要认真分析,如果没有仔细斟酌,便会产生第一种直观思路的解法,这种方法虽然通过了调试,但是严谨来说其逻辑并不是对的,因为我们并不知道员工的薪水详情表中对于一个员工,每次薪水的调整是否一定为涨薪,若最后一次薪水调整为降薪,那么第一种思路便是错误的,题目的意思显然是要给出员工从刚入职到现在,薪水的涨幅。
- 查找所有员工自入职以来的薪水涨幅情况,给出员工编号emp_no以及其对应的薪水涨幅growth,并按照growth进行升序
select sCurrent.emp_no, (sCurrent.salary - sStart.salary) as growth
from (select s.emp_no, s.salary from employees e, salaries s where e.emp_no = s.emp_no and s.to_date = '9999-01-01') AS sCurrent,
(select s.emp_no, s.salary from employees e, salaries s where e.emp_no = s.emp_no and s.from_date = e.hire_date) AS sStart
where sCurrent.emp_no = sStart.emp_no
order by growth
这道题稍微有点复杂,在上一题的基础上复杂程度增大了许多,首先需要查找所有员工自入职以来的薪水涨幅情况,一开始的想法是通过根据emp_no字段进行分组,后来由于必须将结果按照growth进行升序,因此这个方案不可行。后面采用两个子查询的结果表,作为父查询的查询表,第一个子查询获取了所有员工的员工号以及当前的薪水,第二个子查询获取了所有员工的员工号以及入职时的薪水,父查询只需将这两个子查询结果表以emp_no为关联字段相连接,即可得到每一个员工的入职与当前薪水信息。这道题挺具有参考意义的,没事可以多看看。
- 统计各个部门对应员工涨幅的次数总和,给出部门编码dept_no、部门名称dept_name以及次数sum
select d.dept_no, dept.dept_name, count(salary)
from salaries s, dept_emp d, departments dept
where s.emp_no = d.emp_no and d.dept_no=dept.dept_no
group by dept.dept_no
简单的group by语句与count函数的共同使用,如上述SQL语句,没有什么好解释的了。
- 对所有员工的当前(to_date='9999-01-01')薪水按照salary进行按照1-N的排名,相同salary并列且按照emp_no升序排列
select s1.emp_no, s1.salary, count(distinct s2.salary) as rank
from salaries as s1, salaries as s2
where s1.to_date = '9999-01-01' and s2.to_date = '9999-01-01' and s1.salary <= s2.salary
group by s1.emp_no
order by s1.salary desc, s1.emp_no asc
这道题炸了,主要考察的还是相关子查询,应该如果可以写出这道题,前面的题应该都不在话下,当时分析的时候始终没有思路,最后还是看大佬的代码才懂了,这里直接贴出大佬对这题的分析好了,划重点!!!
- 从两张相同的salaries表(分别为s1与s2)进行对比分析,先将两表限定条件设为to_date = '9999-01-01',挑选出当前所有员工的薪水情况。
- 本题的精髓在于 s1.salary <= s2.salary,意思是在输出s1.salary的情况下,有多少个s2.salary大于等于s1.salary,比如当s1.salary=94409时,有3个s2.salary(分别为94692,94409,94409)大于等于它,但由于94409重复,利用COUNT(DISTINCT s2.salary)去重可得工资为94409的rank等于2。其余排名以此类推。
- 千万不要忘了GROUP BY s1.emp_no,否则输出的记录只有一条(可能是第一条或者最后一条,根据不同的数据库而定),因为用了合计函数COUNT()
- 最后先以 s1.salary 逆序排列,再以 s1.emp_no 顺序排列输出结果
- 获取所有非manager员工当前的薪水情况,给出dept_no、emp_no以及salary ,当前表示to_date='9999-01-01'
select dept_emp.dept_no, employees.emp_no, salaries.salary
from employees left join dept_manager on employees.emp_no = dept_manager.emp_no, dept_emp, salaries
where dept_manager.emp_no is null
and employees.emp_no = dept_emp.emp_no
and employees.emp_no = salaries.emp_no
and dept_emp.to_date = '9999-01-01'
and salaries.to_date = '9999-01-01'
这道题也是第一次就过了,这里一开始我的思路便是通过left join左连接以及后面的where语句中增加条件dept_manager.emp_no is null,从而筛选出非manager员工,当然还可以通过子查询获取manager表中当前所有员工的员工号信息,然后在where语句中使用not in语法,同样可以达到目的。
用两个栈实现队列
扫描二维码关注公众号,回复: 8574556 查看本文章
class Solution
{
public:
void push(int node) {
stack1.push(node);
}
int pop() {
if(stack2.empty()){
while(stack1.size() > 0){
int data = stack1.top();
stack1.pop();
stack2.push(data);
}
}
int pop_element = stack2.top();
stack2.pop();
return pop_element;
}
private:
stack<int> stack1;
stack<int> stack2;
};
调整数组顺序使奇数位于偶数前面
void myReOrderArray(vector<int> &array) // 插入排序思想,空间复杂度O(1),时间复杂度O(n²)
{
int size = array.size();
for (int i = 1; i < size; ++i)
{
if (array[i] & 1) // 如果array[i]为奇数的话
{
int value = array[i];
int j;
for (j = i - 1; j >= 0; --j)
{
if (array[j] % 2 == 0) // 如果array[j]为偶数的话
{
array[j + 1] = array[j];
}
else
break;
}
array[j + 1] = value;
}
}
}
连续子数组的的最大和
int FindGreatestSumOfSubArray(vector<int> array)
{
int size = array.size();
if (size == 0) // 边界检测
return 0;
int maxSum = array[0]; // 初始化最大和为array[0]
int curSum = array[0]; // 初始化当前和为array[0]
for (int i = 1; i < size; ++i)
{
// 若当前和小于等于0,抛弃当前和,重新计算
if (curSum <= 0)
curSum = array[i];
// 若当前和大于0,直接累加
else
curSum += array[i];
// 更新最大和
if (curSum > maxSum)
maxSum = curSum;
}
return maxSum;
}
链表中倒数第k个结点
ListNode* FindKthToTail(ListNode* pListHead, unsigned k)
{
if (pListHead == NULL || k == 0) // 边界条件测试
{
return NULL;
}
ListNode *quick = pListHead, *slow = pListHead; // 初始化快慢指针(指向链表首结点)
for (int i = 1; i < k; ++i) // 快指针先行k-1步
{
if (quick->next != NULL)
quick = quick->next;
else
return NULL;
}
while (quick->next != NULL) // 快慢指针同时移动,相聚k-1个结点
{
quick = quick->next;
slow = slow->next;
}
return slow;
}
反转单链表
struct ListNode
{
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) { }
};
ListNode* ReverseList(ListNode* pHead) {
ListNode* pReversedHead = NULL; // 反向链表的头结点
ListNode* pNode = pHead; // 当前处理结点
ListNode* pPrev = NULL; // 上一个处理的结点
while (pNode != NULL) {
ListNode* pNext = pNode->next; // pNext保存下一个要处理的结点
if (pNext == NULL) { // 若pNext为空,表示当前处理的结点pNode为最后一个结点
pReversedHead = pNode;
}
pNode->next = pPrev; // 当前处理结点链接到上一个处理结点的前面
pPrev = pNode; // 重置上一个处理结点pPrev
pNode = pNext; // 重置pNode为下一个要处理的结点
}
return pReversedHead;
}
合并两个排序的链表
struct ListNode
{
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) { }
};
ListNode* Merge(ListNode *pHead1, ListNode *pHead2)
{
if (pHead1 == NULL) // 判断两个子链表是否为空(递归终止条件)
{
return pHead2;
}
else if (pHead2 == NULL)
{
return pHead1;
}
ListNode *head = NULL; // 初始化合并后的链表的头结点head
// 确定当前头结点,递归求得子链表头结点,并相连
if (pHead1->val <= pHead2->val)
{
head = pHead1;
head->next = Merge(pHead1->next, pHead2);
}
else
{
head = pHead2;
head->next = Merge(pHead1, pHead2->next);
}
return head; // 返回最终合并后的链表头结点
}
ListNode* myMerge(ListNode *pHead1, ListNode *pHead2)
{
if (pHead1 == NULL) // 判断两个子链表是否为空(递归终止条件)
{
return pHead2;
}
else if (pHead2 == NULL)
{
return pHead1;
}
ListNode *mergeHead = NULL; // 初始化合并链表的头结点以及正在处理的结点
ListNode *current = NULL;
while (pHead1 != NULL && pHead2 != NULL)
{
if (pHead1->val <= pHead2->val) // 考虑到稳定性
{
if (mergeHead == NULL)
{
mergeHead = current = pHead1;
}
else
{
current->next = pHead1;
current = current->next;
}
pHead1 = pHead1->next;
}
else
{
if (mergeHead == NULL)
{
mergeHead = current = pHead2;
}
else
{
current->next = pHead2;
current = current->next;
}
pHead2 = pHead2->next;
}
}
if (pHead1 == NULL) // 处理长链表的多余子链
current->next = pHead2;
else
current->next = pHead1;
return mergeHead;
}
链表中环的入口结点
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if (pHead == NULL) {
return NULL;
}
ListNode* meetingnode = MeetingNode(pHead);
if (meetingnode == NULL) {
return NULL;
}
// 回环链表结点个数
int nodesloop = 1;
// 找到环中结点个数
ListNode* pNode1 = meetingnode;
while (pNode1->next != meetingnode) {
pNode1 = pNode1->next;
nodesloop++;
}
pNode1 = pHead;
// 第一个指针向前移动nodesloop步
for (int i = 0; i < nodesloop; i++) {
pNode1 = pNode1->next;
}
// 两个指针同时移动,找到环入口
ListNode* pNode2 = pHead;
while (pNode1 != pNode2) {
pNode1 = pNode1->next;
pNode2 = pNode2->next;
}
return pNode1;
}
private:
// 使用快慢指针,找到任意的一个环中结点
ListNode* MeetingNode(ListNode* pHead) {
ListNode* pSlow = pHead->next;
if (pSlow == NULL) {
return NULL;
}
ListNode* pFast = pSlow->next;
while (pFast != NULL && pSlow != NULL) {
if (pFast == pSlow) {
return pFast;
}
pSlow = pSlow->next;
pFast = pFast->next;
if (pFast != NULL) {
pFast = pFast->next;
}
}
return NULL;
}
};
删除链表中重复的结点
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead)
{
if(pHead == NULL){
return NULL;
}
// 指向当前结点前最晚访问过的不重复结点
ListNode* pPre = NULL;
// 指向当前处理的结点
ListNode* pCur = pHead;
// 指向当前结点后面的结点
ListNode* pNext = NULL;
while(pCur != NULL){
// 如果当前结点与下一个结点相同
if(pCur->next != NULL && pCur->val == pCur->next->val){
pNext = pCur->next;
// 找到不重复的最后一个结点位置
while(pNext->next != NULL && pNext->next->val == pCur->val){
pNext = pNext->next;
}
// 如果pCur指向链表中第一个元素,pCur -> ... -> pNext ->...
// 要删除pCur到pNext, 将指向链表第一个元素的指针pHead指向pNext->next。
if(pCur == pHead){
pHead = pNext->next;
}
// 如果pCur不指向链表中第一个元素,pPre -> pCur ->...->pNext ->...
// 要删除pCur到pNext,即pPre->next = pNext->next
else{
pPre->next = pNext->next;
}
// 向前移动
pCur = pNext->next;
}
// 如果当前结点与下一个结点不相同
else{
pPre = pCur;
pCur = pCur->next;
}
}
return pHead;
}
};