一:快速排序:
1:快速排序 ----基本思想(分治)
l ______________________________ r
(1)确定分界点:
1.取左边界 q[l]
2.取中间值 q[(l+r)/2]
3.取随机数 q[r] 随机
**(2)调整区间:
令所有小于x的数在左边
令所有大于x的数在右边(等于任取一边)
(3)递归处理左右两段
实现方法(暴力)
(1)开两个额外数组
a[ ] b[ ]
(2)扫描整个区间
q[l~r]
q[i]<=x, x->a[ ]
q[i]>x , x->b[ ]
(3) a[ ]->q[ ]
b[ ]->q[ ]
2:实现方法(优化):
(1)两个指针 *i从l开始向后扫描,直到*i扫描到一个大于x的数
*j从r开始向前扫描,直到*j扫描到一个小于x的数
执行swap(i , j)
直到i,j相遇为止
(2)递归处理
3:代码模板:
#include<iostream>
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 x=q[l] , i=l-1 , j=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);
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",q+i);
}
quick_sort(q,0,n-1);
for(int i=0;i<n;i++){
printf("%d",q[i]);
}
return 0;
}
4:注意事项:
边界问题
(1)
void quick_sort(int q[],int l,int r){
if(l>=r){return;}
int x=q[l] , i=l-1 , j=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);
}
(2):
void quick_sort(int q[],int l,int r){
if(l>=r){return;}
int x=q[r] , i=l-1 , j=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,i-1);
quick_sort(q,i,r);
}
两种均可
注意:快速排序不够稳定
5:优缺点:
优点:平均情况下具有较好的性能,是一种常用且高效的排序算法。
缺点:最坏情况下可能出现O(n^2)的时间复杂度。
注意事项:为避免最坏情况,可以选择合适的基准元素和优化分区策略。
二、归并排序
1:归并排序----核心思想(分治)
left right
|——————|———————|
(1)确定分界点:mid=(l+r)/ 2
(2)递归排序left,right,使其变为两个有序链表
**(3)归并----合二为一(本质为双指针算法)
指针min
left |——————
指针min
right |——————
res:
2:运行原理:
不断比较两个指针大小,将两者中较小者放入一个新的数组(res)中,然后使其所在数组的指针++
如果两者大小相同,动left的指针(使归并排序成为*稳定的排序)
稳定的排序:
如果两个数相同,在排序中不交换两者位置,则称该排序为稳定排序,否则为不稳定排序
示例:
1 3 5 7 9
2 4 6 8 10
res:1 2 3 4 5 6 7 8 9 10
时间复杂度分析:
n
|————————| 每一层为O(n)
n/2 n/2
|————|————| 有logn层
n/4 n/4 n/4 n/4
|——|——|——|——| 总复杂度为O(n logn)
3:代码模板:
#include<iostream>
using namespace std;
const int N = 1e6+10;
int n;
int q[N],tmp[N];
void merge_sort(int q[],int l,int r){
if(l>=r){return;}
int mid = l + r >> 1; // >>是位运算,等价于/2,但速度更快
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];
}
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",q+i);
}
merge_sort(q,0,n-1);
for(int i=0;i<n;i++){
printf("%d",q[i]);
}
return 0;
}
归并排序是比较稳定的
4:优缺点:
优点:具有稳定的时间复杂度O(nlogn),效果稳定且较好。
缺点:需要额外的存储空间。
注意事项:在实际应用中,可以使用迭代方式实现归并排序来避免递归带来的额外开销。
三:选择排序
1:主要思想:
在长度为N的无序数组中,第一次遍历n-1个数,找到最小的数值与第一个元素交换;
第二次遍历n-2个数,找到最小的数值与第二个元素交换;.........以此类推。
第n-1次遍历,找到最小的数值与第n-1个元素交换,排序完成。
2:代码模版:
public static void select_sort(int array[],int lenth){
for(int i=0;i<lenth-1;i++){
int minIndex = i;
for(int j=i+1;j<lenth;j++){
if(array[j]<array[minIndex]){
minIndex = j;
}
}
if(minIndex != i){
int temp = array[i];
array[i] = array[minIndex];
array[minIndex] = temp;
}
}
}
3:优缺点:
优点:实现简单,思路清晰。
缺点:时间复杂度较高,在大规模数据集上性能较差。
注意事项:每次选择最小(或最大)元素的过程相对耗时,不适合大规模数据集。
四:插入排序:
1:主要思想:
在要排序的一组数中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。(例如斗地主时整牌)
2:代码模版:
public static void insert_sort(int array[],int lenth){
int temp;
for(int i=0;i<lenth-1;i++){
for(int j=i+1;j>0;j--){
if(array[j] < array[j-1]){
temp = array[j-1];
array[j-1] = array[j];
array[j] = temp;
}else{ //不需要交换
break;
}
}
}
}
3:优缺点:
优点:对于小规模数据集和基本有序的数据集效果较好。
缺点:在大规模数据集上性能较差。
注意事项:可以通过二分查找确定插入位置来提高性能。
五:冒泡排序:
1:主要思想:
(1)基本思想:两个数比较大小,较大的数下沉,较小的数冒起来。
(2)过程:
比较相邻的两个数据,如果第二个数小,就交换位置。
从后向前两两比较,一直到比较最前两个数据。最终最小数被交换到起始的位置,这样第一个最小数的位置就排好了。
继续重复上述过程,依次将第2.3...n-1个最小数排好位置
2:代码模版:
public static void BubbleSort(int [] arr){
int temp;//临时变量
for(int i=0; i<arr.length-1; i++){ //表示趟数,一共arr.length-1次。
for(int j=arr.length-1; j>i; j--){
if(arr[j] < arr[j-1]){
temp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = temp;
}
}
}
}
3:优缺点:
优点:实现简单,代码易于理解和实现。
缺点:时间复杂度较高,在大规模数据集上性能较差。
注意事项:可以通过优化算法减少不必要的比较和交换操作。
暂时只总结了常见常用的算法...........
六:各种算法的时间复杂度总结:
- 冒泡排序(Bubble Sort):最坏情况和平均情况的时间复杂度都是O(n^2)。
- 插入排序(Insertion Sort):最坏情况和平均情况的时间复杂度都是O(n^2)。
- 选择排序(Selection Sort):最坏情况和平均情况的时间复杂度都是O(n^2)。
- 快速排序(Quick Sort):最坏情况的时间复杂度是O(n^2),但平均情况下的时间复杂度是O(nlogn)。
- 归并排序(Merge Sort):最坏情况和平均情况下的时间复杂度都是O(nlogn)。
- 堆排序(Heap Sort):最坏情况和平均情况下的时间复杂度都是O(nlogn)。
- 希尔排序(Shell Sort):最坏情况和平均情况下的时间复杂度在不同的增量序列下会有所不同,但通常介于O(nlogn)和O(n^2)之间。
- 计数排序(Counting Sort):时间复杂度是O(n+k),其中k是待排序元素的范围。
- 桶排序(Bucket Sort):最坏情况下的时间复杂度是O(n^2),但平均情况下的时间复杂度是O(n+k),其中k是桶的数量。
- 基数排序(Radix Sort):最坏情况和平均情况下的时间复杂度都是O(d*(n+k)),其中d是最大数字的位数,k是基数。