交换排序:根据序列中两个元素关键字的比较结果来对换这两个记录在序列中的位置。
冒泡排序的基本思想:假设待排序元素表长为size,从前往后(或从后往前)两两比较相邻元素的值,若为逆序(即arr[j]>arr[j+1]),则交换他们,直至整个元素表有序。
// 冒泡排序(升序 从前往后)(下沉)每趟排序确定一个相对最大的数,放在右边
void bubbleSort(int *arr, int size)
{
if (nullptr == arr || size <= 0)
return;
for (int i = 0; i < size - 1; i++) // 一共扫描(size-1)趟
{
bool flag = false; // 本趟扫描是否发生交换的标志
for (int j = 0; j < size - 1 - i; j++) // 第(i+1)趟时,共需比较(size-1-i)次
{
if (arr[j]>arr[j + 1]) // 逆序则交换
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = true;
}
}
if (!flag)
break;
}
}
// 冒泡排序(升序 从后往前 上浮)每趟排序确定一个相对最小的数 放在左边
void bubbleSort_min(int *arr, int size)
{
if (nullptr == arr || size <= 0)
return;
for (int i = 0; i < size - 1; i++)
{
bool flag = false;
for (int j = size - 1; j>i; j--)
{
if (arr[j - 1] > arr[j])
{
int temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
flag = true;
}
}
if (!flag)
break;
}
}
(1)算法的最好时间复杂度
若文件的初始状态是正序的,一趟扫描即可完成排序。所需的关键字比较次数C和记录移动次数M均达到最小值:
Cmin=n–1
Mmin=0
冒泡排序最好的时间复杂度为O(n)。
(2)算法的最坏时间复杂度
若初始文件是反序的,需要进行n–1趟排序。每趟排序要进行n–i次关键字的比较(1≤i≤n–1),且每次比较都必须移动
记录3次来交换记录位置。在这种情况下,比较和移动次数均达到最大值:
Cmax=n(n–1)/2=O(n2)
Mmax=3n(n–1)/2=O(n2)
冒泡排序的最坏时间复杂度为O(n2)。
(3)算法的平均时间复杂度为O(n2)
虽然冒泡排序不一定要进行n–1趟,但由于它的记录移动次数较多,故平均时间性能比直接插入排序要差得多。
(4)算法稳定性
冒泡排序是就地排序,且它是稳定的。
快速排序的基本思想是基于分治法,如下:(挖坑填充+分治法)
1、先从数列中取出一个数作为基准数;
2、分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边;
3、再对左右区间重复第二步,直到各区间只有一个数。
int partition(int *arr, int size, int low, int high)
{
int value = arr[low];
while (low < high)
{
while ((low < high) && (value <= arr[high]))
high--;
arr[low] = arr[high];
while ((low<high) && (value >= arr[low]))
low++;
arr[high] = arr[low];
}
arr[low] = value;
return low;
}
void quickSort(int *arr, int size, int low, int high)
{
assert(arr != nullptr || size > 0 || low >= 0 || high<size ); //判空
if (low < high) // 递归返回条件:当数组长度等于1,即low=high时,递归结束。
{
int index = partition(arr, size, low, high);
quickSort(arr, size, low, index - 1);
quickSort(arr, size, index + 1, high);
}
}
(1) 时间复杂度:最好时间复杂度为O(nlog2n),最坏时间复杂度为O(n2),平均时间复杂度为O(nlog2n);快速排序是所有内部排序算法中平均性能最优的排序算法。
以下情况快排会出现最坏的时间复杂度:
1)输入的元素已经排序或逆向排序
2)每个划分的一边都没有元素
(2) 空间复杂度:O(nlog2n)
(3) 稳定性:快速排序是一个不稳定的排序方法。
剑指offer 对公司员工的年龄进行排序,要求时间效率O(n);
公司员工的年龄有一个范围0~99,使用辅助数组timeofage[]来统计每个年龄出现的次数。某个年龄出现了多少次就在数组ages中设置几次该年龄,就相当于给数组ages[]排序了。该方法用长度为100的整数数组作为辅助空间换来了O(n)的时间效率。
// 给公司员工你年龄排序 O(n) 使用辅助数组timeofage[]
void agesSort(int *ages,int size)
{
assert(nullptr != ages && size > 0); // 判空
const int oldestAge = 99; // 年龄范围0~99
int timeofAge[oldestAge + 1]; // 保存每个年龄出现的次数
for (int i = 0; i < oldestAge; i++) // 每个年龄出现的次数均初始化为0,以便计数
timeofAge[i] = 0;
for (int i = 0; i < size; i++)
{
int age = ages[i];
assert(age >= 0 && age <= 99); // 保证年龄在有效范围内
timeofAge[age]++; // 累计每个年龄出现的次数
}
int index = 0; // 重新定义一个下标,对年龄进行排序
for (int i = 0; i <= oldestAge; i++) // 对年龄进行排序
{
for (int j = 0; j < timeofAge[i]; j++)
{
ages[index] = i;
index++;
}
}
}