【算法设计作业】week14

来源:互联网 发布:美团大数据招聘 编辑:程序博客网 时间:2024/05/16 01:34

【算法设计作业】week14

给出了第33、34,35、36、38、39、41、45题的解析


33. Search in Rotated Sorted Array

题目来源:https://leetcode.com/problems/search-in-rotated-sorted-array/description/

题意

给出一个初始升序序列,经过一个(注:一个)“rotation”之后的序列。例如0 1 2 4 5 6 7 经过一次”rotation”可以变成4 5 6 7 0 1 2
要求在这个rotation过的序列中寻找给定元素target的index。

思路

经过rotation后,必定只有一半是升序的,而另一半是降序的。判断出要哪一半后,就可以对这一半进行二分搜索。

参考代码

http://www.cnblogs.com/grandyang/p/4325648.html


34. Search for a Range

题目来源https://leetcode.com/problems/search-for-a-range/description/

题意

给出一个有序数列,求出给定target的范围,用左闭右闭区间表示。如果不存在,则返回[-1, -1]

思路

实现两个函数,分别是upper_bound求上界,lower_bound求下界,调用这两个函数,即得出结果。

1.lower_bound

首先看int lower_bound(vector<int>& nums, int x, int y, int v),在这个函数中,nums 是有序数列,搜索范围是[x, y)注!左闭右开区间!但返回区间是左闭右闭区间,因为可能返回最后一个元素的后一个位置,这就是待插入位置),v 是所求的搜索目标。
这个函数是这样的:

 int lower_bound(vector<int>& nums, int x, int y, int v) {        int m;        while(x < y) {            m = x + (y - x) / 2;            if(nums[m] >= v) y = m;            else x = m + 1;        }        return x;    }

我们用二分搜索的思想,首先求出xy 的中值,m
记住我们此时求的是下界,并且搜索区间是左闭右开的,返回区间是左闭右闭的,然后有下面这三种情况:

  • nums[m]=v 至少已经找到一个了,左边可能还有,因而区间变为[x,m]
  • nums[m]>v,所求位置不可能在m后面,但有可能在m上,区间变为[x,m]
  • nums[m]<v, m 和前面的都不可行,区间变为 [m+1, y]
    而头两种情况可以合并。

2.upper_bound

lower_bound 原理类似。

参考代码

class Solution {public:    vector<int> searchRange(vector<int>& nums, int target) {        vector<int> ans;        int low = lower_bound(nums, 0, nums.size(), target);        int high = upper_bound(nums, 0, nums.size(), target)-1;        if (low > high) {            ans.push_back(-1);            ans.push_back(-1);        } else {            ans.push_back(low);            ans.push_back(high);        }        return ans;    }    int lower_bound(vector<int>& nums, int x, int y, int v) {        int m;        while(x < y) {            m = x + (y - x) / 2;            if(nums[m] >= v) y = m;            else x = m + 1;        }        return x;    }    int upper_bound(vector<int>& nums, int x, int y, int v) {        int m;        while(x < y) {            m = x + (y - x) / 2;            if(nums[m] <= v) x = m+1;            else y = m;        }        return x;    }};

35. Search Insert Position

来源:https://leetcode.com/problems/search-insert-position/description/

题意

给出一个序列,返回target的位置。如果不存在,则返回target应插入的位置。

思路

和第34题的upper_bound一摸一样

参考代码

class Solution {public:    int searchInsert(vector<int>& nums, int target) {        int x = 0, y = nums.size();        int m;        while(x < y){            m = x + (y-x)/2;            if(nums[m] >= target) y = m;            else x = m + 1;        }        return x;    }};

36. Valid Sudoku

来源https://leetcode.com/problems/valid-sudoku/description/

题意

给出一个部分的数独,问已经填的数字是否符合数独规则。

思路

很简单,只要分别判断每一行,每一列,每个九宫格是否出现重复数字即可。

参考代码

class Solution {public:    bool isValidSudoku(vector<vector<char>>& board) {        for(int i = 0; i < 9; i++) {            if (!(isColValid(board, i) && isRowValid(board, i) && isSquareValid(board, i/3, i%3))) {                return false;            }        }        return true;    }    bool isColValid(vector<vector<char>>& board, int col) {        bool seen[10];        for(int i = 0; i < 10; i++) seen[i] = false;        for(int i = 0; i < 9; i++) {            if(board[i][col] != '.') {                int num = board[i][col] - '0';                if(seen[num] == false) {                    seen[num] = true;                } else {                    return false;                }            }         }        return true;    }    bool isRowValid(vector<vector<char>>& board, int row) {        bool seen[10];        for(int i = 0; i < 10; i++) seen[i] = false;        for(int i = 0; i < 9; i++) {            if(board[row][i] != '.') {                int num = board[row][i] - '0';                if(seen[num] == false) {                    seen[num] = true;                } else {                    return false;                }            }         }        return true;    }    bool isSquareValid(vector<vector<char>>& board, int x, int y) {        x = x * 3;        y = y * 3;        bool seen[10];        for(int i = 0; i < 10; i++) seen[i] = false;        for(int i = 0; i < 3; i++) {            for(int j = 0; j < 3; j++) {                if(board[x+i][y+j] != '.') {                    int num = board[x+i][y+j] - '0';                    if(seen[num] == false) {                        seen[num] = true;                    } else {                        return false;                    }                }             }        }        return true;    }};

38. Count and Say

题目来源:https://leetcode.com/problems/count-and-say/description/

题意

给出序列,如下:

1.     12.     113.     214.     12115.     111221

要求返回第n条序列。第n条序列是对第n-1条序列的读法,如n=2的序列就是对n=1序列的读,“1个1”,即11。11读作“2个1”,因而n=3是“21”.

思路

很简单,按照题意递归就是了。

代码

class Solution {public:    string countAndSay( int n) {        static string strs[] ={"", "1", "11", "21", "1211", "111221"};        if (n <= 5) return strs[n];        else {            string str = countAndSay(n-1);            string ans;            for(int i = 0; i < str.length();) {                int j = i+1;                while(j < str.length() && str[j] == str[i]) {                    j++;                }                ans+=(char(j-i+'0'));                ans+=(str[i]);                i = j;            }            return ans;        }    }};

39.Combination Sum

题目来源:https://leetcode.com/problems/combination-sum/description/

题意

大概就是可重复选取的背包问题。

思路

采用递归的思想,如果lackOfNum比当前位置数字大,则选取当前数字,然后递归下去。如果到了某一个数字,lackOfNum==0,说明刚好满足条件,将当前选取的数列加入答案ans中。

参考代码

class Solution {public:    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {        sort(candidates.begin(), candidates.end());        vector<vector<int>> ans;        vector<int> currentNum;        recurse(ans, candidates, currentNum, 0, target);        return ans;    }    void recurse(vector<vector<int>>& ans, vector<int>& candidates, vector<int>& currentNum, int currentPos, int lackOfNum) {       if (0 == lackOfNum) {           ans.push_back(currentNum);           return;       }        if(currentPos < candidates.size() && lackOfNum >= candidates[currentPos]) {            for(int i = currentPos; i != candidates.size() && candidates[i] <= lackOfNum ; i++) {                currentNum.push_back(candidates[i]);                recurse(ans, candidates, currentNum, i, lackOfNum-candidates[i]);                currentNum.pop_back();            }        }    }};

41. First Missing Positive

题目来源:https://leetcode.com/problems/first-missing-positive/discuss/

题意

给出一个无序数列,要求找到第一个未出现的正数。要求时间复杂度为O(n),空间复杂度为O(constant)。

思路

这是参考别人的代码,大概意思是将正数i放到第i个位置,即下标为i-1的位置。然后遍历这个数组,看哪个位置的元素不对,就知道哪个正数缺失。

参考代码

class Solution {public:    int firstMissingPositive(vector<int>& nums) {        int n = nums.size(), tmp = 0;        for(int i = 0; i < n; i++) {            while(nums[i] > 0 && nums[i] <= n && nums[nums[i]-1] != nums[i]) {                tmp = nums[nums[i]-1];                nums[nums[i]-1] = nums[i];                nums[i] = tmp;            }        }        for(int i = 0; i < n; i++) {            if(nums[i] != i+1)                return i+1;        }        return n+1;    }};

45. Jump Game II

题目来源:https://leetcode.com/problems/jump-game-ii/description/

题意

给出一个数列nums,第i个元素就是从该点最多往前跳的步数nums[i]。问i=0最少要经过多少跳能到i=nums.size()-1。
本题应该要求的是O(n),因为我写递推加记忆化搜索,O(n^2)是超时的。

思路

参考了discussion里的答案,用的是BFS,但不是传统的BFS,因为不需要用到一个队列实例。
思想是极其精妙的:将数列划分为各个level,第i层就是从起点经过i跳可以到达的点。代码只保存了当前层最远的点(最远的都跳到,进的肯定能到),然后遍历这层的点,寻找到下一层能到的最远的点。然后进入下一层,继续这个过程。直到发现终点在某个level,这就是所需的最大跳数。

简洁!高效!美丽!

参考代码

class Solution {public:    int jump(vector<int>& nums) {        int n = nums.size();        if(n < 2) return 0;        int level = 0, curMax = 0, i = 0, nextMax = 0;        while(true) {            level++;            for(;i <= curMax; i++) {                nextMax = max(nextMax, nums[i]+i);                if(nextMax >= n-1) return level;            }            curMax = nextMax;        }        return 0;    }};