Fighting——Day4

今天计划先完成昨天未完成的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

这道题炸了,主要考察的还是相关子查询,应该如果可以写出这道题,前面的题应该都不在话下,当时分析的时候始终没有思路,最后还是看大佬的代码才懂了,这里直接贴出大佬对这题的分析好了,划重点!!!

  1. 从两张相同的salaries表(分别为s1与s2)进行对比分析,先将两表限定条件设为to_date = '9999-01-01',挑选出当前所有员工的薪水情况。
  2. 本题的精髓在于 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。其余排名以此类推。
  3. 千万不要忘了GROUP BY s1.emp_no,否则输出的记录只有一条(可能是第一条或者最后一条,根据不同的数据库而定),因为用了合计函数COUNT()
  4. 最后先以 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;
    }
};
发布了37 篇原创文章 · 获赞 42 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_42570248/article/details/90341980