算法设计与分析第二周练习

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36124194/article/details/82706961

目录
Kth Largest Element in an Array(分治算法)
Course Schedule(拓扑)

Kth Largest Element in an Array

题目

Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.

Example

Input: [3,2,1,5,6,4] and k = 2
Output: 5

Example

Input: [3,2,3,1,2,4,5,5,6] and k = 4
Output: 4

分析

分治算法的概念

在计算机科学中,分治法是建基于多项分支递归的一种很重要的算法范式。字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。这个技巧是很多高效算法的基础,如排序算法(快速排序、归并排序)、傅立叶变换(快速傅立叶变换)。

这题有两个思路,全部排序是不可能的:

  • 第一个思路是建一个堆,其大小为k-1,这个堆是有序的,当堆没满的时候,将元素直接插入堆中,当堆满后,我们可以有取舍进行操作,当遍历的元素小于堆的最小元素(堆的第一个元素),我们不需要处理;当遍历的元素大于堆顶元素时,pop堆顶元素,将元素插入到堆中。最后堆顶的元素即为第k大的元素。
  • 第二个思路就是运用分治算法;具体算法的过程:

    • 是找一个数组元素,将数组中比这个元素大的元素放到这个元素的前面,比这个元素小的元素放到这个元素的后面。
    • 如果这个元素的下标刚好等于k-1,则返回;如果下标大于k-1,则在前面的元素用同样的办法找;如果下标小于k-1,则在后面的元素用同样的办法找。

    这样不断的把这个问题一分为二,不断减少问题的规模,而且问题的本质没有发生变化。

这周刚好讲了分治算法,我们就拿这个简单的求第k大的数来练练手,由于数组是随机的,所以算法的时间复杂度是有提高的,

以第一个例子为例,具体的过程如下:
本来数组【3,2,1,5,6,4】
第一遍分治:3作为标志的数,结果为 【5,6,4,3,2,1】。然后k=2,目标数在左边。
第二遍分治:处理的目标是【5,6,4】,5作为标志数,结果为【6,5,4】此时判断target==5,刚好下标是1,也就是第二大的数。

源码

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        int r = nums.size() - 1, left = 0, right = r, l = left;

        while(1) {
            l = left + 1;
            r = right;
            //cout << "b" << tag << l << r << endl;
            while(l <= r) {
                if(nums[l] <= nums[left]&&nums[r] > nums[left]) {
                    int temp = nums[l];
                    nums[l] = nums[r];
                    nums[r] = temp;
                    l++;
                    r--;
                }
                if(nums[r] <= nums[left]) {
                    r--;
                }
                if(nums[l] >= nums[left]) {
                    l++;
                }

            }
            int temp = nums[left];
            nums[left] = nums[r];
            nums[r] = temp;

            int result = r;

            if((k == result + 1)) {
                return nums[result];
            } else if(result < k - 1) {
                left = result + 1;
            } else {
                right = result - 1;
            }
            //cout << "1" << tag << l << r << endl;
        }

    }
};

Course Schedule(拓扑)

题目

There are a total of n courses you have to take, labeled from 0 to n-1.

Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]

Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?

Example

Input: 2, [[1,0]] 
Output: true
Explanation: There are a total of 2 courses to take. 
             To take course 1 you should have finished course 0. So it is possible.

Example

Input: 2, [[1,0],[0,1]]
Output: false
Explanation: There are a total of 2 courses to take. 
             To take course 1 you should have finished course 0, and to take course 0 you should
             also have finished course 1. So it is impossible.

分析

这题是图论方面的题目,在leetcode只有那么仅仅的几道题。同时这也是Topological Sort的代表性题目,通过规定完成某个事件之前必须要完成某些特点的事件,也就是事件之间有明确的先后顺序,这些事件完成的顺序不一定只有一种,但必须满足某些事件的特定的顺序,这样所形成的序列被称为拓扑序列,选课之前完成某些先导课程这样便是典型的例子。
题目要求我们判断所给出的序列是否为拓扑序列,根据拓扑序列的特征:事件构成的图是无环的。我们得出一下判断的算法。

  • 图的每个点都对应着入度和出度,在初始情况下,只有那些起点的入度为0,一个拓扑序列可以有多个起点,所以我们将起点存放在一个队列中。
  • 先计算每个点的入度。
  • 从起点开始遍历,遍历到入度就少1,将入度为0的点push进栈中(变为起点,这是不再关注其入度,当成起点处理)。
  • 遍历完所有的起点,最后如果某个点入度不为0,那么就证明序列有环。

源码

class Solution {
public:
    bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) {
        vector<int> in(numCourses,0);
        queue<int> empty;
        vector<vector<int> > temp(numCourses, vector<int>(0));
        //建立图,vector的二维数组
        for(auto a : prerequisites) {
            temp[a.second].push_back(a.first);
            in[a.first]++;
        }

        //将入度为0的点存进队列里面
        for(int i = 0; i < numCourses; i++) {
            if(in[i] == 0) {
                empty.push(i);
            } 
        }
        //入度为0的点作为开始的点,检查是否有环
        while(!empty.empty()) {
            int frist_point = empty.front();
            empty.pop();
            for(auto a : temp[frist_point]) {
                in[a]--;
                if(in[a] == 0) {
                    empty.push(a);
                }
            }
        }
        for(int i = 0; i < numCourses; i++) {
            if(in[i] != 0) {
                return false;
            }
        }
        return true;
    }
};
阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页