交换排序
定义:根据两个关键字的比较结果来对换两个关键字在待排序列中的位置
冒泡排序:
void bubblesort(elemtype A[], int n)//冒泡排序
{
int m,j,flag;// m表示排序趟数,j表示比较次数,flag表示是否发生了交换,即待排序列已有序。
for(m=1; m<=n-1&&flag=1; m++)//排序n-1趟且发生了交换
{
flag=0; //初始将flag置为零
for(j=1; j>=n-m; j++) //第m趟需比较n-m次
if(A[j]>A[j+1]) //前后两个比较是否需要交换
{
swap(A[j],A[j+1]);
flag=1; //发生了交换
}
}
}
最好情况:待排序列已有序
比较次数:n-1
交换次数:0
最坏情况:待排序列逆序
比较次数:n-i的累加和
交换次数:3(n-i)的累加和,swap函数需要交换3次才能达到交换两关键字的目的
时间复杂度 :O(n^2)
空间复杂度:O(1)
稳定性:稳定
特点: 每一趟排序必定得到一个最大或最小值放置在最终的位置上。
快速排序:
void quicksort(elemtype A[], int low,int high)//快速排序
{
int pivotposition = partition(A[],low,high)//得到枢纽值(之前为在第一个位置上)新的位置,将左右分为两个子区间
quicksort(A[],low,pivotposition-1);//对左区间的待排序列递归进行快速排序
quicksort(A[],pivotposition+1,high);//对右区间的待排序列递归进行快速排序
}
int partition(elemtype A[],int low, int high)//按枢纽值划分待排序序列
{
int pivot=A[low]; //初始枢纽值定义为区间上的第一个元素
while(low<high)
{
while(low<high&&A[high]>=pivot)//找到从右往左第一个比枢纽值小的数的位置
high--;
swap(A[low],A[high]); //把该小值交换到前面去
while(low<high&&A[low]<=pivot)//找到从左往右第一个比枢纽值大的数的位置
low++;
swap(A[high],A[low]) //把该大值交换到后面去
} //再找剩下的最大最小,直到high=low停止
A[low]=pivot;
return low; //返回枢纽值的位置
}
所用递归栈的深度:
最好情况:树深度最小。O(log2(n+1))
最坏情况:树深度最大、O(n-1)
空间复杂度:
O(log2n)
时间复杂度: 运行时间与枢纽值的选择有关,如果对称的话,时间最短为O(nlog2n)
最坏情况:初始序列有序时,退化为未优化的冒泡算法。时间复杂度O(n2)
稳定性:
不稳定,当都在枢纽值一边有两个相同值时,后一个值可能先交换到左边,相对位置就发生了改变
特点:
每排一趟,都会将一个枢纽值放在最终位置。(适用与寻找第几大元素第几小元素的性质)
一些有关与交换排序的题目:
双向冒泡算法:
void bi_bubblesort(elemtype A[], int n)
{
int i,j;
int low=0;
int high=n-1;
flag=1; //标志是否发生交换。
while(low<high&&flag=1)
{
flag=0;
for(i=low; i<=high-1;i++)//从前往后冒泡
if(A[i]>A[i+1])
{
swap(A[i+1],A[i]);
flag=1;
}
high--;//更新上界
for(j=high; j>=low+1;j--)//从后往前冒泡
if(A[j]<A[j-1])
{
swap(A[j-1],A[j]);
flag=1;
}
low++;//更新下界
} //low=high时跳出,此时待排序列中仅有一个元素
}
对于一个顺序表每个元素不同,要将奇数放在前面。偶数放在后面。
首先:对于这样一个问题,想到交换算法快速排序的划分方法,扫描一边即可。
void oddnum(elemtype A[], int n)
{
int low=0;
int high=n-1;
while(low<high)//当只剩下一个关键字时排序完成
{
while(low<high&&A[high]%2==0)//从右往左找到第一个奇数,且不能比low小
high--;
while(low<high&&A[low]%2==1)//从左往右找第一个偶数,且不能比high高
low++;
if(low<high) //判断到底是找到了还是出界了
{
swap(A[high],A[low]);//将右边的奇数和左边的偶数交换
low++; //再找下一区间的
high--;
}
}
}
**将红黄蓝三种颜色组成的序列,排序后顺序为红黄蓝。**同理可以对应不同的三个数排序
首先,对于这种情况可以采用交换算法,一个指针保存红色位置。一个指针保存蓝色的位置。
一个指针用来扫描所有关键字
typedef enum // 建立一个包括这三种元素的数据结构
{
red,yellow,blue
}color
void redyellowblue(color[],int n)
{
int i=0;
int low =0;
int high =n-1;
while (i<=high) //将所有n个关键字扫描一遍
{
switch(color[i])
case red:
swap(color[i],color[low]); //如果是红色,把他保存到红色的位置上去,交换回来的关键字一定不是红色。
low++;
i++; //所以指针下移一位,而不用判断他是否是红色。
break;
case yellow: //黄色不用管他,直接下移一位
i++;
break;
case blue:
swap(color[i],color[high]); //如果是蓝色,把他放到蓝色的位置上去,但是交换回来的数据不知道是什么颜色
high--; //所以蓝色位置指针位置改变。而扫描指针不下移,继续判断是什么颜色。
}
}
}
从无序线性表中找第K小的关键字。
思考,有三种方法
第一种:先排序后找第k个位置上的数,最低时间复杂度0nlog2n
第二种:采用选择排序中的小顶堆排序,输出第k个关键字即是第k小的关键字。时间复杂度0 n+klog2n
第三种:采用快排中的划分方法,当枢纽值的位置m=k时即为第k小的值,如果m<k,则第k小的值在枢纽右边子序列。同理m>k。
int k_num(elemtype A[], int low, int high, int k)
{
int pivot=A[low];
int low_temp=low; //递归需要更改上下界,所以把设置变量
int high_temp=high;
while(low_temp<high_temp) //快排的划分算法
{
while(low_temp<high_temp&&A[high_temp]>=pivot)
high_temp--;
swap(A[low_temp],A[high_temp]);
while(low_temp<high_temp&&A[low_temp]<=pivot)
low_temp++;
swap(A[high_temp],A[low_temp]);
}
A[low_temp]=pivot;
if(low_temp=k) //第k个位置,正好。
return A[low_temp];
else if(low_temp<k)
k_num(A[],low_temp+1,high,k-low_temp); //从右区间找,注意此时找的是第k-low小值
else
k_num(A[],low,low_temp-1,k);//从左区间找第k大的值。
}