leetcode刷题记录---19.9.13

北战南征 提交于 2019-11-29 13:24:44

概述

1.数组中的第k个最大元素,利用快排的partition思想,加上二分检索

2.比特位计数,直观的方法时间复杂度为o(n*sizeof(int)),有效降低时间复杂度的方法是判断当前数字的奇偶性,再与前一个联系,得出结论。

3.最长上升子序列,动态规划o(n^2),贪心+二分o(nlogn)。维护一个数组,新进来的如果比数组最后的大,插在后面,否则找到第一个比新进来的大的元素,替换掉他。这个查找是属于有序数组中的查找,可以采用折半查找。

4.根据身高重建队列,这题先根据身高和人数排序(用到了sort的复杂排序,自定义排序规则)。然后再根据人数往数组中的合适位置插。

5.寻找重复数,o(logn)使用二分查找,判断左右数字出现的个数。

6.环形链表,查找链表的环的入口。两种方法unordered_set和快慢指针。快慢指针主要是推导公式,证明,为什么A = (n-1)(B+C)+C

 

 

 

1.数组中第k个最大元素,一遍过,莫名其妙

题目描述:给定一个无序数组,找出这个数组中第k大的数字。

思路:快排的Partition函数

示例 1:

输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4

class Solution {
public:
    void swap(int& a,int& b){int temp = a;a = b;b = temp;}
    int Partition(vector<int>& nums,int start,int end){
        int pivotKey = nums[start];
        while(start<end){
            while(pivotKey<=nums[end]&&start<end) end--;
            swap(nums[start],nums[end]);
            while(pivotKey>=nums[start]&&start<end) start++;
            swap(nums[start],nums[end]);
        }
        return start;
    }
    int findKthLargest(vector<int>& nums, int k) {
        int pp = nums.size()-k;
        int end = nums.size()-1;
        int pivot = Partition(nums,0,end);
        while(pp!=pivot){
            if(pp<pivot){pivot = Partition(nums,0,pivot-1);}
            else{pivot = Partition(nums,pivot+1,end);}
        }
        return nums[pp];
    }
};

2. 比特位计数

题目描述:给定一个整数,从0到这个整数之间所有的数的二进制中含1的个数,放入对应数组。

要求,时间o(n).....自己实现的o(nlogn).

时间复杂度为o(n)的思路:

通过奇偶性来判断1的个数。 

1.如果n为奇数,那么他的1的个数等于前一个数的加一

2.如果n为偶数,那么他的1的个数等于他一般的数的1的个数。。。。。。(很关键!!!!!!)

    int countCore(int number){
        int count = 0;
        while(number){
            count++;
            number = (number-1)&number;
        }
        return count;
    }
    vector<int> countBits(int num) {
        vector<int> res(num+1,0);
        for(int i = 1;i<=num;++i){
            res[i] = countCore(i);
        }
        return res;
    }


//o(n)解法
class Solution {
public:
    
    vector<int> countBits(int num) {
        vector<int> res(num+1,0);
        for(int i = 1;i<=num;++i){
            if(i&1 == 1) res[i] = res[i-1]+1;
            else{
                res[i] = res[i/2];
            }
        }
        return res;
    }
};

3.最长上升子序列(较难)

题目描述:给定一个无序的数组,求他的最长子序列,注意子序列是可以不连续的!要求时间复杂度o(nlogn)

参考:https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/dong-tai-gui-hua-er-fen-cha-zhao-tan-xin-suan-fa-p/

思路:动态规划o(n^2),二分查找+贪心o(nlogn)

动态规划:

1.定义状态。。

       dp[i]表示以第i个数字为结尾的“最长上升子序列”的长度。在[0,1,...,i]的范围内,选择以数字nums[i]为结尾可获得的最长上升子序列的长度。nums[i]必须要被选取。初始化,可将dp全部初始化为1。定义输入,题目不是求状态,所以把dp数组看一遍,找最大的。

2.推导“状态转移方程”。

         dp[i] = max{1+dp[j] for j<i if nums[j]<nums[i]}

二分查找+贪心:

1.维护一个升序数组,存放的规则是,如果数组元素来了,判断是否比数组的最后一个元素大。

2.在遍历数组过程中,如果比数组的最后一个元素大,那么加载数组后面,数组长度加一

3.否则就要找到这个元素在这个升序数组中的存放位置,如果已经存在,那就不更新,如果位于俩数中间,替换后一个数。

     这一步使用二分查找。(尽量使得辅助数组的结尾更小,这就会有更大的可能性构造更长的上升子序列)

4.tail的长度即为结果

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int len = nums.size();
        if(len<2) return len;
        vector<int> temp;
        temp.push_back(nums[0]);
        int end = 0;
        for(int i = 1;i<len;++i){
            if(nums[i]>temp[end]){
                temp.push_back(nums[i]);
                end++;
            }
            else{
                int start = 0;
                int right = end;
                while(start<right){
                    int mid = (start+right)>>1;
                    if(temp[mid]<nums[i]) start = mid+1;
                    else right = mid;
                }
                temp[start] = nums[i];
            }
        }
        return end+1;
        
    }
};

4.根据身高重建队列

题目描述:给定一组数据,存放的是数值对[x,y],x表示此人的身高,y表示在此人前面还有y个身高大于等于他的人。要求对这些无序的数据进行排序。

参考:https://leetcode-cn.com/problems/queue-reconstruction-by-height/solution/406-gen-ju-shen-gao-zhong-jian-dui-lie-pai-xu-hou-/

思路:

1.建立辅助数组,把最高的人先挑出来,按照次数升序排序。。。sort(people.begin(),people.end(),[](vector<int>&a,vector<int>&b){...})                //在这里学会使用sort进行随心所欲的排序。

2.从候选数组中挑选出最高的人,插入辅助数组的合适位置。for(auto &e:people){res.insert(res.begin()+e[1],e);}

class Solution {
public:
    vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
        //先排序,再一个一个插入
        sort(people.begin(),people.end(),[](const vector<int>&a,const vector<int>&b){
           if(a[0] > b[0]) return true;
            if(a[0] == b[0]&&a[1]<b[1]) return true;
            return false;
        });
        vector<vector<int>> res;
        for(auto &e:people){
            res.insert(res.begin()+e[1],e); 
        }
        return res;
    }
};

 

5.寻找重复数287.....关键是边界条件

题目描述:给定一个大小为n+1的数组,里面放着从1到n的数字,假设只有一个数字是重复的,找出他,他可能重复好多次。

要求以o(1)的空间复杂度,小于o(n^2)的空间复杂度,不能改变原数组,这个数字有可能重复出现n次。

参考:https://leetcode-cn.com/problems/find-the-duplicate-number/solution/er-fen-fa-si-lu-ji-dai-ma-python-by-liweiwei1419/

思路:无,,,想到了剑指offer上的二分定位重复数字

1.以n/2为界限,统计小于n/2的数字个数

2.如果数字个数大于n/2,那就在左边,end = mid+1;             注意边界条件[),

3.反之再右边,start = mid;             注意边界条件[]

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        //先排序,再双指针....不行,不能改变原数组
        //用二分查找
        int len = nums.size();
        int start = 0;
        int end = len-1;
        while(start<end){
            int mid = (start+end)>>1;
            int count = 0;
            for(auto num:nums){
                if(num<=mid) count++; 
            }
            if(count>mid) end = mid;
            else start = mid+1;
        }
        
        return start;
    }
};

6.环形链表II

题目描述:给定一个链表,判断是否有环,并找到环的入口。

思路:1.快慢指针2.(别人的)unordered_set代码简短

快慢指针:

1.在第一次相遇时,慢指针走过的路程为:slow=A+B。快指针走过的路程为:fast=A+n(B+C)+B。

2.因为快指针走过的距离一定是慢指针的二倍,所以有2(A+B) = A+n(B+C)+B。化简为A = (n-1)(B+C)+C

3.将快指针指向head,并且前进步长改为1。当走改进后的快指针走了A步,则一定会与慢指针再入口处重合

unordered_set:

1.定义unordered_set。

2.只要head不为空,并且set中没有出现过head,那就往set里面加。

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        // unordered_set<ListNode*> myset;
        // while(head){
        //     if(myset.count(head)>0) return head;
        //     else myset.insert(head);
        //     head = head->next;
        // }
        // return nullptr;
        
         
        
        ListNode* pfast = head;
        ListNode* pslow = head;
        ListNode* ptemp = head;
        bool hasCycle = false;
        while(pfast != nullptr && pfast->next != nullptr){
            pslow = pslow->next;
            pfast = pfast->next->next;
            if(pslow == pfast){
                hasCycle = true;
                while(hasCycle && ptemp != pslow){
                    ptemp = ptemp->next;
                    pslow = pslow->next;
                }
                break;
            }
        }
        return hasCycle?pslow:nullptr;
    }
};

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!