交換排序:根據序列中兩個元素關鍵字的比較結果來對換這兩個記錄在序列中的位置。
冒泡排序的基本思想:假設待排序元素表長爲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++;
}
}
}