前个做leetcode的打卡题
https://leetcode-cn.com/problems/sort-an-array/comments/
就一道排序题。
给你一个整数数组 nums,请你将该数组升序排列。
示例:
输入:nums = [5,2,3,1]
输出:[1,2,3,5]
本来我是sort(nums.begin(),nums.end());后来觉得这样似乎不太好,太对不起人家这道题了,寝食难安啊。
于是费了一会时间写了这几个排序方法。
//直接插入
void InsertSort(vector<int>& nums,int n){
for(int i=0;i<n;++i){
int temp=nums[i]; //temp为监视哨
int j=i-1;
while(j>=0 && nums[j]>temp){
nums[j+1]=nums[j];
--j;
}
nums[j+1]=temp;
}
}
//时间复杂度O(n^2),空间复杂度O(1),是稳定的排序方法.
//选择排序
void SelectSort(vector<int>& nums,int n){
for(int i=0;i<n-1;++i){
int min=i;
for(int j=i+1;j<n;++j){
if(nums[j]<nums[min]) min=j;
}
if(min!=i) swap(nums[i],nums[min]);
}
}
//时间复杂度O(n^2),空间复杂度O(1),不是稳定的排序方法.
//冒泡排序
void BubbleSort(vector<int>& nums,int n){
for(int i=0;i<n-1;++i){
bool flag=true; //设立标志
for(int j=0;j<n-i-1;++j){
if(nums[j+1]<nums[j]){
flag=false;
swap(nums[j],nums[j+1]);
}
}
if(flag) return; //就是说如果一趟排序没有发生交换,就证明已经有序了.
}
}
//或者也可以这样写,上面是把大数放下面,下面是把小数放上面,其本质都是一样的。
void BubbleSort2(vector<int>& nums,int n){
for(int i=0;i<n-1;i++) {
bool flag = true;
for(int j=n-1;j>i;j--) {
if(nums[j-1]>nums[j]){
swap(nums[j-1],nums[j]);
flag = false;
}
}
if(flag) return;
}
}
//时间复杂度O(n^2),空间复杂度O(1),是稳定的排序方法.
//插入法的改进版本:折半插入
//改进思想:由于关键字之间的比较以及记录的移动是直接插入排序算法最基本的两个操作,
//所以改进必须从减少这两种操作的次数着手.
void BinInsertSort(vector<int>& nums,int n){
int low,high,mid;
for(int i=0;i<n;++i){
int temp=nums[i];
low=0;
high=i-1;
while(low<=high){
mid=(low+high)/2;
if(nums[mid]>temp){
high=mid-1;
}
else{
low=mid+1;
}
}
for(int j=i-1;j>=high+1;--j){
nums[j+1]=nums[j]; //记录右移
}
nums[high+1]=temp; //插入记录
}
}
//时间复杂度O(n^2),空间复杂度O(1),是稳定的排序方法. 和直接插入的不同在于比较次数较少,记录移动次数是相同的.
//希尔排序也是插入排序的一种改进方法;
//依据: 待排序记录很少时,直接插入排序运行效率很高; 待排序记录按关键字基本有序时,直接插入的时间复杂度可达到O(n)数量级。
//希尔排序正是基于直接插入排序的这两点特征对它进行改进的.
//思想:
//1.先将待排序记录序列按指定的增量间隔分割成若干子序列分别进行直接插入排序;
//2.不断缩小增量间隔,重复以上操作;
//3.待整个序列中记录基本有序时,最后再对全体记录进行一次直接插入排序。
//特点:相隔某个增量的记录组成一个子序列。
void ShellInsertSort(vector<int>& nums,int n){
for(int dk=n/2;dk>=1;dk/=2){
for(int i=dk;i<n;++i){
if(nums[i]<nums[i-dk]){
int temp=nums[i];
int j;
for(j=i-dk;j>=0 && temp<nums[j];j-=dk){
nums[j+dk]=nums[j];
}
nums[j+dk]=temp;
}
}
}
}
//空间复杂度O(1),是不稳定的排序方法,时间复杂度取决于增量序列,是增量和记录数的函数。
//注意:增量序列中的值应没有除1以外的公因子,且最后一个增量必须是1。
//快速排序:是对冒泡排序的改进,又称划分交换排序。
//基本思想:在待排序序列中任选一个记录作为中轴,通过一趟排序将待排序记录分割成独立的两部分,其中前一部分记录的关键字均小于等于中轴的关键字,
//后一部分的关键字都大于等于中轴的关键字。
//分别对这两个子序列按照同样方法再次进行快速排序,直到分隔出来的每个子序列只包含一个记录为止。
//此时序列达到有序。
void QuickSort(vector<int> &nums,int low,int high){ //调用时 QuickSort(nums,0,n-1);
if(low<high){
int pivotloc=Partition(nums,low,high);
QuickSort(nums,low,pivotloc-1);
QuickSort(nums,pivotloc+1,high);
}
}
int Partition(vector<int> &nums, int low, int high){
int temp=nums[low];
while(low<high){
while(low<high && nums[high]>=temp) --high;
nums[low]=nums[high];
while(low<high && nums[low]<=temp) ++low;
nums[high]=nums[low];
}
nums[low]=temp;
return low;
}
//时间复杂度:O(nlogn),就平均时间而言,排序性能最好。若经过每一趟快速排序得到的两个子序列基本相等,时间效率最高。
//若初始关键字有序或基本有序(正序或倒序),此时快速排序退化为冒泡排序。
//空间复杂度:O(logn),需要一个栈空间。
//是不稳定的排序方法。
//归并排序:归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。
//将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
void MergeSort(vector<int> &nums,int low,int high){ //调用时 MergeSort(nums,0,n-1);
if (low<high)
{
int mid = (low+high)/2;
MergeSort(nums, low, mid);
MergeSort(nums, mid+1, high);
Merge(nums, low, mid, high);
}
}
void Merge(vector<int>& nums,int low,int mid,int high){
int l=low, m=mid+1, h=high;
vector<int> temp(h-l+1,0);
int k=0;
while(l<=mid && m<=h){
if(nums[l]<nums[m]){
temp[k++]=nums[l++];
}
else{
temp[k++]=nums[m++];
}
}
while(l<=mid){
temp[k++]=nums[l++];
}
while(m<=h){
temp[k++]=nums[m++];
}
for(int i=0;i<temp.size();++i){
nums[i+low]=temp[i];
}
}
//时间复杂度O(nlogn),空间复杂度O(n),是稳定的排序方法。
//先写到这…