排序--JZ29、63、32、面试题51、61

JZ29 最小的k个数

题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。

示例 1:

输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
示例 2:

输入:arr = [0,1,2,1], k = 1
输出:[0]

题解:

方法一:排序(需改变输入数组)
直接排序,然后取前k小数据。

class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        //1、
        vector<int> ret;
        if (k==0 || k>arr.size()) return ret;
        sort(arr.begin(), arr.end());
        for (int i = 0; i < k; ++i) {
            //2、
            ret.push_back(arr[i]);
        }
        return ret;
    }
};
//注意1、2、两处,
若vector<int> ret,这只是定义,ret是空的,需用ret.push_back()来添加元素
若vector<int> ret(k)或vector<int> ret(k,0,其执行了初始化,ret开始就有K个0,其添加元素需用下标形式,即ret[i]=arr[i],若用ret.push_back()是在0后继续添加。

时间复杂度:O(nlongn),其中 n是数组 arr 的长度。算法的时间复杂度即排序的时间复杂度。
空间复杂度:O(logn),排序所需额外的空间复杂度为O(logn)。

方法二:堆排序

方法一中决定时间复杂度的因素是sort()函数使用的算法的时间复杂度就是O(nlongn),这在目前的排序算法中已经是最快的了,所以若想更快些,需借助数据结构来实现。

大根堆大顶堆)是由完全二叉树实现的一个数据结构,其重建一次堆的时间是O(logk),k是堆中结点的数量。
大根堆的特点是每个结点的值都大于或等于其左右孩子节点的值。也就是堆顶(根节点)值最大。

具体步骤:
我们用一个大根堆实时维护数组的前 k小值。
首先将前 k 个数插入大根堆中(没插入一个数,大根堆就重建一次,重建一次用时O(logk)),
随后从第 k+1个数开始遍历,如果当前遍历到的数比大根堆的堆顶的数要小,就把堆顶的数弹出,再插入当前遍历到的数。
最后将大根堆里的数存入数组返回即可。
C++ 语言中的堆(默认是大根堆)由优先队列priority_queue表示
Java 中提供了现成的 PriorityQueue(默认小根堆),默认是小根堆,实现大根堆需要重写一下比较器。PriorityQueue<Integer> pq = new PriorityQueue<>((v1 , v2) -> v2 - v1);

代码:

class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        vector<int> ret;
        if (k==0 || k>arr.size()) return ret;
        priority_queue<int> pq;
        for(int i=0;i<k;i++){
            pq.push(arr[i]);
        }
        for(int i=k;i<arr.size();i++){
            if(arr[i]<pq.top()){
                pq.pop();
                pq.push(arr[i]);
            }
        }
        for(int i=0;i<k;i++){
            ret.push_back(pq.top());
            pq.pop();
        }
        return ret;

    }
};

Java

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        if(k == 0 || arr.length == 0 || arr.length < k) return new int[0]; 
        PriorityQueue<Integer> pq = new PriorityQueue<>((v1 , v2) -> v2 - v1);
        for(int val : arr){
            if(pq.size() < k){
                pq.offer(val);
            }else{
                if(val < pq.peek()){
                    pq.poll();
                    pq.offer(val);
                }
            }
        }

        int[] res = new int[k];
        for(int i = 0; i < k; i++){
            res[i] = pq.poll();
        }
        return res;

    }
}

时间复杂度:O(nlogk),其中 n是数组 arr 的长度。由于大根堆实时维护前 k小值,所以插入删除都是 O(logk) 的时间复杂度,最坏情况下数组里 n个数都会插入,所以一共需要 O(nlogk) 的时间复杂度。

空间复杂度:O(k),因为大根堆里最多 k 个数。

方法三:快排思想(需改变输入数组)(该方法受快排启发,不同于快排每次排序左右两侧,它每次排一侧,时间复杂度为O(n))

注意找前 K 大/前 K 小问题不需要对整个数组进行 O(NlogN) 的排序,每次对符合要求的一边排序即可

由于我们是要找到最小的k个数,可以想象,若将数组划分成前k-1个数小于第k个数,k后面的数大于第k个数,则前k个数就是小的k个数(这k个数不一定是排序的)。这自然就想到了快速排序

我们可以借鉴快速排序的思想。我们知道快排的划分函数每次执行完后都能将数组分成两个部分,小于等于分界值 pivotKey 的元素的都会被放到数组的左边,大于的都会被放到数组的右边,然后返回分界值的下标 pivot。与快速排序不同的是,快速排序会根据分界值的下标递归处理划分的两侧,而这里我们只处理划分的一边

我们定义函数 select_k(arr, l, r, k) 表示划分数组 arr 的 [l,r] 部分,使前 k 小的数在数组的左侧,在函数里我们调用快排的划分函数,假设划分函数返回的下标是 pivot(表示分界值 pivotKey 最终在数组中的位置),即pivotKey是数组中第pivot - l + 1 小的数,那么一共会有三种情况:

1、如果 pivot- l + 1 == k,表示pivotKey 就是第 k小的数,直接返回即可;

2、如果 pivot - l + 1 < k,表示第 k 小的数在 pivotKey 的右侧,因此递归调用 select_k(arr, pivot + 1, r, k - (pivot - l + 1));

3、如果 pivot - l + 1 > k,表示第 k小的数在 pivotKey的左侧,递归调用select_k(arr, l, pivot - 1, k)。

函数递归入口为 randomized_selected(arr, 0, arr.length - 1, k)。在函数返回后,将前 k 个数放入答案数组返回即可。

具体步骤:
由上面分析得这是一个递归过程,则递归三部曲为:

1、递归函数功能(原问题):
select_k(arr, l, r, k) 表示划分数组 arr 的 [l,r] 部分,使前 k 小的数在数组的左侧

2、递归终止条件
if (l >= r) return; 如果越界,返回
if (k == num) return; 如果划分出来的值是第k小的数,返回。
3、下一步递归(子问题):

如果 pivot - l + 1 < k,select_k(arr, pivot + 1, r, k - (pivot - l + 1));

如果 pivot - l + 1 > k,select_k(arr, l, pivot - 1, k)。

代码:

class Solution {
    // 基于三数取中的划分
    int partition(vector<int>& nums, int l, int r) {
        int m=l+(r-l)/2;
        if(nums[l]>nums[r]) swap(nums[l],nums[r]);
        if(nums[m]>nums[r]) swap(nums[m],nums[r]);
        if(nums[m]>nums[l]) swap(nums[m],nums[l]);
        int pivotKey=nums[l];
        while(l<r){
            while(l<r&&nums[r]>=pivotKey){
                r--;
            }
            swap(nums[r],nums[l]);
            while(l<r&&nums[l]<=pivotKey){
                l++;
            }
            swap(nums[r],nums[l]);

        }
        return l;
    }
    void select_k(vector<int>& arr, int l, int r, int k) {
        if (l >= r) {
            return;
        }
        int pivote = partition(arr, l, r);
        int num = pivote - l + 1;
        if (k == num) {
            return;
        } else if (k < num) {
            select_k(arr, l, pivote - 1, k);
        } else {
            select_k(arr, pivote + 1, r, k - num);
        }
    }
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        select_k(arr, 0, (int)arr.size() - 1, k);//递归入口
        vector<int> vec;
        for (int i = 0; i < k; ++i) {
            vec.push_back(arr[i]);
        }
        return vec;
    }
};


Java

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        if(k == 0 || arr.length == 0 || arr.length < k) return new int[0]; 
        int[] res = new int[k];
        qSort(arr , 0 , arr.length - 1 , k);
        for(int i = 0 ; i < k ; i++){
            res[i] = arr[i];
        }
        return res;

    }

    void qSort(int[] arr , int low , int high , int k){
            int pivot;//pivot:枢轴
            if(low < high){
                pivot = partition(arr , low , high);//partition:划分
                int num = pivot - low + 1;
                if(k == num) return;
                else if(k < num) qSort(arr , low , pivot - 1 , k);
                else qSort(arr , pivot + 1 , high , k - num);
            }
    }


     int partition(int[] arr , int low , int high){
            int pivotkey;//pivotkey枢轴值
            //三数取中确定pivotkey
            //先是low和mid分别与high比较,将最大值放在high,然后比较较小的low和mid,使中间值处于low.
            int mid = low + (high - low);//不用(high + low)/2是防止溢出;
            if(arr[low] > arr[high]) swap(arr , low , high);
            if(arr[mid] > arr[high]) swap(arr , mid , high);
            if(arr[mid] > arr[low]) swap(arr , mid , low);
            pivotkey = arr[low];
            while(low < high){
                while(low < high && arr[high] >= pivotkey) high--;
                swap(arr , low ,high);
                while(low < high && arr[low] <= pivotkey) low++;
                swap(arr , low ,high);
            }
            return low;
        }

        void swap(int[] arr , int low , int high){
            int temp = arr[low];
            arr[low] = arr[high];
            arr[high] = temp;
        }
}

时间复杂度:O(n),

空间复杂度:O(logn)。

JZ63 数据流中的中位数

题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

题解:
首先想到要得到一组数的中位数,先对这些数排序,再取中位数就简单了。
由于数据是从数据流中读取出来的,所以数据是随时间增加的不是一次固定的。所以从读取的数据流中获取中位数可从两个方面展开(对应要实现的两个函数):
1、Insert()中不排序:--------方法一
读取的数据流直接存储到数组,不排序,在GetMedian()中实现排序再取中位数;
2、Insert()中排序:--------方法二、三
读取的数据流时边读边排序,这样GetMedian()可直接取中位数

显然Insert()中排序好,因为这样每一次读入新数据都是再有序的数组的基础上排序,而Insert()中不排序,每次GetMedian()都会对没排序的数据重新排序。
方法一:暴力方法
对于读取数据流Insert(),用vector<int> v来实现;
对于得到中位数GetMedian(),对读取的数据vector<int> v先排序,再取中间的数;

代码:

class Solution {
public:
    vector<int> v;
    void Insert(int num)
    {
        v.push_back(num);
    }

    double GetMedian()
    { 
        sort(v.begin(), v.end());
        if(v.size()&1)//用逻辑运算符判断奇偶比算术运算符快
            return v[v.size()/2]*1.0;//乘1.0是因为返回的是double类型;
        else return (v[v.size()/2]+v[v.size()/2-1])/2.0;//除2.0是因为返回的是double类型
    
    }

};

时间复杂度:Insert()为O(1), GetMedian()为O(nlogn)
空间复杂度:O(n)

方法二:插入排序

对于方法一,可以发现有个优化的地方。
方法一中GetMEdian()操作,是每次都对整个vector调用排序操作。
但是其实每次都是在一个有序数组中插入一个数据。因此可以用插入排序。
所以:

Insert()操作可改为插入排序
GetMedian()操作可直接从有序数组中获取中位数

代码:

class Solution {
public:
    vector<int> v;
    void Insert(int num)
    {
        if(v.empty()) v.push_back(num);
        else{
            auto location=lower_bound(v.begin(), v.end(), num);
            v.insert(location, num);
        }
    }

    double GetMedian()
    { 
        if(v.size()&1)
            return v[v.size()/2]*1.0;
        else return (v[v.size()/2]+v[v.size()/2-1])/2.0;
    
    }

};

时间复杂度:Insert()为O(n),即二分查找的O(logn)和挪动数据的O(n), GetMedian()为O(1)
空间复杂度:O(n)

:lower_bound() 和v.insert()的用法,见下面知识点

方法三:堆(左手一个大顶堆 + 右手一个小顶堆)
中位数是指:有序数组中中间的那个数(或两个树的平均)。则根据中位数可以把数组分为如下两段:
[0 … median ], [median+1 … arr.size() - 1],即[中位数的左边,中位数的右边]

我们没有必要把所有数据进行排序。只需要保证数据左半边的数都小于右半边的数,那么根据左半边的数的最大值及右半边的数的最小值即可得到中位数。在一组数中要在O(1)时间得到最大最小值,自然想到

步骤:
1、
建立一个大顶堆A(存储左半边)和一个小顶堆B(存储右半边),若数据的个数N是偶数:
A.size()=B.size()=N/2;
若数据的个数N是奇数,一半为N+1/2,一半为N-1/2:
令A.size()=N+1/2,B.size()=N-1/2【当然也可反过来】

2、Insert(num) 函数【从数据流中读取数据】:
-----插入新数据前,当 A.size()=B.size()=N/2(即 N 为 偶数,N为正在读取的数据插入前的值):需向 A 添加一个元素【向A中添加,是因为当N为奇数时我们令A.size()=N+1/2】。实现方法:将新元素 num 插入至 B ,再将 B 堆顶元素插入至 A ;【这么做可以保证小顶堆的所有数永远大于等于大顶堆的 top()。因为如果先将新元素插入A,就有A.size()=N+1/2,A的大小已满足不需要再改动了,但这样不能保证新元素加入后,小顶堆的所有数还大于等于大顶堆的 top()】

-----插入新数据前,当 A.size()不等于B.size()(即 N 为 奇数):需向 B 添加一个元素【向B中添加,是因为A.size()=N+1/2,B.size()=N-1/2,B比A少一下,所以要加入B在能保证,加入新元素后A.size()=B.size()】。实现方法:将新元素 num插入至 A ,再将 A 堆顶元素插入至 B ;【这么做可以保证大顶堆的所有数永远小于等于小顶堆的 top()。】

整个过程我们都动态地做到了平衡两个堆的 size(),即保证它们的 size() 最大只相差了 1

3、GetMedian()函数:

-----当 A.size()=B.size()=N/2( N 为 偶数):则中位数为 ( A 的堆顶元素 + B 的堆顶元素 )/2。
-----当 A.size()不等于B.size()( N 为 奇数):则中位数为 A 的堆顶元素。

代码:

class Solution {
public:
    priority_queue<int, vector<int>, less<int>> maxpq;
        priority_queue<int, vector<int>, greater<int>> minpq;
    void Insert(int num)
    {
        if(maxpq.size()==minpq.size()){
            minpq.push(num);
            maxpq.push(minpq.top());
            minpq.pop();
        }
        else{
            maxpq.push(num);
            minpq.push(maxpq.top());
            maxpq.pop();
        }
    }

    double GetMedian()
    { 
        int mid1=maxpq.top();
        int mid2=minpq.top();
        //注意:返回类型是double,而堆中存放的数据时int类型,所以要除以2.0,而不是2
        return maxpq.size()==minpq.size()? (mid1+mid2)/2.0: mid1;
    }

};

注意: GetMedian()的返回类型是double,而堆中存放的数据时int类型,所以要除以2.0,而不是2。

时间复杂度:
GetMedian()为O(1): 获取堆顶元素使用 O(1)时间;
Insert()为O(logn): 堆重建一次用时O(logN) ,每次插入新元素,两个堆的插入弹出操作共3次,即时间复杂度3O(logN) ,3可省略,即约为O(logN) 。
空间复杂度 O(N) : 其中 N 为数据流中的元素数量,大顶堆 A 和小顶堆 B 最多同时保存 N 个元素。

: C++中的堆的数据结构见下面知识点4

JZ32 把数组排成最小的数(定义新的排序规则)

题目描述
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

示例1
输入
[3,32,321]
返回值
"321323"

题解:
我们如果能针对“拼接”,定义一个排序规则,将数组内元素排序,然后按从小到大的顺序拼接起来,即可得到最小值;

首先,针对“拼接”要制定一个排序规则:两个元素m和n能拼接成数字mn和nm,【符号"<" , “>” , "="表示常规意义的数值大小关系,而文字的“大于” “小于” “等于” 是新定义的大小关系】
如果mn<nm,说明m拼接在前面才能使值小,定义m小于n;
如果mn>nm,说明n拼接在前面才能使值小,定义n小于m;
如果mn=nm,说明m拼接在前面后面一样大,定义m等于n;

接下来,考虑怎么去拼接数字
在这里插入图片描述

最后,怎么比较mn和nm的大小,因为在新制定的排序规则中,需要先对mn和nm比较才能判断m和n的大小。为了解决大数问题将m和n转换成了字符串,其拼接成的mn和nm自然也是字符串,字符串之间的比较直接用"<" , “>” , "="来操作,即可按字典序自动比较。

代码(一):使用C++内置函数sort,并更改其排序规则:

class Solution {
public:
    string PrintMinNumber(vector<int> numbers) {
        string ret;
        if(numbers.size()<=0) return ret;
        if(numbers.size()==1) return to_string(numbers[0]);
        vector<string> store;
        for(int val: numbers){
            store.push_back(to_string(val));
        }
        sort(store.begin(), store.end(), strCompare);
        for(string i:store){
            ret+=i;
        }
        return ret;
    }
    //加static的原因:类成员函数有隐藏的this指针,static 可以去this指针????????????????
    static bool strCompare(string& a, string& b){
        string add1=a+b;
        string add2=b+a;
        return add1<add2;
    }
};

代码(二):不使用内置函数,套用各种排序算法,更改其中的比较规则即可,本例以快速排序举例:

class Solution {
public:
    string minNumber(vector<int>& nums) {
        string ret;
        if(nums.size()<=0) return ret;
        if(nums.size()==1) return to_string(nums[0]);
        vector<string> store;
        for(int val: nums){
            store.push_back(to_string(val));
        }
        qsort(store, 0, nums.size()-1);
        for(string i:store){
            ret+=i;
        }
        return ret;

    }
    void qsort(vector<string>& store, int low, int high){
        int pivot;
        if(low<high){
            pivot=partition(store, low, high);
            qsort(store,low, pivot-1);
            qsort(store,pivot+1,high);
        }
    }
    int partition(vector<string>& store, int low, int high){
        //三数取中
        int m=(low+high)/2;
        if(store[low]+store[high]>store[high]+store[low]? true: false)
            swap(store[low],store[high]);
        if(store[m]+store[high]>store[high]+store[m]? true: false)
            swap(store[m],store[high]);
        if(store[m]+store[low]>store[low]+store[m]? true: false)
            swap(store[m],store[low]);
        string pivotKey=store[low];
        while(low<high){
            while(low<high&&store[high]+pivotKey>=pivotKey+store[high]? true: false){
                high--;
            }
            swap(store[low],store[high]);
            while(low<high&&store[low]+pivotKey<=pivotKey+store[low]? true: false){
                low++;
            }
            swap(store[low],store[high]);
        }
        return low;
    }
};

Java

class Solution {
    String[] arr;
    public String minNumber(int[] nums) {
        String res = "";
        arr = new String[nums.length];
        for(int i = 0; i < nums.length; i++){
            arr[i] = String.valueOf(nums[i]);
        }
        qsort(0 , arr.length - 1);
        for(String val : arr){
            res += val;
        }
        return res;

    }

    void qsort(int low , int high){
        int pivot;
        if(low < high){
            pivot = partition(low , high);
            qsort(low , pivot -1);
            qsort(pivot + 1 , high);
        }
    }

    int partition(int low , int high){
        int mid = low + (high - low) / 2;
        if((arr[low]+arr[high]).compareTo(arr[high]+arr[low]) > 0? true: false) swap(low , high);
        if((arr[mid]+arr[high]).compareTo(arr[high]+arr[mid]) > 0? true: false) swap(mid , high);
        if((arr[mid]+arr[low]).compareTo(arr[low]+arr[mid]) > 0? true: false) swap(low , mid);
        String pivotKey = arr[low];
        while(low < high){
            while(low < high && (arr[high] + pivotKey).compareTo(pivotKey + arr[high]) >= 0 ? true : false) high--;
            swap(low , high);
            while(low < high && (arr[low] + pivotKey).compareTo(pivotKey + arr[low]) <= 0 ? true : false) low++;
            swap(low , high);
        }
        return low; 
    }

    void swap(int low , int high){
        String temp = arr[low];
        arr[low] = arr[high];
        arr[high] = temp;
    }
}

时间复杂度 O(NlogN) : N 为最终返回值的字符数量( strs 列表的长度 ≤N );使用快排或内置函数的平均时间复杂度为 O(NlogN) ,最差为 O(N^2)。

空间复杂度 O(N) : 字符串列表 strs 占用线性大小的额外空间。

:sort的自定义排序规则见下方 知识点6.

面试题51 数组中的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

示例 1:

输入: [7,5,6,4]
输出: 5

题解:
1、本题其实就是在对一个数组进行归并排序的基础上,增加了一个统计逆序对数目的障眼法,其实还是归并排序。
2、如果了解归并排序的话,就会想到我们可以用分治的思想,将给定的 nums 先一分为二,统计左半部分的逆序对数目,再统计右半部分的逆序对数目,以及统计跨越左半部分和右半部分的逆序对数目,然后将三者相加即可。

具体参考视频详解

在这里插入图片描述

代码:

class Solution {
public:
    int reversePairs(vector<int>& nums) {
        int lenght=nums.size();
        if(lenght<=1) return 0;
        vector<int> numsCopy=nums;
        vector<int> temp(lenght);
        int count=mergeSort(numsCopy, temp, 0, lenght-1);
        return count;
    }
    int mergeSort(vector<int>& nums, vector<int>& temp, int left, int right){
        if(left==right){
            return 0;
        } 
        int mid=left+(right-left)/2;//不用 int mid=(left+right)/2是为了防止溢出;
        int leftCount=mergeSort(nums, temp, left, mid);
        int rightCount=mergeSort(nums, temp, mid+1, right);
        if(nums[mid] <= nums[mid+1]){
            return leftCount+rightCount;
        }
        int crossCount=merge(nums, temp, left, right, mid);
        return leftCount+rightCount+crossCount;
    }
    int merge(vector<int>& nums, vector<int>& temp, int left, int right, int mid){
        for(int i = left; i <= right; i++) temp[i] = nums[i];
        int i=left,j=mid+1,pos=left;
        int count=0;
        while(i<=mid&&j<=right){
            if(temp[i]<=temp[j]){
                nums[pos]=temp[i];
                i++;
                pos++;
            }
            else{
                nums[pos]=temp[j];
                count+=mid-i+1;
                j++;
                pos++;
            }
        }
        if(i<=mid){
            for(int k=i;k<=mid;k++){
                nums[pos]=temp[k];
                pos++;
            }
        }
        if(j<=right){
            for(int k=j;k<=right;k++){
                nums[pos]=temp[k];
                pos++;
            }
        }
        return count;    
    }
};

java:

class Solution {
    public int reversePairs(int[] nums) {
        if(nums.length <= 1) return 0;
        int[] temp = new int[nums.length];
        int res = Msort(nums , temp , 0 , nums.length - 1);
        return res;

    }

    int Msort(int[] nums , int[] temp , int low , int high){
        if(low == high) return 0;
        else{
            int mid  = low + (high - low) / 2;
            int leftCount = Msort(nums , temp , low , mid);
            int rightCount = Msort(nums , temp , mid + 1 , high);
            //注意这里不能是temp[mid] <= temp[mid + 1],因为向temp里添加元素是在下面Merge函数里,这里还没添加。所以要用num
            if(nums[mid] <= nums[mid + 1]){
                return leftCount + rightCount;
            }
            int crossCount = Merge(temp , nums , low , mid ,high);
            return leftCount + rightCount + crossCount;
        }
    }

    int Merge(int[] temp , int[] nums , int low , int mid , int high){
        for(int i = low ; i <= high ; i++){
            temp[i] = nums[i];
        }
        int count = 0;
        int i = low , j = mid + 1 , pos = low;
        for(; i <= mid && j <= high ; pos++){
            if(temp[i] <= temp[j]){
                nums[pos] = temp[i++];
            }else{
                nums[pos] = temp[j++];
                count += (mid - i + 1);
            }
        }

        if(i <= mid){
            for(int k = i ; k <= mid ; k++){
                nums[pos++] = temp[k];
            }
        }
        if(j <= high){
            for(int k = j ; k <= high ; k++){
                nums[pos++] = temp[k];
            }
        }
        return count;
    }
}

时间复杂度:同归并排序 O(nlogn)。
空间复杂度:同归并排序 O(n),因为归并排序需要用到一个临时数组O(n)和递推栈O(logn),共O(n+logn),因O(logn)和O(n)比可不计,所以空间复杂度为O(n)。

面试题61:扑克牌中的顺子

在这里插入图片描述

示例 1:

输入: [1,2,3,4,5]
输出: True
 

示例 2:

输入: [0,0,1,2,5]
输出: True

题解
抽象建模:

在这里插入图片描述
思路:
先将数组排序,排序后【除大小王(即,0)外】若有重复元素,则肯定不是顺子;若无重复元素,最大值和最小值的差值小于5,则是顺子,否则不是;

【代码参考
在这里插入图片描述

代码:

class Solution {
public:
    bool isStraight(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        for(int i=0; i<nums.size()-1; i++){
            if(nums[i]==0) continue;
            else if(nums[i]==nums[i+1]) return false;
        }
        int max=nums[nums.size()-1];
        int min;
        for(int i=0; i<nums.size(); i++){
            if(nums[i]==0) continue;
            min=nums[i]; break;
        }
        if((max-min)<5) return true;
        else return false;
    }
};

在这里插入图片描述

知识点

1、vector的添加元素
若vector ret,这只是定义,ret是空的,需用ret.push_back()来添加元素
若vector ret(k)或vector ret(k,0),其执行了初始化,ret开始就有K个0,其添加元素需用下标形式,即ret[i]=arr[i],若用ret.push_back()是在0后继续添加。

2、vector.insert()

insert() 函数有以下三种用法:
-----在指定位置loc插入值为val的元素,返回指向这个元素的迭代器

v.insert(loc, val)

-----指定位置loc前插入num个值为val的元素

v.insert(loc,num, val)

-----在指定位置loc前插入区间[start, end)的所有元素

3、堆

4、C++中的堆:优先队列priority_queue

5、lower_bound()和upper_bound( )
lower_bound( )和upper_bound( )都是利用二分查找的方法在一个排好序的数组中进行查找的。

在从小到大的排序数组中,

lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

6、sort
基本概况自定义排序规则

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值