2018年力扣高频算法面试题8排序与检索

1、寻找峰值

峰值元素是指其值大于左右相邻值的元素。
给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。
数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞。
要求:你的解法应该是O(logN)时间复杂度的。
参考思路:
方法一:线性扫描:时间复杂度O(n),空间复杂度O(1)。
方法二:二分查找:O(logN)时间复杂度。

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

2、寻找重复数

给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
要求:不能更改原数组(假设数组是只读的)。只能使用额外的 O(1) 的空间。时间复杂度小于 O(n2) 。数组中只有一个重复的数字,但它可能不止重复出现一次。
解答:
鸽子洞原理/抽屉原理:有n只鸽子和m个鸽洞,所有鸽子都住在鸽洞里,如果n>m,那么至少有2只鸽子必须住在同一鸽洞里。
https://leetcode-cn.com/problems/find-the-duplicate-number/solution/287xun-zhao-zhong-fu-shu-by-kirsche/

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int slow=0,fast=0;
        slow=nums[slow];
        fast=nums[nums[fast]];
        while(slow!=fast)
        {
            slow=nums[slow];
            fast=nums[nums[fast]];
        }
        int pre1=0,pre2=slow;
        while(pre1!=pre2)
        {
            pre1=nums[pre1];
            pre2=nums[pre2];
        }
        return pre1;
    }
};

3、计算右侧小于当前元素的个数

给定一个整数数组nums,按要求返回一个新数组counts。数组counts有该性质:counts[i]的值是nums[i]右侧小于nums[i]的元素的数量。
解答:归并排序 + 索引数组
https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self/solution/gui-bing-pai-xu-suo-yin-shu-zu-python-dai-ma-java-/

class Solution {
    vector<int>index;
    vector<int>helper;
    vector<int>count;
public:
    void merge(const vector<int> &nums,int lo,int mi,int hi)
    {
        if(lo==mi)return ;
        if(mi==hi)return ;
        // 先merge两个小的
        merge(nums, lo, (lo + mi) >> 1, mi);
        merge(nums, mi, (mi + hi) >> 1, hi);
        
        // 把需要用的index拷贝到helper里面,一会儿直接往index里面写就好了
        for (int k = lo; k < hi; k++) helper[k] = index[k];
        
        // 合并两个有序数组,并计算向右移动的位数(即右边有几个比当前元素小)
        int p1 = lo, p2 = mi, p = lo;
        while (p1 != mi or p2 != hi) {
            if (p1 == mi) {
                index[p++] = helper[p2++];
                
            } else if (p2 == hi) {
                index[p++] = helper[p1++];
                count[index[p - 1]] += (p2 - mi);  // p2 - mi即右边出了多少个
                
            } else if (nums[helper[p2]] < nums[helper[p1]]) {
                index[p++] = helper[p2++];
                
            } else {
                index[p++] = helper[p1++];
                count[index[p - 1]] += (p2 - mi);  // p2 - mi即右边出了多少个
            }
        }
    }
    
    vector<int> countSmaller(vector<int>& nums) {
        int len=nums.size();
        if(len==0)return {};
        if(len==1)return {0};
        
        index.resize(len);
        helper.resize(len);
        count.resize(len);
        
        for(int i=0;i<len;i++)
        {
            index[i]=i;
            count[i]=0;
        }
        
        //merge
        merge(nums,0,len/2,len);
        
        return count;
    }
};

4、摆动排序 II

给定一个无序的数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]… 的顺序。
要求:用 O(n) 时间复杂度和 / 或原地 O(1) 额外空间来实现。
解答:快速选择 + 3-way-partition + 虚地址
https://leetcode-cn.com/problems/wiggle-sort-ii/solution/yi-bu-yi-bu-jiang-shi-jian-fu-za-du-cong-onlognjia/

class Solution {
public:
    void wiggleSort(vector<int>& nums) {
        int n = nums.size();

        // Find a median.
        auto midptr = nums.begin() + n / 2;
        nth_element(nums.begin(), midptr, nums.end());
        int mid = *midptr;

        // Index-rewiring.
        #define A(i) nums[(1+2*(i)) % (n|1)]

        // 3-way-partition-to-wiggly in O(n) time with O(1) space.
        int i = 0, j = 0, k = n - 1;
        while (j <= k) {
            if (A(j) > mid)
                swap(A(i++), A(j++));
            else if (A(j) < mid)
                swap(A(j), A(k--));
            else
                j++;
        }
    }
};

5、最大数

给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数。输出结果可能非常大,所以你需要返回一个字符串而不是整数。
解答:下面是在题解里找到的两个很厉害的答案。
方法一:一套STL走天下,tql

class Solution {
public:
    string largestNumber(vector<int>& nums) 
    {
        if (all_of(nums.begin(), nums.end(), [](int x) { return x == 0; })) {
            return string("0");
        }
        vector<string> strNums(nums.size());
        std::transform(nums.begin(), nums.end(), strNums.begin(), [](int x) {
            return std::to_string(x);
        });

        std::sort(strNums.begin(), strNums.end(), [](const string& x, const string& y) {
            /* x为后面元素,y为前面元素,return true则将x移动到前面 */
            return x + y > y + x;
        });

        return std::accumulate(strNums.begin(), strNums.end(), string());
    }
};

方法二:实现一个自定义排序。
将输入的数组按照某个顺序排序,然后按顺序组合就是最大数。
这个顺序的目的是实现组合后较大的数字在前面。
所以排序的比较算法应该是对组合后的数字进行比较。
通过计算进行比较的两个数字的位数,交叉相乘再相加(a * b的位数 + b),实现将两个数字组合到一起。
链接:https://leetcode-cn.com/problems/largest-number/solution/179-by-ikaruga/

class Solution {
public:
    string largestNumber(vector<int>& nums) 
    {
        auto f_sort = [](const int &a, const int &b)
        {
            long long n_a = 10;
            while (a / n_a) n_a *= 10;
            long long n_b = 10;
            while (b / n_b) n_b *= 10;
            long long r_a = (long long)a * n_b + (long long)b;
            long long r_b = (long long)b * n_a + (long long)a;
            return r_a < r_b;
        };

        sort(nums.rbegin(), nums.rend(), f_sort);

        string ans;
        for (auto n : nums)
        {
            ans = (ans == "0") ? to_string(n) : ans + to_string(n);
        }
        return ans;
    }

};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值