文章目录
前言
常用的排序算法描述及代码实现
1、选择排序
1.1、算法描述
以8个元素的排序为例,模拟一下整个算法过程。
找出1~8元素的最小值,并且记录最小值的位置为k。
将其与第1位元素交换。
找出2~8元素的最小值,并且记录最小值位置k。
将其与第2位元素交换。
照此过程一直进行,直到所有元素按照从小到大的顺序排列。
选择排序不是一个稳定的排序算法(有相同的元素相对位置可能会改变)。
1.2、时间复杂度
O(n^2)
1.3、代码
// 选择排序过程
for (int i = 0; i < n - 1; ++i) { // 一共进行n-1躺
int min_pos = i; // 记录最小元素的位置
for (int j = i + 1; j < n; ++j) {
if (a[j] < a[min_pos]) { // 在数组中选择最小元素
min_pos = j; // 更改最小元素位置
}
}
if (min_pos != i) swap(a[i], a[min_pos]); // 将最小值与第i个位置交换
}
2、冒泡排序
2.1、算法描述
冒泡过程就是一个从第1位到第n位进行的连续交换过程。
(1)、冒泡排序分为n-1个阶段。
(2)、在第1个阶段,通过“冒泡”,我们将前n个元素的最大值移动到序列的最后一位。此时只剩前n-1个元素未排序。
(3)、在第i个阶段,此时序列前n-i+1个元素未排序。通过“冒泡”,我们将前n-i+1个元素中的最大值移动到最后一位。此时只剩前n-i个元素未排好序。
(4)、最终到第n-1个阶段,前2个元素未排序。我们将其中的较大值移动到后一位,则整个序列排序完毕。
冒泡排序是一个稳定的排序算法。
2.2、时间复杂度
O(n^2)
2.3、代码
for (int i = 0; i < n; i++)
{
for (int j = 1; j <= n - 1 - i; j++)
{
if (arr[j -1] > arr[j])
{
int temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
}
}
}
3、插入排序
3.1、算法描述
对于如下序列,这个序列的前4个元素已经有序,现在我们要把2插入到前4个元素中去。
首先让2出队,然后将其与6比较,发现2<6,说明2应该在6前面。所以我们将6向后移动。
再将其与5比较,发现2<5,说明2应该排在5前面。所以我们将5向后移动。
经过同样的分析,4也应该向后移
最后我们将2与1比较,发现1<2,说明2应该放在1的后面。正好经过前面的移动,1后面有一个空位,所以我们把2插入在这个空位中。
插入排序是一个稳定的排序算法。
3.2、时间复杂度
O(n^2)
3.3、代码
for (int i = 1;i < len;i++)
{
int temp = a[i], j; // 无序的元素
for (j = i - 1;j >= 0 && a[j] > temp;j--) // j是已排好的元素的下标,
//无序的元素和有序的元素作比较
{
a[j + 1] = a[j];
}
a[j + 1] = temp;
}
4、快速排序
4.1、算法描述
(1)、假设我们要对数组a[1…n]排序。初始化区间[1…n]。
(2)、令l和r分别为当前区间的左右端点。下面假设我们对l到r子段内的数字进行划分。取pivot = a[l]为分界线,将<pivot的数字移到左边,>pivot的数字移到右边,然后将pivot放在中间。假设pivot的位置是k。
(3)、如果左边区间[l…k-1]长度大于1,则对于新的区间[l…k-1],重复调用上面的过程。
(4)、如果右边区间[k+1…r]长度大于1,则设置新的区间[k+1, r],重复调用上面的过程。
(5)、当整个过程结束以后,整个序列排序完毕。
快速排序是一个不稳定的排序算法。
4.2、时间复杂度
最好的情况:O(nlogn)
最差的情况:O(n^2)
平均时间复杂度:O(nlogn)
4.3、代码
/**
* 快速排序
*/
void quickSort(int arr[], int left, int right){
if (left < right){
// 把数组分块
int pivot = partition(arr, left, right);
// 基准元素左边递归
quickSort(arr, left, pivot-1);
// 基准元素右边递归
quickSort(arr, pivot+1, right);
}
}
int partition(int arr[], int left, int right){
int pivot = arr[left]; // 选取第一个为基准元素
while(left<right){
/* 先从右往移动,直到遇见小于 pivot 的元素 */
while (left<right && arr[right]>=pivot){
right--;
}
arr[left] = arr[right]; // 记录小于 pivot 的值
/* 再从左往右移动,直到遇见大于 pivot 的元素 */
while(left<right && arr[left]<=pivot){
left++;
}
arr[right] = arr[left]; // 记录大于 pivot 的值
}
arr[left] = pivot; // 记录基准元素到当前指针指向的区域
return left; // 返回基准元素的索引
}
5、归并排序
5.1、算法描述
假设我们要对数组a[1…n]排序。初始化左端点l=1,右端点r=n。
(1)、下面假设我们对l到r子段内的数字进行划分。取l和r的中点mid,将l到mid的元素看成第一个子段的部分,将mid+1到r的部分看成第二个子段的部分。两边分别进入下一层,重复调用上面的过程。直到子段长度为1,返回上一层。
(2)、当算法阶段返回到当前层时,使用归并操作合并下一层的左右两个有序序列,形成本层的有序序列,继续返回上一层。
(3)、当整个过程结束以后,整个序列排序完毕。
归并排序是稳定的排序算法。
5.2、时间复杂度
O(nlogn)
5.3、代码
void Merge(int a[], int low, int mid, int high){
int b[high - low + 1];
int i = low, j = mid + 1, k = 0;
while (i <= mid && j <= high){
if (a[i] <= a[j]){ // 将a数组分成两份
b[k++] = a[i++];
}
else{
b[k++] = a[j++];
}
}
while (i <= mid) b[k++] = a[i++];
while (j <= high) b[k++] = a[j++];
i = low;
for (int temp = 0; temp < k && i <= high; temp++){ // 将原本数组的内容进行改变
a[i++] = b[temp];
}
}
void MergeSort(int a[], int low, int high){
if (low <high){
int mid = (low + high) / 2; // 从中间划分两个子集
MergeSort(int a, low, mid); // 对左侧子集进行递归排序
MergeSort(int a, mid + 1, high); // 对右侧子集进行递归排序
Merge(a, low, mid, high); // 归并
}
}
总结
以上就是比较常用的排序算法,要注意的点在于排序稳定性的判断。
参考
青舟智学
快速排序算法
C++ 归并排序与快速排序