2020软件工程作业04

|这个作业属于哪个课程|https://edu.cnblogs.com/campus/zswxy/2018SE|
| ---- | ---- | ---- |
|这个作业要求在哪里|https://edu.cnblogs.com/campus/zswxy/2018SE/homework/11406|
|这个作业的目标|算法和数据结构初体验|
|其他参考文献| |

第一题:寻找数组中第k大的数
解题思路
很经典的算法题了,最近复习了一下。

一般来说有两种经典的解法,快排和堆,各有优势,以下分开来讲。

快排
快排的思想简单来说,就是每次选取数组中一个元素,然后以它作为中轴点,把当前数组中所有比它大的数移到它左边,所有比它小的数移到它右边(这里讨论的是降序,升序反之),这样经过一次遍历后,原来的数组就以中轴点分成了两部分,换言之,这个中轴点数字就已经被正确地排到了它最终应该在的位置上。

然后运用分治的思想,把中轴点左边、右边的两个数组继续分别进行上面一样的操作,这里我们一般是写成递归来提高代码的简洁性和易读性,直到细分到最后每个子数组只有一个元素为止。

如果完整地跑完以上过程,最终我们就可以得到一个排好序的数组,但是这里我们的目标是寻找数组中第 K 大的数,因此在分治阶段,我们可以通过判断中轴点所在的位置,来选择只对一边进行继续排序,另一边则可以不管了。

这里其实不难理解,第一轮完成的时候,第一个中轴点来到了它最终的位置,我们假设它来到了数组的 i 位置,那么我们就可以比较 i 和 K:

假如 i 等于 K,则这个数就是我们要寻找的那个数。(因为在最终排好序的数组里,它还是会在这个位置,也就是第 K 位,对于降序数组而言,第 K 位也就是第 K 大的数)
假如 i 小于 K,则说明我们要召的那个数应该在中轴点的右边,那么我们就对右边的数组进行处理,左边则可以不管了
假如 i 大于 K,那么我们就处理左边的数组
Python 实现如下:

class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
def q(s, e):
t = nums[s]
i, j = s, e
while i < j:
while i < j and nums[j] <= t:
j -= 1
nums[i] = nums[j]
while i < j and nums[i] >= t:
i += 1
nums[j] = nums[i]
nums[i] = t
if i == k-1:
return t
elif i > k-1:
return q(s, i-1)
else:
return q(i+1, e)
return q(0, len(nums)-1)
快排法的时间复杂度为 O(n),但是缺点在于需要存储整个数组,当数组非常巨大的时候,有时我们需要选择堆方法。

二、二叉树的先、中、后 序遍历与层级遍历
原理
先序遍历
又可叫前根序遍历,先遍历根节点,再遍历根节点的左子树,然后遍历根节点的右子树。
中序遍历
又可叫中根序遍历,先遍历根节点的左子树,再遍历根节点,然后遍历根节点的右子树。
后序遍历
又可叫后根序遍历,先遍历根节点的左子树,再遍历根节点的右子树,然后遍历根节点。
层次遍历
对整棵二叉树按从上到下,对每一层按从左到右的顺序进行遍历。这里我们需要借助队列:先访问节点的孩子节点也应比后访问节点的孩子节点先访问。故这就类似于队列先进先出的性质,每访问一个节点时,它的孩子节点(如果存在)就随着入队。
迭代实现
另外一种代码
//先序遍历
void preOrderWithoutRecursion() {
if (!empty()) {
//使用一个栈,栈的先进后出实现功能
stack<BTNode> s;
BTNode
p = root;
//先将根节点存入栈
s.push(p);
while (!s.empty()) {
//首先输出当前节点
cout << setw(4) << p->data;
//先存入右节点,栈的特性使之后出
if (p->rchild) s.push(p->rchild);
//后存入左节点,栈的特性使之先出
if (p->lchild) p = p->lchild;
else {
//p为输出节点,根节点输出后,应该为左节点,然后释放栈顶
p = s.top();
s.pop();
}
}
cout << endl;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//中序遍历
void inOrderWithoutRecursion() {
if (!empty()) {
//利用栈的特性,先进后出。
stack<BTNode> s;
BTNode
p = Root;
while (!s.empty() || p) {
if (p) {
//一直下降到最左边
s.push(p);
p = p->lchild;
}
else {
//当到底最左时,输出当前节点
p = s.top();
s.pop();
cout << setw(4) << p->data;
//如果该左节点被视为根节点并有右节点,p设为右节点,
p = p->rchild;
}
}
cout << endl;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//后序遍历
void postOrderWithoutRecursion() {
if (!empty()) {
//利用栈特性,进行操作
stack<BTNode> s;
BTNode
p = Root;
while (p != NULL) {
//下降到最左边
s.push(p);
p = p->lchild;
}
while (!s.empty()) {
//判断当前该访问左节点还是右节点
BTNode* pLastVisit = p;
//最左节点入栈
p = s.top();
s.pop();
//当右节点为空或前节点,则说明该节点为左节点,直接输出
if (p->rchild == NULL || p->rchild == pLastVisit)
cout << setw(4) << p->data;
//当左节点为前节点时,该点为根,该访问右节点
else if (p->lchild == pLastVisit) {
s.push(p);
p = p->rchild;
s.push(p);
//如果右节点作为根节点有左右子树,则进行该操作
while (p != NULL) {
if (p->lchild != NULL)
s.push(p->lchild);
p = p->lchild;
}
}
}
cout << endl;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
递归实现
void preorder (node *p) {
if (p != NULL) {
cout << p->data << " ";
preorder(p->left);
preorder(p->right);
}
}
void inorder (node *p) {
if (p != NULL) {
inorder(p->left);
cout << p->data << " ";
inorder(p->right);
}
}
void postorder (node *p) {
if (p != NULL) {
postorder(p->left);
postorder(p->right);
cout << p->data << " ";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
参考
http://blog.csdn.net/zhangxiangdavaid/article/details/36634411

posted @ 2020-10-29 13:47  不想敲代码了  阅读(104)  评论(1编辑  收藏  举报