力扣刷题笔记26——最小的k个数/快速排序学习/快排与冒泡的时间复杂度

最小的k个数/快速排序学习/快排与冒泡的时间复杂度

  • 问题
  • 我的代码
  • 示例代码
  • 快速排序代码

问题

来自力扣:

输入整数数组 arr ,找出其中最小的 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]
 

限制:
0 <= k <= arr.length <= 10000
0 <= arr[i] <= 10000

我的代码


#include 
using namespace std;
#include 
#include 
#include
#include 
#include 
#include
#include
#include
#include
#include

class Solution {
public:
	vector<int> getLeastNumbers(vector<int>& arr, int k) {
		if (arr.size() == 0 || k == 0) {
			//vector tmp;
			return {};
		}
		int nsize = arr.size();
		sort(arr.begin(), arr.end());
		vector<int> tmp;
		for (int i = 0; i < k; ++i) {
			tmp.push_back(arr[i]);
		}
		return tmp;
	}
};
int main() {
	Solution myso;
	vector<int> arr = {1,2,3};
	vector<int> nums = myso.getLeastNumbers(arr,1);
	for (auto num : nums) {
		cout << num<<"\t";
	}

	return 0;
}

做法:先排序,后输出。我本来自己用冒泡排序实现排序的,但是提交后报错了,说超时。可能是因为冒泡排序是O( N 2 N^2 N2)的时间复杂度。用了sort函数后,就没有报错。查了下,好像复杂度低,只有O( N l o g 2 N Nlog_2N Nlog2N)。

示例代码

class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        const int N=10005;
        vector<int> cnt(N);
        int mmax=-1;
        for(auto num:arr){
            cnt[num]+=1;
            mmax=max(mmax, num);
        }
        cnt.resize(mmax+1);
        vector<int> ans;
        ans.reserve(k);
        for(int i=0; k&&i<=N; i++){
            while(cnt[i]>0&&k){
                ans.push_back(i);
                k--;
                cnt[i]--;
            }
        }
        return ans;
    }
};

这个代码其实有点投机取巧,题目说值不会超过10000,所以它用了个数组,记录每种值出现了几次。然后把小的数值存进一个ans数组里,作为输出。这么做的好处就是,只需要对原数组进行一次循环遍历。坏处就是内存消耗较大。

快速排序代码

快速排序的思想:选定一个值p,然后把小于p的放v1,大于p的放v2。这样就得到了两个子数组v1和v2,其中v1的元素值都是小于v2。对v1再进行选值和排序,可以得到v1_1和v1_2。一直这样划分下去,就可以实现排序。
时间复杂度的证明看这个:快速排序时间复杂度分析
知乎有人给出了一个表格:
力扣刷题笔记26——最小的k个数/快速排序学习/快排与冒泡的时间复杂度_第1张图片
对于这道题,代码为这个:

class Solution {
    int partition(vector<int>& nums, int l, int r) {
        int pivot = nums[r];
        int i = l - 1;
        for (int j = l; j <= r - 1; ++j) {
            if (nums[j] <= pivot) {
                i = i + 1;
                swap(nums[i], nums[j]);
            }
        }
        swap(nums[i + 1], nums[r]);
        return i + 1;
    }

    // 基于随机的划分
    int randomized_partition(vector<int>& nums, int l, int r) {
        int i = rand() % (r - l + 1) + l;
        swap(nums[r], nums[i]);
        return partition(nums, l, r);
    }

    void randomized_selected(vector<int>& arr, int l, int r, int k) {
        if (l >= r) {
            return;
        }
        int pos = randomized_partition(arr, l, r);
        int num = pos - l + 1;
        if (k == num) {
            return;
        } else if (k < num) {
            randomized_selected(arr, l, pos - 1, k);
        } else {
            randomized_selected(arr, pos + 1, r, k - num);
        }
    }

public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        srand((unsigned)time(NULL));
        randomized_selected(arr, 0, (int)arr.size() - 1, k);
        vector<int> vec;
        for (int i = 0; i < k; ++i) {
            vec.push_back(arr[i]);
        }
        return vec;
    }
};

代码解析

  1. 它每次选择的p都是这段数组的最后一个值。分成v1和v2后,会返回一个位置索引pos,告知p的位置。
  2. 如果pos刚好等于k,说明v1刚好就是k个最小。
  3. 如果pos小于k,说明v1的个数小于k个,还得从v2中找出k-pos个。所以对v2进行快速排序,找出k-pos个。
  4. 如果pos大于k,说明v1的个数大于k个,得从v1中找出较小的k个。所以对v1进行快速排序,找出k个。

你可能感兴趣的:(算法题,leetcode,学习,算法,排序算法)