交换排序基本思想
两两比较待排记录的关键字,一旦发现两个记录的次序与排序的要求相逆,则交换这两个记录的位置,直到表中没有逆序的记录存在为止。
- 冒泡排序
- 快速排序(对冒泡的改进)
<1>冒泡排序
基本思想:序列中相邻的两个元素进行比较,如果前一个元素大于后一个元素,则交换二者位置,接着继续向后循环比较直到最后一个元素,这样一趟下来就可以将最大的那个元素放到最后的位置。完成之后,进行第二趟排序,循环比较直到倒数第二个元素,就可以将第二大元素放于倒数第二个位置......循环以上步骤,直到循环比较到原数组的最后两个元素,排序即可完成。
代码体现:
void bubble_sort(int arr[],int len)
{
for(int i=0;i<len-1;i++) //排序的趟数,n个数就需要排序n-1趟
{
bool flag=true; //一个小小的优化,在某一趟如果没有改变就不再进行最外层循环了
for(int j=0;j<len-i-1;j++) //每一趟中的交换排序
{
if(arr[j]>arr[j+1])
{
swap(arr[j],arr[j+1]);
flag=false; //进入这个判断就证明排序没有完成
}
}
if(flag)
break;
}
}
复杂度分析:
1:空间复杂度:可以看出并没有什么大的额外空间,所以为O(1);
2:时间复杂度:
- 最好情况下,待排序列已经是有序的,这样冒泡排序在第一趟排序过程就没有交换发生,所以一趟之后即排序结束。也即,只在第一趟中进行了n-1次比较。最坏情况是待排序列记录按逆序排序,所以需要进行n-1趟排序,且第i趟需要进行n-i次比较。所以总的比较次数为1/2*n*(n-1);所以冒泡排序的时间复杂度为O(n²)。
3:算法稳定性:
冒泡排序并未改变相同元素的先后次序,所以是稳定的排序算法
<2>快速排序
基本思想:快速排序就是冒泡排序的一种改进,冒泡排序是通过每一趟冒泡将最大值(最小值)放到恰当位置,而快速排序則是每趟排序从待排序区间选一个基准值(也称作枢纽值),将比它小的数据全放在其左边,将比它大的值放在其右边然后递归其左右子区间对其排序,一层层递归下去,某区直到间只剩一个数据时,停止递归,此子区间已经算是有序,继而向其上层区间返回,一层层向上返回,当首次枢纽值的左右区间均已有序时,整个排序就算完成。
递归代码:
#include<iostream>
using namespace std;
int Partition(int *arr,int low,int high) //划分算法,找到元素正确位置
{
int tmp=arr[low]; //从第一个元素作为基准进行划分
while(low<high) //从序列两端交替向中间扫描
{
while(arr[high]>=tmp&&low<high) //从右向左扫描查找第一个小于基准的
high--;
if(low<high) //找到小于基准的数,将其交换到表的左边
{
arr[low]=arr[high];
low++; //因为本位low已存从右边拿来的数据,所以low往后移
}
while(arr[low]<=tmp&&low<high) //从左往右找第一个大于基准的元素
low++;
if(high>low) //找到大于基准的数,将其交换到表的右边
{
arr[high]=arr[low];
high--;
}
}
arr[low]=tmp; //最终的low/high位置即是基准在序列中的最终位置,放入即可
return low; //返回基准的最终位置下标
}
void QuickSort(int *arr,int low,int high) //递归形式的快排
{
int i;
if(low<high)
{
i=Partition(arr,low,high); //以i为基准将序列分为两部分
if(i-low>1) //避免一次递归调用,左边没有元素或者只有一个就不用排序
QuickSort(arr,low,i-1); //左半边进行排序
if(high-i>1)
QuickSort(arr,i+1,high); //右半边进行排序
}
}
void Show(int *arr,int len)
{
for(int i=0;i<len;i++)
cout<<arr[i]<<" ";
cout<<endl;
}
int main()
{
int arr[]={61,33,48,82,72,11,25,48};
int len=sizeof(arr)/sizeof(arr[0]);
QuickSort(arr,0,len-1);
Show(arr,len);
}
非递归代码(我们都知道栈与递归很相似,那么我们就可以用栈来替换递归)
#include<iostream>
#include<stack>
#include<assert.h>
using namespace std;
void QuickSort(int *arr,int low,int high)
{
assert(arr);
if(low>=high)
throw "error";
int i=low; //赋值给i只是为了用于操作,不想打字low(懒)。。。
int j=high;
stack<int> s;
s.push(i); //将序列最左边下标入栈
s.push(j); //将最右边下标入栈
while(!s.empty()) //当栈不空证明还有序列需要处理
{
j=s.top(); //因为先入左边,所以先出右边
high=j; //将当前的左右端记录,下边要进行判断,重要啊,否则就死了
s.pop();
i=s.top();
low=i;
s.pop();
int tmp=arr[i];
while(i<j)
{
while(i<j&&arr[j]>=tmp)
j--;
if(i<j)
{
arr[i]=arr[j];
i++;
}
while(i<j&&arr[i]<=tmp)
i++;
if(i<j)
{
arr[j]=arr[i];
j--;
}
}
arr[i]=tmp; //此时的i和j都是分段的下标
if(high-j>1) //判断分割点右边是否还有数据需要入栈进行排序
{
s.push(j+1); //入分割点右边序列的最左边的下标
s.push(high); //入分割点右边序列的最右边的下标
}
if(i-low>1)
{
s.push(low);
s.push(i-1);
}
}
}