交换排序
冒泡排序
思路:
- 两两比较相邻记录的关键字,若发生逆序,则进行交换。若是从小到大排序,则每轮都选出一个最大的数交换至最右边。
实现:
//冒泡排序
void BubbleSort(SqList &L)
{
int m = L.length - 1; //一般r[0]闲置用作哨兵单元,所以有L.length-1个
bool flag = 1; //标记某一趟排序是否发生排序,flag初始化为1进入循环
while (flag && (m >= 0))
{
flag = 0;//若本趟未发生交换,则不会进入下一趟排序
for (int i = 1; i <= m; i++) //循环遍历,比较所有相邻的两个数的大小,看本趟是否需要交换
if (L.r[i].key > L.r[i + 1].key)
{
flag = 1;//进入if语句说明至少需要交换一次,则标记交换过位置
//交换位置
ReadType temp = L.r[i];
L.r[i] = L.r[i + 1];
L.r[i + 1]= temp;
}
m--; //执行一次长度减一
}
}
复杂度
- 空间复杂度:n2/4~3n2/4
- 时间复杂度:O(n2)
特点:
- 稳定排序
- 移动次数较多,需要排序的数量n较多时不宜采用
- 可用于链式存储
快速排序
思路:
- 如3,9,7,4,6五个数字排序
- 随便选一个 其中的数字,比如4,然后把3放到4的左边,把9,7,6放到4的右边。
- 然后在9,7,6中选一个数字如7,把6放到7的左边,把9放到7的右边。
总之,在一组数中选一个数,比这个数大放左边,比这个数小的放右边。分成两组,再在每一组中递归的调用这种方法。直到分不下去为止。
实现
//快速排序
//一次排序,以最后一位数的大小作为标准将数列分为两组
int partition(SqList &L ,int low, int high )
{
L.r[0] = L.r[low];//随便取一个记录放在哨兵中,在这里取最低位的(如果取最高位的数时,则后面的while之后的语句要覆盖高位,而不是低位)
int pivotkey = L.r[low].key;//把这个记录的key保存在pivotkey中
while (low<high)//进行一趟排序
{
while (low<high && L.r[high].key >= pivotkey)//从这一组数的最后一个(高位)开始,和之前记录的数比较,
high--;//若大于之前的记录,则位置前移动,直到遇到一个小于的
L.r[low]= L.r[high];//若高位的数小于之前记录的数,则覆盖低位(即将高位的数赋值给低位,低位保存于哨兵中),然后从被覆盖的低位开始
while (low < high && L.r[low].key < pivotkey)//若被覆盖的低位小于之前记录的数,
low++;//则位置向后移动,直到遇到一个大于的
L.r[high] = L.r[low];//若高位的数大于之前记录的数,则覆盖上次覆盖低位的high(上次覆盖低位的high已经保存在低位中),然后如果low<high则,从被覆盖的高位开始while循环
}
L.r[low] = L.r[0];//最后将哨兵中的数据归还,因此,左边的都是比该记录小的,右边的都是比该记录大的,到此一次排列结束
return low; //返回该记录的位置,枢轴位置
}
//递归调用
void quicksort(SqList &L, int low, int high)
{
if (low < high)
{
int pivotloc = partition(L,low,high);//将返回的枢轴位置记录下来,以此为临界点分为左半部分和右半部分两组
partition(L,low, pivotloc-1);//递归调用左半部分
partition(L, pivotloc+1,high);//递归调用右半部分
}
}
void quickSort(SqList &L)
{
quicksort(L, 1, L.length);
}
复杂度
- 空间复杂度:O( l o g n log{n} logn)~O(n)
- 时间复杂度:O(n l o g n log{n} logn)
特点
- 不是很稳定
- 较适用于顺序结构
- 适合于需要排序的数量n较多时,是所有内部排序方法中速度最快的一种。