算法基础——算法排序(一)

简介: 算法基础——算法排序(一)

一、快速排序

快速排序算法模板

 
void quick_sort(int q[], int l, int r)
{
    //递归的终止情况
    if (l >= r) return;
    //第一步:分成子问题
    int i = l - 1, j = r + 1, x = q[l + r >> 1];
    while (i < j)
    {
        do i ++ ; while (q[i] < x);
        do j -- ; while (q[j] > x);
        if (i < j) swap(q[i], q[j]);
    }
    //第二步:递归处理子问题
    quick_sort(q, l, j), quick_sort(q, j + 1, r);
   //第三步:子问题合并.快排这一步不需要操作,但归并排序的核心在这一步骤
}
思路:

(1).有数组 q, 左端点 l, 右端点r

(2).确定划分边界 x.(这里可以是r,l,(l+r)/2,都可以)

(3).将 q 分为 <=x 和 >=x 的两个小数组

(4).i 的含义: i 之前的元素都 ≤x, 即 q[l..i−1] ≤x

(5).j的含义: j 之后的元素都 ≥x, 即 qq[j+1..r] ≥x

(6).结论: while 循环结束后, q[l..j] ≤x,q[j+1..r]≥x     q[j+1..r] ≥x

(7).简单不严谨证明:

while 循环结束时, i≥j

若 i>j, 显然成立

若 i=j

∵ 最后一轮循环中两个 do−while 循环条件都不成立

∴ q[i]≥x,q[j]≤x

∴ q[i]=q[j]=x

∴ 结论成立

(8).递归处理两个数组

过程

循环不变式:q[l..i] <= x q[j..r] >= x

1. 初始化

循环开始之前i = l - 1, j = r + 1

则q[l..i],q[j..r]为空,循环不变式显然成立

2. 循环

假设某轮循环开始前循环不变式成立,即q[l..i] <= x, q[j..r] >= x

执行循环体

do i++; while(q[i] < x);

会使得 q[l..i-1] <= x, q[i] >= x

do j--; while(q[j] > x);

会使得 q[j+1..r] >= x, q[j] <= x

if(i < j) swap(q[i], q[j]);

会使得 q[l..i] <= x, q[j..r] >= x

注意:由于使用do-while循环,所以i和j一定会自增,使得循环会继续下去,但是如果采用while循环(i和j的初始化做出对应的变更),i和j在特殊情况下不自增的话,循环就会卡死

例如:

while(q[i] < x) i++;

while(q[j] > x) j--;

当q[i]和q[j]都为 x 时, i 和 j 都不会更新,导致 while 陷入死循环

3.终止

循环结束时,i >= j

二、归并排序

归并排序算法模板

 
void merge_sort(int q[], int l, int r)
{
     //递归的终止情况
    if (l >= r) return;
 
    int mid = l + r >> 1;
    //第二步:递归处理子问题
    merge_sort(q, l, mid);
    merge_sort(q, mid + 1, r);
     //第三步:合并子问题
    int k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r)
        if (q[i] <= q[j]) tmp[k ++ ] = q[i ++ ];
        else tmp[k ++ ] = q[j ++ ];
 
    while (i <= mid) tmp[k ++ ] = q[i ++ ];
    while (j <= r) tmp[k ++ ] = q[j ++ ];
 
    for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];
}

思路:

(1).有数组 q, 左端点 l, 右端点 r

(2).确定划分边界 mid

(3).递归处理子问题 q[l..mid], q[mid+1..r]

(4).合并子问题

a.主体合并

至少有一个小数组添加到 tmp 数组中

b.收尾

可能存在的剩下的一个小数组的尾部直接添加到 tmp 数组中

c.复制回来

tmp 数组覆盖原数组

过程:

1.初始化

k = 0, tmp[0..k-1] 为空,显然成立

2.循环

假设某轮循环开始之前,循环不变式成立

q[i] <= q[j],tmp[k] = q[i]

其中, q[i] <= q[i+1..mid], q[i] <= q[j] <= q[j+1..r]

∴ q[i] 是剩下的所有数中最小的一个

q[i] > q[j] 时,同理可以得到 tmp[k] = q[j] 是剩下数中最小的一个

∴ tmp[k] 是剩下数中最小的一个

∴ k自增之后,下轮循环开始之前,tmp[0..k-1]保存从小到大排序的最小k个数

3.终止

i > mid 或 j > r

则 q[l..mid] 和 q[mid+1..r] 其中一个数组的数都已遍历

tmp[0..k-1]保存从小到大排序的最小k个数

三、算法排序

sort(q,q+n);

例题:

给定你一个长度为 n的整数数列。

请你使用快速排序对这个数列按照从小到大进行排序。

并将排好序的数列按顺序输出。

输入格式

输入共两行,第一行包含整数 n。

第二行包含 n 个整数(所有整数均在 1∼10^9 范围内),表示整个数列。

输出格式

输出共一行,包含 n 个整数,表示排好序的数列。

数据范围

1≤n≤100000

输入样例:

 
5
3 1 2 4 5

输出样例:

 
1 2 3 4 5

快速排序:

1. 
2. #include <bits/stdc++.h>
3.  
#include <bits/stdc++.h>
 
using namespace std;
 
const int N = 1e6 + 10;
 
int n;
int q[N];
 
void quick_sort(int q[] , int l , int r)
{
    if(l >= r) return ;
    
    int i = l - 1 , j = r + 1;
    int mid = q[l + r >> 1];
    
    while(i < j)
    {
        do i ++; while (q[i] < mid);
        do j --; while (q[j] > mid);
        if(i < j) swap(q[i] , q[j]);
    }
    quick_sort(q , l , j);
    quick_sort(q , j + 1 , r);
}
int main()
{
    cin >> n;
    for(int i = 0; i < n; i ++)
        cin >> q[i];
        
    quick_sort(q , 0 , n - 1);
    
    for(int i = 0; i < n; i ++)
        cout << q[i] << " ";
    
    return 0;
}
6. const int N = 1e6 + 10;
7. 
8. int n;
9. int q[N];
10. 
11. void quick_sort(int q[] , int l , int r)
12. {
13. if(l >= r) return ;
14. 
15. int i = l - 1 , j = r + 1;
16. int mid = q[l + r >> 1];
17. 
18. while(i < j)
19.     {
20. do i ++; while (q[i] < mid);
21. do j --; while (q[j] > mid);
22. if(i < j) swap(q[i] , q[j]);
23.     }
24. quick_sort(q , l , j);
25. quick_sort(q , j + 1 , r);
26. }
27. int main()
28. {
29.     cin >> n;
30. for(int i = 0; i < n; i ++)
31.         cin >> q[i];
32. 
33. quick_sort(q , 0 , n - 1);
34. 
35. for(int i = 0; i < n; i ++)
36.         cout << q[i] << " ";
37. 
38. return 0;
39. }

归并排序

 
#include <bits/stdc++.h>
 
using namespace std;
 
const int N = 1e6 + 10;
 
int n;
int q[N] , temp[N];
 
void merge_sort(int q[] , int l , int r)
{
    //第一步:递归的终止情况
    if(l >= r) return ;
    
    int mid = l + r >> 1; 
    
    //第二步:递归处理子问题
    merge_sort(q , l , mid);
    merge_sort(q , mid + 1 , r);
    
    //第三步:合并子问题
    int k = 0;
    int i = l , j = mid + 1;
    while(i <= mid && j <= r)
    {
        if(q[i] < q[j]) temp[k ++] = q[i ++];//这里<就是升序,>就是降序
        else temp[k ++] = q[j ++];
    }
    while(i <= mid) temp[k ++] = q[i ++];
    while(j <= r) temp[k ++] = q[j ++];
    
    for(int i = l, k = 0; i <= r; i ++, k ++) q[i] = temp[k];
}
int main()
{
    cin >> n;
    for(int i = 0; i < n; i ++)
        cin >> q[i];
        
    merge_sort(q , 0 , n - 1);
    
    for(int i = 0; i < n; i ++)
        cout << q[i] << " ";
    
    return 0;
}

算法库函数:

 
#include <bits/stdc++.h>
 
using namespace std;
 
const int N = 1e6 + 10;
 
int n;
int q[N];
 
int main()
{
    cin >> n;
    for(int i = 0; i < n; i ++)
        cin >> q[i];
    sort(q , q + n);
    for(int i = 0; i < n; i ++)
        cout << q[i] << " ";
    return 0;
}


目录
相关文章
|
5月前
|
算法 C++
【洛谷 P1223】排队接水(贪心算法+结构体排序)
该问题要求安排$n$个人的排队顺序以最小化平均等待时间。每个人接水需时$T_i$,解决方案是让接水时间短的人优先。给定$n\leq1000$和$t_i\leq10^6$,代码示例使用C++实现,通过排序使时间从小到大排列,然后计算平均等待时间。样例输入为10个人的时间数组,输出为优化后的排队顺序及平均等待时间(291.90)。
53 0
|
3月前
|
算法
【算法】二分查找——在排序数组中查找元素的第一个和最后一个位置
【算法】二分查找——在排序数组中查找元素的第一个和最后一个位置
|
11天前
|
搜索推荐 算法 C语言
【排序算法】八大排序(上)(c语言实现)(附源码)
本文介绍了四种常见的排序算法:冒泡排序、选择排序、插入排序和希尔排序。通过具体的代码实现和测试数据,详细解释了每种算法的工作原理和性能特点。冒泡排序通过不断交换相邻元素来排序,选择排序通过选择最小元素进行交换,插入排序通过逐步插入元素到已排序部分,而希尔排序则是插入排序的改进版,通过预排序使数据更接近有序,从而提高效率。文章最后总结了这四种算法的空间和时间复杂度,以及它们的稳定性。
53 8
|
11天前
|
搜索推荐 算法 C语言
【排序算法】八大排序(下)(c语言实现)(附源码)
本文继续学习并实现了八大排序算法中的后四种:堆排序、快速排序、归并排序和计数排序。详细介绍了每种排序算法的原理、步骤和代码实现,并通过测试数据展示了它们的性能表现。堆排序利用堆的特性进行排序,快速排序通过递归和多种划分方法实现高效排序,归并排序通过分治法将问题分解后再合并,计数排序则通过统计每个元素的出现次数实现非比较排序。最后,文章还对比了这些排序算法在处理一百万个整形数据时的运行时间,帮助读者了解不同算法的优劣。
42 7
|
1月前
|
搜索推荐 Shell
解析排序算法:十大排序方法的工作原理与性能比较
解析排序算法:十大排序方法的工作原理与性能比较
51 9
|
1月前
|
算法 搜索推荐 Java
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
基数排序是一种稳定的排序算法,通过将数字按位数切割并分配到不同的桶中,以空间换时间的方式实现快速排序,但占用内存较大,不适合含有负数的数组。
24 0
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
|
1月前
|
算法
❤️算法笔记❤️-(每日一刷-83、删除排序链表中的重复项)
❤️算法笔记❤️-(每日一刷-83、删除排序链表中的重复项)
31 0
|
1月前
|
存储 算法 搜索推荐
算法进阶之路:Python 归并排序深度剖析,让数据排序变得艺术起来!
算法进阶之路:Python 归并排序深度剖析,让数据排序变得艺术起来!
72 0
|
3月前
|
搜索推荐 算法 Java
现有一个接口DataOperation定义了排序方法sort(int[])和查找方法search(int[],int),已知类QuickSort的quickSort(int[])方法实现了快速排序算法
该博客文章通过UML类图和Java源码示例,展示了如何使用适配器模式将QuickSort类和BinarySearch类的排序和查找功能适配到DataOperation接口中,实现算法的解耦和复用。
40 1
现有一个接口DataOperation定义了排序方法sort(int[])和查找方法search(int[],int),已知类QuickSort的quickSort(int[])方法实现了快速排序算法
|
3月前
|
算法 搜索推荐 Java
算法实战:手写归并排序,让复杂排序变简单!
归并排序是一种基于“分治法”的经典算法,通过递归分割和合并数组,实现O(n log n)的高效排序。本文将通过Java手写代码,详细讲解归并排序的原理及实现,帮助你快速掌握这一实用算法。
42 0