前言
这一节分析下两种交换排序,冒泡排序和快速排序
交换的方法
因为是交换排序自然是少不了元素的交换,之前看面试题的时候有看到要求不使用额外的空间交换两个数,这里先列举几个方法。
交换的思想
根据要求不申请额外空间,那么可以很直接的想到在已有的两个数上做操作,那么操作两个数自然能想到的就是加减乘除与或异或等操作,于是就有了下面三种方法。
基于加减的不申请额外空间交换两数
void swap(int &a,int &b)//input a = 3; b = 4
{
a = a + b;//a = 7 b = 4
b = a - b;//a = 7 b = 3
a = a - b;//a = 4 b = 3
}
基于乘除的不申请额外空间交换两数
void swap(int &a,int &b)//input a = 3; b = 4
{
a = a * b;//a = 12 b = 4
b = a / b;//a = 12 b = 3
a = a / b;//a = 4 b = 3
}
基于异或的不申请额外空间交换两数
void swap(int &a,int &b)// input a = 0011 b = 0100
{
a = a ^ b;//a = 0111 b = 0100
b = a ^ b;//a = 0111 b = 0011
a = a ^ b;//a = 0100 b = 0011
}
冒泡排序
原理就是第一趟排序一直两两比较将较大的移动到数组的最后,这时数组的最后一个有序,然后再从第一个数两两比较将第二大的移动到倒数第二个位置,以此类推。
void bubbleSort(int arr[], int len)
{
for (int i = 0; i < len; i++)
{
for (int j = 0; j < len - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
arr[j] = arr[j + 1] + arr[j];
arr[j + 1] = arr[j] - arr[j + 1];
arr[j] = arr[j] - arr[j + 1];
}
}
}
}
冒泡排序时间复杂度
if (arr[j] > arr[j + 1])
这是程序最复杂的语句,当外层i = 1时内层执行N-1次,i = 2时内层执行N - 2次,一次类推,所以总执行时间是(N-1 + N - 2 + N-3 + … … + 0),所以时间复杂度是O(N^2)。
冒泡排序的稳定性
冒泡排序交换元素的时候,只要规定是小于才交换,剔除等于的情况就不会破坏两个相同数的原始顺序,所以是稳定的。
快速排序
参考博客:快速排序详解
这好像是大话数据结构书上的,写的非常好了。
快排代码
//快速排序(从小到大)
void quickSort(int left, int right, int arr[])
{
if (left >= right)
return;
int i, j, base, temp;
i = left, j = right;
base = arr[left]; //取最左边的数为基准数
while (i < j)
{
while (arr[j] >= base && i < j)
j--;
while (arr[i] <= base && i < j)
i++;
if (i < j)
{
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
//基准数归位
arr[left] = arr[i];
arr[i] = base;
quickSort(left, i - 1, arr);//递归左边
quickSort(i + 1, right, arr);//递归右边
}
快速排序的稳定性
快排比冒泡排序快的原因是,冒泡交换的数是相邻的两个,快排是跳跃的交换,但是因此也就破坏了相同两数的原有顺序,例如数组:1,6,6,6,6第一次以1为主元,left指针指向索引1上的6,right指针指向索引4上的6,right指针上的数大于主元1,right指针减一,left指针上的数大于6,所以此时交换left,right指针上的两个数,此时就已经破坏了这两个6原有的顺序,所以快速排序不稳定。
快速排序的时间复杂度
参考博客:快排时间复杂度分析