问题描述:
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
思路:
通过排序,将n个数变为有序序列后再输出前k个数。以下实现了七种排序方法:
#include<iostream>
#include<vector>
using namespace std;
/*对于排序算法,排序前后,两个相等的数相对位置不变,则算法稳定;
不稳定指的是排序前后两个相等的数相对位置发生了变化*/
class Solution {
public:
/*在C++里很多时候我们会遇到函数想返回两个以上结果的情况,这时候可以用数组(vector)、类来作为容器返回,也可以声明一个全局变量的数组,将数值存放在数组里解决。
第一个方式是使用引用来解决,将vector的引用在函数间传递,对于input这个vector进行排序,需要传递引用,这样被调函数中排完序的数组才能和调用函数共用*/
/*快速排序:平均时间复杂度o(nlogn),不稳定
通过一趟排序将序列分为左右两部分,其中左半部分的值均比右半部分的值小
然后分别对左右部分的记录进行排序,直到整个序列有序
*/
int partition(vector<int> &a, int low, int high) {
int key = a[low]; //初始以低位上的数字作为分割线分割出左右两部分
while (low<high) {
while (low<high&&a[high] >= key) high--; //从最右端依次判断右端元素是否大于等于分割数字,如果大于等于,high--
a[low] = a[high]; //当a[high]<key时,这个值属于左端
while (low<high&&a[low] <= key) low++;
a[high] = a[low];
}
a[low] = key; //key应位于最终low的位置上
return low;
}
void quick_sort(vector<int> &a, int low, int high) {
if (low >= high) return;
int keypos = partition(a, low, high);
quick_sort(a, low, keypos - 1);
quick_sort(a, keypos + 1, high);
}
/*冒泡排序:平均时间复杂度o(n^2),稳定
比较相邻的元素,如果第一个比第二个大,就交换他们两个
针对所有元素重复以上步骤,除了最后一个*/
void bubble_sort(vector<int> &a) {
for (int i = 0; i < a.size()-1; i++) {
for (int j = 0; j < a.size() - 1 - i; j++) {
if (a[j] > a[j + 1]) {
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
}
/*插入排序:平均时间复杂度o(n^2),稳定
将一个记录插入到已排序的有序表中,得到一个新的,记录数增1的有序表
*/
void insert_sort(vector<int> &a) {
for (int i = 1; i < a.size(); i++) {
int key = a[i]; //当前值
int j = i - 1;
while (j >= 0 && a[j] > key) { //当已排好序的元素大于当前要插入的元素,就进行后移,把a[j]的值移到a[j+1]
a[j + 1] = a[j];
j--;
}
a[j + 1] = key; //当不满足上述条件跳出while循环后证明a[j]<key,所以a[j+1]就是最终key所在的位置
}
}
/*shell排序:平均时间复杂度o(n^1.3),不稳定
希尔排序是插入排序的一种高效实现,也叫缩小增量排序。
先将整个待排记录序列分割为若干子序列分别进行直接插入排序,待整个序列中的记录基本有序时再对全体记录进行一次直接插入排序
将相隔某个“增量”的记录组成一个子序列
*/
void shell_insert(vector<int> &a, int d) {
for (int i = d; i < a.size(); i++) {
int key = a[i];
int j = i - d;
while (j >= 0 && a[j] > key) {
a[j + d] = a[j];
j = j - d;
}
a[j + d] = key;
}
}
void shell_sort(vector<int> &a) {
int d = a.size() / 2; //间隔值
while (d >= 1) {
shell_insert(a, d);
d = d / 2;
}
}
/*选择排序:平均时间复杂度o(n^2),不稳定
选择排序每次在确定了最小数的下标之后再进行交换,减少交换次数
*/
void select_sort(vector<int> &a) {
for (int i = 0; i < a.size() - 1; i++) {
int minIndex = i;
for (int j = i; j < a.size(); j++) {
if (a[j] < a[minIndex]) minIndex = j;
}
if (minIndex != i) {
int temp = a[i];
a[i] = a[minIndex];
a[minIndex] = temp;
}
}
}
/*堆排序:平均时间复杂度o(nlogn),不稳定
堆是完全二叉树中所有非终端结点的值均不大于(或不小于)其左、右孩子结点的值。因此,若一个序列是堆,则堆顶元素必为序列中n个元素的最小值(或最大值)
堆排序需要解决的两个问题:(1)如何由一个无序序列建成一个堆?(2)如何在输出堆顶元素之后,调整剩余元素成为一个新的堆?
假设序列有n个元素,先将这n个元素建成大顶堆,然后取堆顶元素,与序列第n个元素交换,然后调整前n-1个元素,使其重新成为堆
*/
/*当数组下标从0开始时,结点下标为i时,其左节点为2*i+1,右节点为2*i+2 */
void heapAdjust(vector<int> &a, int s, int m) {
int key = a[s];
for (int j = 2 * s + 1; j <= m; j = 2 * j + 1) { //从当前位置s的左节点开始
if (j < m && a[j] <= a[j + 1]) {
j++; //当j不超过数组长度时,找到左右节点中较大的那个
}
if (a[j] < key) break; //若较大的那个结点还是小于父节点,跳出for循环
a[s] = a[j];
s = j; //否则父节点与子结点交换
}
a[s] = key;
}
void heap_sort(vector<int> &a) {
//初始建堆,从第一个非叶子结点开始,建好堆后堆顶为最大元素
for (int i = a.size() / 2 - 1; i >= 0; i--) {
heapAdjust(a, i, a.size() - 1);
}
//取堆顶元素,并且调整
for (int i = a.size()-1; i >= 0; i--) {
int temp = a[i];
a[i] = a[0];
a[0] = temp;
heapAdjust(a,0,i-1);
}
}
/*归并排序:平均时间复杂度o(nlogn),稳定
把有序表划分成元素个数尽量相等的两半
把两半元素分别排序
把两个有序表合并成一个
*/
void merge(vector<int> &a, int first, int mid, int last) {
vector<int> temp(last - first + 1); //定义一个辅助数组
int copyIndex = last - first; //开始复制的位置
int i = mid, j = last;
while (i >= first && j >= mid + 1) {
if (a[i] > a[j]) temp[copyIndex--] = a[i--];
else temp[copyIndex--] = a[j--];
}
while (i >= first) temp[copyIndex--] = a[i--];
while (j >= mid + 1) temp[copyIndex--] = a[j--];
for (int k = first; k <= last; k++) {
a[k] = temp[k-first];
}
}
void merge_sort(vector<int> &a,int first,int last) {
if (first >= last) return;
int mid = (first + last) / 2;
merge_sort(a, first, mid);
merge_sort(a, mid + 1, last);
merge(a, first, mid, last);
}
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> vk;
if (input.size() == 0 || k>input.size()) {
return vk;
}
//quick_sort(input, 0, input.size() - 1);
//bubble_sort(input);
//insert_sort(input);
//shell_sort(input);
//select_sort(input);
//heap_sort(input);
merge_sort(input, 0, input.size() - 1);
for (int i = 0; i<input.size(); i++) {
cout << input[i] << " ";
}
cout << endl;
for (int i = 0; i<k; i++) {
vk.push_back(input[i]);
}
return vk;
}
};
int main() {
vector<int> v = { 4,5,1,6,2,7,3,0 };
Solution s;
vector<int> result;
result=s.GetLeastNumbers_Solution(v, 4);
for (int i = 0; i < 4; i++) {
cout << result[i] << " ";
}
return 0;
}