十大排序算法
#面经 #算法与数据结构
- 冒泡排序(Bubble Sort)
- 选择排序(Selection Sort)
- 插入排序(Insertion Sort)
- 希尔排序(Shell Sort)
- 快速排序(Quick Sort)
- 归并排序(Merge Sort)
- 堆排序(Heap Sort)
- 计数排序(Counting Sort)
- 基数排序(Radix Sort)
- 桶子排序(Bucket Sort)
冒泡排序
// 冒泡排序
void bubble_sort(int data[], int n)
{
for (int i = 0; i < n; ++i)
{
for (int j = 1; j < n-i; ++j)
{
if (data[j] < data[j-1])
swap(data[j], data[j-1]);
}
}
}
冒泡排序算法的优化,当数据已经成升序排放,则算法的时间复杂度为 O(n)。
// 冒泡排序优化
void bubble_sort2(int data[], int n)
{
bool is_sorted = false;
for (int i = 0; i < n && !is_sorted; ++i)
{
is_sorted = true; // 如果遍历一遍后,没有元素交换,则说明已经有序
for (int j = 1; j < n-i; ++j)
{
if (data[j] < data[j-1])
{
swap(data[j], data[j-1]);
is_sorted = false;
}
}
}
}
选择排序
// 选择排序
void selection_sort(int data[], int n)
{
for (int i = 0; i < n; ++i)
{
int min_index = i;
for (int j = i+1; j < n; ++j)
{
if (data[j] < data[min_index])
min_index = j;
}
swap(data[i], data[min_index]);
}
}
插入排序
插入排序算法有一个很明显的优势,当数据几乎有序时,算法的时间复杂度可以将为线性的。插入排序可作为其他排序算法的子过程,进行优化。
// 插入排序
void insertion_sort(int data[], int n)
{
for (int i = 0; i < n-1; ++i)
{
for (int j = i+1; j > 0 && data[j] < data[j-1]; --j)
swap(data[j], data[j-1]);
}
}
希尔排序
布长的选择:
while (h < n/3)
h = h*3 + 1; // 1 4 13 43 …
// 希尔排序
void shell_sort(int data[], int n)
{
int h = 1;
while (h < n/3)
h = h*3 + 1; // 1 4 13 43 ...
while (h >= 1)
{
for (int i = h; i < n; ++i)
{
for (int j = i; j >= h && data[j] < data[j-h]; j -= h)
swap(data[j], data[j-h]);
}
h /= 3;
}
}
快速排序
快速排序的核心在于切分(partition
)。
单路快排
// 单路快排
int partition(int data[], int l, int r)
{
int v = data[l];
int p = l, i = l + 1;
for (; i <= r; ++i)
{
if (data[i] > v)
continue;
else
{
swap(data[i], data[++p]);
}
}
swap(data[l], data[p]);
return p;
}
双路快排
最常用的快速排序算法的 partition 是双路快排。
// 双路切分
int partition_2ways(int data[], int l, int r)
{
int v = data[l];
int i = l+1, j = r;
while (true)
{
while (i <= r && data[i] <= v)
i++;
while (j >= l && data[j] > v)
j--;
if (i > j)
break;
swap(data[i], data[j]);
i++;
j--;
}
swap(data[l], data[j]);
return j;
}
三路快排
拥有大量重复元素时,性能相比双路快排得到优化。不常用
void quick_sort_3ways(int data[], int l, int r)
{
if (l >= r)
return;
// 切分
int v = data[l]; // 基准值
int lt = l, i = l + 1, gt = r + 1;
while (i < gt)
{
if (data[i] < v)
{
swap(data[i], data[lt+1]);
i++;
lt++;
}
else if (data[i] > v)
{
swap(data[i], data[gt-1]);
gt--;
}
else // ==
i++;
}
swap(data[l], data[lt]);
// data[l ...lt-1] < v
// data[lt … gt-1] == v
// data[gt …r] > v
quick_sort_3ways(data, l, lt-1);
quick_sort_3ways(data, gt, r);
}
void quick_sort_3ways(int data[], int n)
{
quick_sort_3ways(data, 0, n-1);
}
归并排序
void merge(int data[], int l, int mid, int r)
{
int* aux = new int[r-l+1];
for (int i = l; i <= r; ++i)
aux[i-l] = data[i];
// merge
int i = l, j = mid+1;
for (int k = l; k <= r; ++k)
{
if (i > mid)
{
data[k] = aux[j-l];
j++;
}
else if (j > r)
{
data[k] = aux[i-l];
i++;
}
else if (aux[i-l] < aux[j-l])
{
data[k] = aux[i-l];
i++;
}
else
{
data[k] = aux[j-l];
j++;
}
}
delete [] aux;
aux = nullptr;
}
void merge_sort(int data[], int l, int r)
{
if (l >= r)
return;
int mid = l + (r - l) / 2;
merge_sort(data, l, mid);
merge_sort(data, mid+1, r);
merge(data, l, mid, r);
}
// 归并排序
void merge_sort(int data[], int n)
{
merge_sort(data, 0, n-1);
}
堆排序
// 节点 k 进行下沉操作
// k 的左孩子:2*k+1 右孩子:2*k+2
void sink(int data[], int n, int k)
{
while (2*k+1 < n)
{
int j = 2*k + 1; // 左孩子
if (j+1 < n && data[j+1] > data[j]) //i 比较左右孩子
j++;
if (data[k] < data[j])
swap(data[k], data[j]);
k = j; // 继续下沉
}
}
// 堆排序
void heap_sort(int data[], int n)
{
// 从最后一个非叶子节点开始执行下沉操作
for (int k = (n-2)/2; k >= 0; --k)
sink(data, n, k);
for (int i = n-1; i > 0; --i)
{
swap(data[0], data[i]); // data[0] 为堆中的最大值
sink(data, i, 0); // 重新调整,使之满足最大堆
}
}
计数排序
// 计数排序
// 0 <= data[i] < n
void counting_sort(int data[], int n)
{
int max = data[0];
for (int i = 1; i < n; ++i)
{
if (data[i] > max)
max = data[i];
}
int* count = new int[max+1];
int* aux = new int[n];
for (int i = 0; i < max+1; ++i)
count[i] = 0;
for (int i = 0; i < n; ++i)
count[data[i]]++;
for (int i = 1; i <= max; ++i)
count[i] += count[i-1];
for (int i = n - 1; i >= 0; —i)
{
aux[count[data[i]] - 1] = data[i];
count[data[i]]--;
}
for (int i = 0; i < n; ++i)
data[i] = aux[i];
delete [] aux;
delete [] count;
aux = nullptr;
count = nullptr;
}
基数排序
int get_max(int data[], int n)
{
int max = data[0];
for (int i = 1; i < n; ++i)
{
if (data[i] > max)
max = data[i];
}
return max;
}
// 计数排序只需要对 0-9 进行计数排序
// 参数 place 表示位(个/十/百…)
void counting_sort_ten(int data[], int n, int place)
{
int max = 10; // [0...9]
int* count = new int[max];
int* aux = new int[n];
for (int i = 0; i < max; ++i)
count[i] = 0;
for (int i = 0; i < n; ++i)
count[(data[i] / place) % 10]++;
for (int i = 1; i < max; i++)
count[i] += count[i - 1];
for (int i = n - 1; i >= 0; --i)
{
aux[count[(data[i] / place) % 10] - 1] = data[i];
count[(data[i] / place) % 10]--;
}
for (int i = 0; i < n; ++i)
data[i] = aux[i];
delete [] aux;
delete [] count;
aux = nullptr;
count = nullptr;
}
// 基数排序
void radix_sort(int data[], int n)
{
int max = get_max(data, n);
for (int place = 1; max / place > 0; place *= 10)
counting_sort_ten(data, n, place);
}
桶子排序
// 桶子排序 [0…100]
void bucket_sort(int data[], int n)
{
// vec[0] : [0…9]
// vec[1] : [10…19]
// vec[2] : [20…29] …..
vector<vector<int> > vec(10);
for (int i = 0; i < n; ++i)
vec[(data[i]/10)].push_back(data[i]);
int k = 0;
for (int i = 0; i < 10; ++i)
{
for (int j = 0; j < vec[i].size(); ++j)
{
data[k] = vec[i][j];
k++;
}
}
// 已经几乎有序,调用插入排序算法
insertion_sort(data, n);
}
排序算法的测试
在我的电脑上,各种算法一秒内可以完成排序的整型数个数。