案例1(以后再看,对堆排序了解不足)
已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离不超过k,并且k相对于数组长度来说很小。问选什么方法对其排序比较好。
分析:时间复杂度从小到大分析
复杂度O(N)的排序算法:
计数排序、基数排序
不基于比较的排序算法的限制:
不适用所有情况
复杂度O(N^2)的排序算法:
冒泡排序、选择排序
这两个排序算法与数据原始序列无关,时间复杂度严格符合O(N^2)
插入排序
插入排序的过程与原始顺序有关
每个元素移动距离不超过k
对本题来说,插入排序O(N*k)
复杂度O(N*logN)的排序算法:
快速排序
与数据原始序列无关
归并排序
与数据原始序列无关
这题答案:改进后的推排序
每个元素移动距离不超过k
所以最小值位置
堆顶是最小值,放在数组首位。
数组加入一个数,插入堆顶。
递归完成,数组排序完毕。
每调整一个数O(logK),总共N个数
总的为O(N*logK)
案例2(以后再看,对堆排序了解不足)
判断数组中是否有重复值。必须保证额外空间复杂度为O(1)。
分析:若无空间复杂度限制,用哈希表实现。哈希表实现,时间复杂度为O(N),空间复杂度为O(N)。
若有限制,先排序,再判断。所以此题是考察经典算法的空间复杂度限制。
选择堆排序
推排序经典实现使用了递归的方式(函数栈),但这样,其空间复杂度为O(logN)
所以答案需要改写出一个非递归的排序。
案例3
把两个有序数组合并为一个数组。第一个数组空间正好可以容纳两个数组的元素。
A:2 4 6 空 空 空
B:1 3 5
6和5比,6大,6放最后;
4和5比,5大,5放倒数第二个位置
依次。
直到完成排序。
该题核心:从后向前覆盖数组A,保证数组A有用信息不会被覆盖掉
#include<iostream>
#include<vector>
using namespace std;
void out(vector<int> arr,int n){ //数组输出
for(int k=0;k<n;k++){
cout<<"arr["<<k<<"]:"<<arr[k]<<endl;
}
}
void sort(vector<int> &arr, int length,vector<int> &arr2, int length2){
for(int l=1;l<=length2;l++){//数组增大
arr.push_back(0);
}
int k=length+length2-1;//总长度
int i=length-1; //arr长度
int j=length2-1; //arr2长度
while(i>=0&&j>=0){
if(arr[i]>= arr2[j]){
arr[k] = arr[i];
i--;
k--;
}
else{
arr[k] = arr2[j];
j--;
k--;
}
}
while(i>=0){ //若arr还没扫描完,将其全部复制到合并序列
arr[k] = arr[i];
i--;
k--;
}
while(j>=0){ //若arr2还没扫描完,将其全部复制到合并序列
arr[k]=arr2[j];
j--;
k--;
}
}
int main(){
int num;
vector<int>arr;
cout<<"请输入第一个数组"<<endl;
int n = 0;//n表示输入个数
while (cin >> num){
arr.push_back(num);
n++;
if(cin.get() == '\n') //遇到回车,终止
break;
}
int num2;
vector<int>arr2;
cout<<"请输入第二个数组"<<endl;
int n2 = 0;//n2表示输入个数
while (cin >> num2){
arr2.push_back(num2);
n2++;
if(cin.get() == '\n') //遇到回车,终止
break;
}
sort(arr,n,arr2,n2); //排序
out(arr,n+n2); //输出数组
}
代码注:方法源自《排序(1)》中的归并排序。