冒泡排序
void BubbleSort(vector<int>& arr) {
int n = arr.size();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr[j], arr[j + 1]);
}
}
}
}
优化:
void bubbleSort(vector<int> &arr){
int len=arr.size();
for(int i=len-1;i>0;i--){//进行len-1次循环
bool didSwap=false;
for(int j=0;j<i;j++){//每次对arr[0,i-1]进行冒泡
if(arr[j]>arr[j+1]){
swap(arr[j],arr[j+1]);
didSwap=true;
}
}
if(!didSwap) return;
}
}
进一步优化:利用一个标志位,记录一下当前第 i 趟所交换的最后一个swap的下标,在进行第 i+1 趟的时候,只需要内循环到这个下标的位置就可以了,因为后面位置上的元素在上一趟中没有换位,这一次也不可能会换位置了。
时间复杂度:
最差:O(n²),最优:O(n)(优化后),平均O(n²);
空间复杂度:O(1);
稳定排序。
选择排序
void selectSort(vector<int> &arr){
int len=arr.size();
for(int i=0;i<len-1;i++){
int index=max_element(arr.begin(),arr.end()-i)-arr.begin();
swap(arr[index],arr[len-1-i]);
}
}
时间复杂度:
最优、最差、平均都是O(n²);
空间复杂度:O(1);
不稳定排序。
插入排序
基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。
输入敏感,输入数组越有序,效率越高。
void InsertSort(vector<int>& arr) {
int n = arr.size();
for (int i = 1; i < n; i++) {
int tmp = arr[i];
int j;
for (j = i - 1; j >= 0; j--) {
if (arr[i] > arr[i + 1]){
arr[i + 1] = arr[i];
} else {
break;
}
}
arr[j + 1] = tmp;
}
}
时间复杂度:
平均、最坏O(n²),最优:O(n);
空间复杂度:O(1);
稳定排序。
PS:算法最好情况下,待排元素全部有序,这样只需比较n-1次,每次比较之后,待插入元素e仍处在原来位置,无需移动元素,时间复杂度O(n),算法最坏情况下,待排元素全部逆序,这样在插入元素时,需要比较1+2+3+4+…+n-1=,同时移动元素次数也是,这样时间复杂度为O(n²),平均复杂度也为O(n²)。
希尔排序
希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率;
但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位;
希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。
void shellSort(vector<int>& arr) {
int len=arr.size();
int interval=len/3+1;
while(interval>1){
interval=interval/3+1;//确定分组的增量
for(int i=0;i<interval;i++){//分组
for(int j=i+interval;j<len;j+=interval){//分组后根据分组的增量来排序
int tmp=arr[j];
int k=0;
for(k=j;k>i&&arr[k-interval]>tmp;k-=interval)
arr[k]=arr[k-interval];
arr[k]=tmp;
}
}
}
}
优化希尔
时间复杂度不确定:O(nm),m∈[1,2);
空间复杂度:O(1);
不稳定排序。
归并排序
void merge(vector<int> &arr,int L,int mid,int R) //[L,R]闭区间
{
vector<int> help(R-L+1);
int p1=L,p2=mid+1,i=0;
while(p1<=mid && p2<=R)
help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
while(p1<=mid)
help[i++] = arr[p1++];
while(p2<=R)
help[i++] = arr[p2++];
copy(help.begin(),help.end(),arr.begin()+L);
}
void mergesort(vector<int> &arr,int L,int R)
{
if (R==L) return;
int mid = L + ((R-L)/2); // (L+R)/2
mergesort(arr,L,mid);//如果被分为[L,mid-1]、[mid,R]则会死循环
mergesort(arr,mid+1,R);
// inplace_merge(arr.begin()+L,arr.begin()+mid+1,arr.begin()+R+1);//库函数实现归并
merge(arr,L,mid,R);
}
void mergesort(vector<int> &arr)
{
if (arr.size() < 2) return;
mergesort(arr,0,arr.size()-1);
}
int main()
{
vector<int> arr={9,3,6,3};
mergesort(arr);
for(int i=0;i<arr.size();i++)
cout<<arr[i]<<" ";
cout<<endl;
system("pause");
return 0;
}
时间复杂度:
平均、最优、最坏都是O(nlogn);
空间复杂度:O(n);
稳定排序。
堆排序
void heapify(int arr[], int heapsize, int i)
{
int largest = i; // Initialize largest as root
int l = 2*i + 1; // left = 2*i + 1
int r = 2*i + 2; // right = 2*i + 2
if (l < heapsize && arr[l] > arr[largest])
largest = l;
if (r < heapsize && arr[r] > arr[largest])
largest = r;
if (largest != i)
{
swap(arr[i], arr[largest]);
heapify(arr, heapsize, largest);
}
}
void bulid_max_heap(int arr[], int heapsize)
{
for(int i = heapsize / 2 - 1; i >= 0; i--)
heapify(arr,heapsize,i);
//make_heap(RandomIt first, RandomIt last);//建堆函数,O(n)
}
void HeapSort(int arr[], int heapsize)
{
bulid_max_heap(arr,heapsize);
for(int i = heapsize - 1; i >= 0; i--)
{
swap(arr[0],arr[i]);
heapify(arr,i,0);
}
}
STL堆排函数:
vector<int> arr={……};
make_heap(arr.begin(),arr.end()/*,cmp=less<int>*/);//建堆函数
sort_heap(arr.begin(),arr.end()/*,cmp=less<int>*/);//堆排
默认使用大根堆,升序排序,同priority_queue。
cmp重载operator>后,使用小根堆,降序排序。
时间复杂度:
平均、最优、最坏都是O(nlogn);
空间复杂度:O(1);
不稳定排序。
快速排序
递归版本:
//闭区间[L,R],Hoare双向扫描
int Partition(vector<int>& arr,int L,int R){//传引用
int pivot=arr[L+rand()%(R-L+1)];
int i=L-1,j=R+1;
while(1){
do ++i;while(arr[i]<pivot);//不能取等号,取等号可能越界
do --j;while(arr[j]>pivot);
if(i>=j) return j;//j右边都大于等于pivot,j左边(包括j)都小于等于pivot
swap(arr[i],arr[j]);
}
}
void quicksort(vector<int>& arr,int L,int R){//传引用
if(L<R){
int index=Partition(arr,L,R);
//因为j小于等于pivot,所以不能划分为[L, index-1][index, R],并且在[1,1]情况下排序会陷入死循环
quicksort(arr,L,index);
quicksort(arr,index+1,R);
}
}
写法二:
int partition(vector<int> &arr, int left, int right) {
int mid = rand() % (right - left + 1) + left;
int pivot = arr[mid];
swap(arr[mid], arr[left]);
int l = left, r = right;
for(int i =left+1;i<=right;i++)
if(arr[i]<pivot)
swap(arr[i], arr[++l]);
swap(arr[left], arr[l]);
return l;
}
void sort(vector<int> &arr, int left, int right){
if(left >= right) return;
int mid = partition(arr, left, right);
sort(arr, left, mid-1);
sort(arr, mid+1, right);
}
其他优化:https://blog.csdn.net/qq_40586164/article/details/105774094
非递归:
递归的算法主要是在划分子区间,如果要非递归实现快排,只要使用一个栈来保存区间就可以了。
一般将递归程序改成非递归首先想到的就是使用栈,因为递归本身就是一个压栈的过程。
int Partition(vector<int>& arr,int L,int R){//传引用
swap(arr[L],arr[L+rand()%(R-L+1)]); //随机化
int pivot=arr[L];
int i=L,j=R+1;
while(1){
while(i<R && arr[++i]<pivot); //i<=R时可能越界
while(arr[--j]>pivot);
if(j<=i) break;
swap(arr[i],arr[j]);
}
swap(arr[L],arr[j]);
return j;
}
void QuickSortNotR(vector<int>& array,int left,int right)
{
stack<int> s;
s.push(left);
s.push(right);//后入的right,所以要先拿right
while(!s.empty())//栈不为空
{
int right = s.top();
s.pop();
int left = s.top();
s.pop();
int index = Partition(array,left,right);
if((index - 1) > left)//左子序列
{
s.push(left);
s.push(index - 1);
}
if((index + 1) < right)//右子序列
{
s.push(index + 1);
s.push(right);
}
}
}
平均时间复杂度:
平均、最优O(nlogn),最坏:O(n²);
空间复杂度:O(logn);(栈空间)
不稳定排序。
Q:快排如何实现稳定排序?
A:将每个元素变为pair<int,int> ,second位置放下标,按pair排序。
基数排序
先将数组内的元素按个位上的数分进0-9 十个桶内,接下来将这些桶子中的数值重新串接起来;
再将新数组元素按十位上的数分进0-9 十个桶内,接下来将这些桶子中的数值重新串接起来;
……
void radixSort(vector<int> &arr){
int len=arr.size();
int maxnum=*max_element(arr.begin(),arr.end());
int numOfBits=log10(maxnum)+1;
int radix=1;
for(int i=0;i<numOfBits;i++){
vector<vector<int>> buckets(10);
for(int num:arr){//将各个元素根据当前位的数字分到各个桶
int k=(num/radix)%10;
buckets[k].push_back(num);
}
int k=0;
for(auto &bucket:buckets){//将桶中元素写回到原数组
for(int num:bucket)
arr[k++]=num;
}
radix*=10;
}
}
时间复杂度:O(n);
空间复杂度:O(n);
稳定排序。