leetcode题解日练--2016.7.17

来源:互联网 发布:python实战 编辑:程序博客网 时间:2024/05/22 15:14

日练三题,冰冻三尺非一日之寒。

今日题目:1、找到顶点元素;2、去除排序数组中的重复元素II;3、H指数 ; 4、H指数II;5、判断一棵二叉树是否合法。

今日摘录:

你站在桥上看风景,
看风景的人在楼上看你。
明月装饰了你的窗子,
你装饰了别人的梦。
——卞之琳《断章》

162. Find Peak Element | Difficulty: Medium

A peak element is an element that is greater than its neighbors.
Given an input array where num[i] ≠ num[i+1], find a peak element and return its index.
The array may contain multiple peaks, in that case return the index to any one of the peaks is fine.
You may imagine that num[-1] = num[n] = -∞.
For example, in array [1, 2, 3, 1], 3 is a peak element and your function should return the index number 2.

Note:
Your solution should be in logarithmic complexity.

题意:找到一个数组中的峰值元素,即该值比左右元素均大。

思路:
1、logN的复杂度,一般直观上想应该是二分的思想。但是这里困难的地方在于首先这里不是单纯查找一个值,而是查找一个峰值,也就是与三个值的大小有关,第二是元素并不是有序的。
这里面有一个需要注意的地方就是nums[-1]和nums[n]= - ∞
题目可能存在不止一个峰顶元素,我们二分去查找的时候,首先可以考虑找到一个局部的坡,如果找到的是一个上坡,即nums[i]

class Solution {public:    int findPeakElement(vector<int>& nums) {        return recursion(nums,0,nums.size()-1);    }    int recursion(vector<int>&nums,int left,int right)    {        if(left==right) return left;        int mid1 = left+(right-left)/2;        int mid2 = mid1+1;        if(nums[mid1]>nums[mid2])   return recursion(nums,left,mid1);        else        return recursion(nums,mid2,right);    }};

结果:8ms

2、将上述思路该写成迭代版本

class Solution {public:    int findPeakElement(vector<int>& nums) {        int left = 0,right = nums.size()-1;        int mid1,mid2;        while(left<right)        {            mid1 = left+(right-left)/2;            mid2 = mid1+1;            if(nums[mid1]>nums[mid2])   right = mid1;            else    left=mid2;        }        return left;    }};

结果:4ms

3、还有一种很巧妙的思路:
首先从nums[0]看起,nums[0]>nums[-1],那么nums[1]只有两种可能,一种是比nums[0]小,一种是比nums[0]大,小的情况就可以直接返回0,也就是上一个元素的值,大的话更新上一个元素为nums[1]。
通俗来讲就是之前全部都是上坡,直到某个元素会打破这种一直上坡的规律,那么这个元素前面一个元素就必定是一个峰值元素。
但是这种方法就不是logN的复杂度了,最好是O(1),最坏O(N),平均O(N)
只要理解了这点就不难写出代码了

class Solution {public:    int findPeakElement(vector<int>& nums) {        for(int i=1;i<nums.size();i++)        {            if(nums[i]<nums[i-1])   return i-1;        }        return nums.size()-1;    }};

结果:4ms

80. Remove Duplicates from Sorted Array II | Difficulty: Medium

Follow up for “Remove Duplicates”:
What if duplicates are allowed at most twice?
For example,
Given sorted array nums = [1,1,1,2,2,3],
Your function should return length = 5, with the first five elements of nums being 1, 1, 2, 2 and 3. It doesn’t matter what you leave beyond the new length.

题意:一个排序链表中最多允许一个元素出现两次,返回去掉多于两次的元素之后剩下的链表元素个数
相关题目:26.Remove Duplicates from Sorted Array

思路:
1、首先想了一种效率比较低的元素,时间O(N),空间O(N),利用一个哈希来存放每个元素出现的次数。

class Solution {public:    int removeDuplicates(vector<int>& nums) {        if(nums.size()<3)   return nums.size();        unordered_map<int,int> maps;        int res=0;        for(int i=0;i<nums.size();i++)        {         maps[nums[i]]++;           }        int k=0;        for(int i=nums[0];i<=nums[nums.size()-1];i++)        {            for(int j=0;j<2&&j<maps[i];j++)                nums[k++] = i;        }        return k;    }};

结果:32ms

2、想到了利用两个指针,这两个指针最开始放在前面2个位置,然后根据这两个指针的值相等和不相等两种情况进行讨论:
两个指针相等的情况下,首先将second移动到相等值的最右边,注意不能越界,然后记录下这两个值,同时first和second右移2位,这里可能会出现一种特殊情况就是second越界但是first没有越界,这个时候需要将first指针的元素加入到结果中。
两个指针不相等的情况下,记录firs指针,同时两个指针右移一位

class Solution {public:    int removeDuplicates(vector<int>& nums) {        if(nums.size()<3)   return nums.size();        int first = 0,second = 1;        int res=0;        while(second<=nums.size()-1)        {            //两个指针不相等的情况下,记录firs指针,同时两个指针右移一位            if(nums[first]!=nums[second])            {                nums[res++] = nums[first];                first  = second;                second++;            }            //两个指针相等的情况下,首先将second移动到相等值的最右边,注意不能越界,然后记录下这两个值,同时first和second右移2位,这里可能会出现一种特殊情况就是second越界但是first没有越界,这个时候需要将first指针的元素加入到结果中。            else            {                while(nums[first]==nums[second+1]&&second<nums.size()-1)    second++;                nums[res++] = nums[first];                nums[res++] = nums[second];                first =second+1;                second+=2;            }        }        if(first<=nums.size()-1)    nums[res++] = nums[first];        return res;    }};

结果:19ms

3、看了下高赞答案,看到一个很好的思路,比之前的解法好理解很多
https://discuss.leetcode.com/topic/17180/3-6-easy-lines-c-java-python-ruby

class Solution {public:    int removeDuplicates(vector<int>& nums) {        int i=0;        for(auto cur:nums)        {            //当nums不满2个元素或者nums数组中不包含2个当前访问元素的时候,就将当前元素加入,即num>nums[i-2]            if(i<2 || cur>nums[i-2])                nums[i++] = cur;        }        return i;    }};

结果:16ms

274. H-Index | Difficulty: Medium

Given an array of citations (each citation is a non-negative integer) of a researcher, write a function to compute the researcher’s h-index.
According to the definition of h-index on Wikipedia: “A scientist has index h if h of his/her N papers have at least h citations each, and the other N − h papers have no more than h citations each.”
For example, given citations = [3, 0, 6, 1, 5], which means the researcher has 5 papers in total and each of them had received 3, 0, 6, 1, 5 citations respectively. Since the researcher has 3 papers with at least 3 citations each and the remaining two with no more than 3 citations each, his h-index is 3.
Note: If there are several possible values for h, the maximum one is taken as the h-index.

题意:一个数组中有n个数字大于等于n,找到这个n。
思路:
1、O(NlogN)时间复杂度的方法,先排序再查找。
我们以citations = [3, 0, 6, 1, 5]为例,当我们对其进行排序之后,元素变为citations = [6,5,3,1,0]这个时候,我们只需要找到第一个不满足citations[i]>=i的值,那么这个值的前一个就是我们要找的。
那么很显然,按照定义从大到小排序再进行一次二分查找就搞定了,注意二分的边界条件和返回值与下标之间相差1这两点就不难写出代码。

class Solution {public:    int hIndex(vector<int>& citations) {        if(citations.size()<1)  return 0;        sort(citations.begin(),citations.end(),greater<int>());        int left = 0,right=citations.size()-1;        while(left<=right)        {            int mid = left+(right-left)/2;            if(citations[mid]-1==mid) return mid+1;            else if(citations[mid]-1>mid) left = mid+1;            else    right = mid-1;        }        return right+1;    }};

结果:4ms

2、hint说更快的方法是使用额外的空间,这里很容易联想到桶排序,桶排序在数字变化较大的情况下不太适用,但是这道题是没问题的,日常被引用的次数都是小于等于4位数的。之前的排序是对数组中每个元素与下标进行比较,而利用桶排序解决此题的时候是需要累计篇数大于等于当时正在计算的论文的被引次数。说着比较拗口,一切都在代码里。

class Solution {public:    int hIndex(vector<int>& citations) {        int size = citations.size();        if(size<1)  return 0;        //建立一个大小为size-1的桶,这里多1是为了下标的方便        int *cnt = new int[size+1]();        //统计不同引用次数的论文的篇数,存在桶里面        for(auto num:citations) cnt[min(num,size)]++;        int tol = 0;        //遍历桶元素,tol代表目前统计有多少篇论文,i代表正在统计的论文的被引次数,题目要找的就是n篇>=n被引的论文的那个n,tol第一次大于等于i的时候,就是i能取的最大值,因为后面tol越来越大,        //i越来越小,无论如何都是符合条件的,所以返回第一次满足条件的i        for(int i=size;i>=0;i--)        {            tol+=cnt[i];            if(tol>=i)  return i;        }        return tol;    }};

结果:4ms

275. H-Index II | Difficulty: Medium

Follow up for H-Index: What if the citations array is sorted in ascending order? Could you optimize your algorithm?

题意:在上述H-Index 的基础上加了限制条件就是citations是升序的,如何进行进一步的优化?

思路:
1、直接二分查找

class Solution {public:    int hIndex(vector<int>& citations) {        int n=citations.size();        if(n==0)    return 0;        int left = 0,right = n-1;        while(left<=right)        {            int mid = left+(right-left)/2;            if(citations[mid]==n-mid)   return n-mid;            else if(citations[mid]>n-mid)   right  = mid-1;            else    left = mid+1;        }        return n-(right+1);    }};

结果:13ms

2、如何换成逐个去找,时间大大增加

class Solution {public:    int hIndex(vector<int>& citations) {        int n=citations.size();        for(int i=n-1;i>=0;i--)        {            if(citations[i]<n-i) return n-(i+1);        }        return n;    }};

结果:64ms

331. Verify Preorder Serialization of a Binary Tree | Difficulty: Medium

One way to serialize a binary tree is to use pre-order traversal. When we encounter a non-null node, we record the node’s value. If it is a null node, we record using a sentinel value such as #.

     _9_    /   \   3     2  / \   / \ 4   1  #  6/ \ / \   / \# # # #   # #

For example, the above binary tree can be serialized to the string “9,3,4,#,#,1,#,#,2,#,6,#,#”, where # represents a null node.

Given a string of comma separated values, verify whether it is a correct preorder traversal serialization of a binary tree. Find an algorithm without reconstructing the tree.

Each comma separated value in the string must be either an integer or a character ‘#’ representing null pointer.

You may assume that the input format is always valid, for example it could never contain two consecutive commas such as “1,,3”.

Example 1:
“9,3,4,#,#,1,#,#,2,#,6,#,#”
Return true

Example 2:
“1,#”
Return false

Example 3:
“9,#,#,1”
Return false

题意:判断一棵二叉树是否合法

思路:
1、先观察怎样的二叉树才算合法的,先看特例,满二叉树(根节点深度算作1),看看深度为3的二叉树有什么特点,深度为3的二叉树应该写成X,X,X,#,#,X,#,#,X,X,#,#,X,#,#,从数量上来看,空节点比非空节点多一个,怎么理解这个结论呢?从出度与入度的角度来看,能够提供出度的节点是非空节点,假设有X个非空节点,那么就有2*x的出度,那么又有哪些节点有入度呢?除了根节点没有其他节点均有1个入度,换句话说非空节点提供了X-1个入度,那么2X-(X-1) = 非空节点提供的出度-非空节点消耗的入度 = X+1=空节点的入度,每个空节点的入度都是1,也就是有X-1个空节点。
那顺序上有没有什么要求呢?用一个变量记录diff = 出度-入度,从根节点开始看,diff=2,根节点的左节点,非空就diff+1,空就diff-1,总之diff不能小于0,如果只有一个根节点的情况下,diff=0.为了更加统一进行计算,将初始值设置为1,然后从根节点开始遍历,每次遇到非空就+1,遇到空就-1,到最后应该是diff=0,否则就不是一棵二叉树。

class Solution {public:    bool isValidSerialization(string preorder) {        int size = preorder.length();        if(size<=0) return false;        istringstream tree(preorder);        int diff = 1;        string cur;        while(getline(tree,cur,','))        {   //首先判断在结束之前diff是否已经为0,如果中间过程已经为0就说明这个不是一棵二叉树            if(--diff<0)    return false;            //默认每个节点先减1,如果遇到了非空节点再加2,相当于是空节点-1,非空节点+1            if(cur!="#")    diff+=2;        }        return diff==0?true:false;    }};

结果:4ms

0 0