冒泡排序:
思想:交换类排序,两两交换,每趟交换完成后都会有一个最大的被冒到后面
性能
* 时间复杂度:O(n^2)
* 空间复杂度:O(1)
* 稳定:稳定
代码
//两两交换,每趟交换之后都会冒出一个最大的放在后面,
void maopao(int a[],int n){
for (int i = 0; i<n;i++) {
for (int j = 0; j<n-i-1; j++) {
if (a[j]>a[j+1]) {
int temp;
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
}
例子:8,7,6,5,4
第一趟冒泡::最大值8被放到最后面
7,8,6,5,4--------》8和7交换
7,6,8,5,4--------》8和6交换
7,6,5,8,4--------》8和5交换
7,6,5,4,8--------》8和4交换
第二趟冒泡::最大值8被放到最后面
6,7,5,4,8--------》7和6交换
6,5,7,4,8--------》7和5交换
6,5,4,7,8--------》7和4交换
后面的也一样:其实就是每次从前面比较出一个最大的放到后面,
时间复杂度:o(n2)
冒泡优化1:之设置标记位:
举例:对于1,2,3,5,4
按照普通的冒泡
第一趟
1,2,3,5,4--------->1和2没有
1,2,3,5,4--------->2和3没有
1,2,3,5,4--------->3和4没有
1,2,3,4,5--------->4和5进行了交换//这里已经完全有序了
第二趟
1,2,3,4,5--------->1和2没有
1,2,3,4,5--------->2和3没有
1,2,3,4,5--------->3和4没有
1,2,3,4,5--------->4和5没有
可以看出,在已经有序的情况下,还是会按照上面的方式重新比较,即使没有发生交换,所以这里就有一个优化的思路,设置一个标志位,每趟冒泡将其置为 true,当这一趟发生交换时把他置为 false,继续循环,若一趟结束没有发生一次交换,说明已经有序,也就是标志位一直为 true,就可以 break 跳出循环
代码如下
//优化后的冒泡,一个标志位:每一趟循环之前设置标记为 true,当一趟遍历发生交换记录将标记为改为 false,当某一趟遍历结束还没有发生交换时,说明已经有序,所以就不会置为 false;
void maopao1(int a[],int n){
int flag ;//标志位
for (int i = 0; i<n;i++) {
flag = 1;//每趟循环之前设置为 true
for (int j = 0; j<n-i-1; j++) {
if (a[j]>a[j+1]) {
flag = 0;//当发生了交换将其置为 false
int temp;
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
//如果一趟冒泡之后没有发生任何交换,说明已经有序,跳出循环
if (flag) {
break;
}
}
}
演示结果
第一趟:flag 为 true
1,2,3,5,4--------->1和2没有
1,2,3,5,4--------->2和3没有
1,2,3,5,4--------->3和4没有
1,2,3,4,5--------->4和5进行了交换//flag 被置为 flase
第二趟:flag 为 true
1,2,3,4,5--------->1和2没有
1,2,3,4,5--------->2和3没有
1,2,3,4,5--------->3和4没有
1,2,3,4,5--------->4和5没有//这一趟没有发生任何交换,flag 依然为 true,就可以跳出循环了
冒泡优化之:记录上一趟最后交换的位置
代码如下
//优化的冒泡,设置一个下表记录上次最后一次发生交换的位置,该位置前面的没有发生交换,说明已经有序,那么下次就可以缩小范围,最他后面的进行排序
void maopao2(int a[],int n){
int lsp=0;//交换每趟结束之后最后一次交换的位置
int lspt=0;//记录每趟之中交换的位置
for (int i = 0;i<n-1; i++) {
lsp = lspt;
for (int j=n-1; j>lsp; j--) {
if (a[j-1]>a[j]) {
int temp;
temp = a[j];
a[j] = a[j-1];
a[j-1] = temp;
lspt = j;
}
}
if (lspt == lsp) {
break;
}
}
}
演示如下
第一趟
5,7,1,4,2,3------->2和3没有交换
5,7,1,2,4,3------->2和4发生交换,当前的 j 是4,所以本次交换的位置为4
5,7,1,2,4,3-------->1和 2没有交换
5,1,7,2,4,3-------->1和7发生了交换:当前 j 是2,所以本次交换的位置为2
1,5,7,2,4,3-------->1和5发生了交换,当前 j 是1,所以本次交换的位置为1
第一趟结束以后,最后一次发生交换的位置为1,之前的没有再发生交换,说明已经有序,那么下一趟要排序的范围就缩小到1~n,而不是0~n
第二趟
1,5,7,2,3,4--------->4和3交换,当前 j 是5,所以本次交换的位置为5;
1,5,7,2,3,4---------->2和3没有交换
1,5,2,7,3,4---------->7和2发生了交换,当前 j 是3,所以本次交换的位置为3
1,2,5,7,3,4---------->5和2发生了交换,所以当前位置为2,所以本次交换的位置为2
第二趟结束后,最终交换位置为2,说明2号位置前面已经有序,下次缩小范围 2~n 就可。
快排
- 思想:
- 选中一个key,一般以待排序序列的第一个作为key
- 从前往后找比key大的放在key的后面
- 从后往前找比key小的放在key的前面
- 一趟排序,数组被划分成了两个部分:比key小的和比key大的,对每一部分递归操作
- 性能
- 时间复杂度:
- 取决于快排递归的深度,用递归树来描述
- 最优情况下,每次划分均匀,平衡二叉树,递归树深度为log2n,第一次要对整个数组扫描一遍,需要n次比较,所以是o(nlogn)
- 最坏情况下,待排序列为正序或者逆序,退化成一颗斜树,所以是O(n^2)
- 平均:O(nlogn)
- 空间复杂度:主要是递归造成的栈空间的使用
- 最好情况:log2n,空间复杂度:O(logn)
- 最坏情况:O(n)
- 平均情况:O(logn)
- 稳定性
- 关键字的比较和交换是跳跃的
- 不稳定
- 时间复杂度:
- 代码
*/
void quickSort(int a[],int l,int r){
if (l<r) {
//i和j作为两个指针,i++找比当前key小的,j--找比key大的
int i = l;
int j = r;
int key = a[i];
while (i<j) {
//从后往前找找比key小的
while (i<j&&a[j]>key) {
j--;
}
if (i<j) {
a[i++] = a[j];
}
//从前往后找找比key大的
while (i<j&&a[i]<key) {
i++;
}
if (i<j) {
a[j--] = a[i];
}
}
a[i] = key;
quickSort(a, l, i-1);
quickSort(a, i+1, r);
}
}
- 优化
* 随机产生key
* 三数取中:左边、右边、中间取一个值在中间的作为key