排序算法


快速排序(重点)
基本步骤
- 确定分界点
- 调整区间
- 递归处理
基本思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
注意边界问题,分界点和递归参数选取不当可能造成死循环
##时间复杂度
最优O(nlogn)\最差O(n2)
##空间复杂度
最优O(logn)\最差O(n)
具体复杂度的推导可以参考:快速排序的时间复杂度和空间复杂度分析(图文结合)-CSDN博客
#经典快排
#include<iostream>
using namespace std;
const int N = 1e6 + 10;
int q[N];
int n;
void quik_sort(int q[], int l,int r){
    if(l>=r)
    return;
    
    int mid = q[(l+r)>>1],i=l-1,j=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]);
    }
    quik_sort(q, l, j);
    quik_sort(q, j+1, r);
    
}
int main(){
    scanf("%d",&n);
    
    for(int i = 0;i < n;i++)
    scanf("%d",&q[i]);
    
    quik_sort(q,0,n-1);
    
    for(int i = 0;i < n;i++)
    printf("%d ",q[i]);
    
    return 0;
}
#第k个数
#include<iostream>
using namespace std;
const int N = 1e6+10;
int k,n;
int q[N];
int quik_sort(int l,int r,int k){
    if(l>=r)
    return q[l];
    
    int mid = q[(l+r)>>1],i = l-1,j = 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]);
    }
    #选择对应区间
    int num = j-l+1;
    if(num>=k) return quik_sort(l,j,k);
    else return quik_sort(j+1,r,k-num);
}
int main(){
    scanf("%d",&n);
    scanf("%d",&k);
    
    for(int i = 0;i<n;i++)
    scanf("%d",&q[i]);
    
    printf("%d",quik_sort(0,n-1,k));
    return 0;
}
归并排序(重点)
基本步骤
- 确定分界点,mid=(l+r)/2
- 递归排序left和right
- 归并,合二为一
基本思想:归并排序算法有两个基本的操作,一个是分,也就是把原数组划分成两个子数组的过程。另一个是治,它将两个有序数组合并成一个更大的有序数组。将待排序的线性表不断地切分成若干个子表,直到每个子表只包含一个元素,这时,可以认为只包含一个元素的子表是有序表。将子表两两合并,每合并一次,就会产生一个新的且更长的有序表,重复这一步骤,直到最后只剩下一个子表,这个子表就是排好序的线性表。
时间复杂度
O(nlogn)
空间复杂度
O(n),归并排序需要一个与原数组相同长度的数组做辅助来排序
参考文章:排序——归并排序(Merge sort)-CSDN博客
#经典归并排序
#include<iostream>
using namespace std;
const int N = 1e5+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 num = 0,i = l,j = mid+1;
    while(i<=mid&&j<=r){
        if(q[i]<=q[j]) temp[num++]=q[i++];
        else temp[num++]=q[j++];
    }
    while(i<=mid) temp[num++]=q[i++];
    while(j<=r) temp[num++]=q[j++];
    
    for(int i=l ,j = 0;i<=r;i++,j++) q[i]=temp[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;
}
#逆序对数量
#include<iostream>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
int n;
int q[N],temp[N];
ll merge_sort(int l,int r){
    if(l>=r)
    return 0;
    
    int mid = (l+r)>>1;
    ll num = 0;
    num+=merge_sort(l,mid);
    num+=merge_sort(mid+1,r);
    
    int i = l,j = mid+1,k = 0;
    while(i<=mid&&j<=r){
        if(q[i]<=q[j]) temp[k++]=q[i++];
        else{
            temp[k++]=q[j++];
            num+=mid-i+1;
        }
    }
    while(i<=mid) temp[k++]=q[i++];
    while(j<=r) temp[k++]=q[j++];
    
    for(int i=l,j=0;i<=r;i++,j++) q[i]=temp[j];
    return num;
}
int main(){
    scanf("%d",&n);
    for(int i = 0;i<n;i++)
    scanf("%d",&q[i]);
    
    printf("%lld",merge_sort(0,n-1));
    return 0;
}
冒泡排序
算法步骤
冒泡排序(Bubble Sort)是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。
时间复杂度
平均:O(n2),最好O(n),最坏O(n2)
空间复杂度
O(1)
#经典款冒泡排序
void bubble_sort(int arr[], int len) {
        int i, j;
        for (i = 0; i < len - 1; i++)
                for (j = 0; j < len - 1 - i; j++)
                        if (arr[j] > arr[j + 1])
                                swap(arr[j], arr[j + 1]);
}
 #增设判断的冒泡排序,不过感觉用处不大
 void bubbleSort(int array[], int length)
 {
     int i, j, tmp;
     int flag = 1;
     
     if (1 >= length) return;
 
     for (i = length-1; i > 0; i--, flag = 1){ 
         for (j = 0; j < i; j++){
             if (array[j] > array[j+1]){
                 tmp = array[j];
                 array[j] = array[j+1];
                 array[j+1] = tmp;
                 flag = 0;
             }   
         }   
         if (flag) break;
     }   
 }
#改进的冒泡排序(鸡尾酒排序)就是把最大的数往后面冒泡的同时,最小的数也往前面冒泡
void cocktailSort(int arr [] , int n) {
        int L = 0;
        while(L < n) {
            for(int i = L; i < n-1; i++) 
            {
                if(arr[i] > arr[i+1])
                {
                        int temp = arr[i];
                        arr[i] = arr[i+1]; 
                        arr[i+1] = temp;
                }
            }
            n--;
            for(int i = n-1; i > L; i--) 
            {
                if(arr[i] < arr[i-1])
                {
                        int temp = arr[i];
                        arr[i] = arr[i-1]; 
                        arr[i-1] = temp;
                }
            }
            L++;
        }
}
选择排序
算法步骤
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。重复第二步,直到所有元素均排序完毕。
时间复杂度
O(n2)
空间复杂度
O(1)
#经典选择排序
void Select_Sort(int arr[], int n)   
{
	for (int i = 0; i < n-1; i++) {
		int min = i;
		for (int j = i; j < n; j++) {
			if (arr[min] > arr[j]) {
				min = j;
			}
		}
		if (min != i) {
			swap(arr[i], arr[min]);
		}
	}
插入排序
算法步骤
插入排序是一种最简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
详细资料:排序算法——直接插入排序(图文超详细!)-CSDN博客
时间复杂度
平均:O(n2),最好O(n),最坏O(n2)
空间复杂度
O(1)
#经典插入排序
void insert_sort(int arr[], int n) {
    for (int i = 1; i < n; i++) {
        int key = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
}
希尔排序(插入排序的优化版本)
算法步骤
先选定一个整数,把待排序文件中所有记录分成多个组。所有距离为的记录分在同一组内,并对每一组内的记录进行直接插入排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。
详细资料:七大排序算法——希尔排序,通俗易懂的思路讲解与图解(完整Java代码)-CSDN博客
时间复杂度
平均:O(n1.3),最好O(n),最坏O(n2)
空间复杂度
O(1)
#经典希尔排序
void shell_sort(int arr[], int n) {
    for (int gap = n / 2; gap > 0; gap /= 2) {
        for (int i = gap; i < n; i++) {
            int key = arr[i];
            int j = i - gap;
            while (j >= 0 && arr[j] > key) {
                arr[j + gap] = arr[j];
                j -= gap;
            }
            arr[j + gap] = key;
        }
    }
}
堆排序
算法步骤
1.首先将待排序的数组构造成一个大根堆,此时,整个数组的最大值就是堆结构的顶端
2.将顶端的数与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1
3.将剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组
详细讲解:【从堆的定义到优先队列、堆排序】 10分钟看懂必考的数据结构——堆_哔哩哔哩_bilibili
时间复杂度
平均:O(nlogn),最好O(nlogn),最坏O(nlogn)
空间复杂度
O(1)
#构建最大堆,升序
void heapify(int arr[], int n, int i) {
    int largest = i;
    int l = 2 * i + 1;
    int r = 2 * i + 2;
    if (l < n && arr[l] > arr[largest]) largest = l;
    if (r < n && arr[r] > arr[largest]) largest = r;
    if (largest != i) {
        swap(arr[i], arr[largest]);
        heapify(arr, n, largest);
    }
}
void heap_sort(int arr[], int n) {
    for (int i = n / 2 - 1; i >= 0; i--) {
        heapify(arr, n, i);
    }
    for (int i = n - 1; i > 0; i--) {
        swap(arr[0], arr[i]);
        heapify(arr, i, 0);
    }
}
计数排序
算法步骤:
- 找出待排序的数组中最大和最小的元素
- 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
- 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
- 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减1
局限:
- 当数列最大最小值差距过大时,并不适用于计数排序
- 当数列元素不是整数时,并不适用于计数排序
计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。 当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(nlog(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(nlog(n)), 如归并排序,堆排序)
详细参考:算法系列十一:十大经典排序算法之——计数排序-CSDN博客
时间复杂度
O (n+range)
空间复杂度
O(range)
#经典计数排序
void counting_sort(int arr[], int n) {
    int max = *max_element(arr, arr + n);
    int min = *min_element(arr, arr + n);
    int range = max - min + 1;
    int count[range] = {0};
    int output[n];
    for (int i = 0; i < n; i++) {
        count[arr[i] - min]++;
    }
    for (int i = 1; i < range; i++) {
        count[i] += count[i - 1];
    }
    for (int i = n - 1; i >= 0; i--) {
        output[count[arr[i] - min] - 1] = arr[i];
        count[arr[i] - min]--;
    }
    for (int i = 0; i < n; i++) {
        arr[i] = output[i];
    }
}
桶排序(计数排序的升级版)
算法步骤
参考:
时间复杂度
最好:O(n+k),最坏:所有数据都集中在一个桶中,桶排序退化为桶内排序算法的时间复杂度,例如O(n^2)
空间复杂度
O(n+k)
#经典桶排序
void bucket_sort(int arr[], int n) {
    int max = *max_element(arr, arr + n);
    int min = *min_element(arr, arr + n);
    int range = max - min + 1;
    int bucketCnt = 10;
    vector<int> buckets[bucketCnt];
    
    // 计算桶的大小
    int bucketSize = 1;
    while (max) {
        max /= 10;
        bucketSize *= 10;
    }
    bucketSize /= 10;
    // 将元素分配到桶中
    for (int i = 0; i < n; i++) {
        int idx = arr[i] / bucketSize;
        buckets[idx].push_back(arr[i]);
        // 对每个桶进行插入排序
        for (int j = int(buckets[idx].size()) - 1; j > 0; j--) {
            if (buckets[idx][j] < buckets[idx][j - 1]) {
                std::swap(buckets[idx][j], buckets[idx][j - 1]);
            }
        }
    }
    // 将桶中的元素合并到原数组中
    for (int i = 0, k = 0; i < bucketCnt; i++) {
        for (int j = 0; j < int(buckets[i].size()); j++) {
            arr[k++] = buckets[i][j];
        }
    }
}
基数排序
算法步骤
参考:最详细的【基数排序】—排序算法,思路清晰动图保姆级讲解,五分钟搞懂!_csdn基数排序动图-CSDN博客
时间复杂度
O(K*n),k一般取10
空间复杂度
O(N+K)
#经典基数排序
void radix_sort(int arr[], int n) {
    int max = *max_element(arr, arr + n);
    int exp = 1;
    int output[n];
    while (max / exp > 0) {
        int count[10] = {0};
        for (int i = 0; i < n; i++) {
            count[arr[i] / exp % 10]++;
        }
        for (int i = 1; i < 10; i++) {
            count[i] += count[i - 1];
        }
        for (int i = n - 1; i >= 0; i--) {
            output[count[arr[i] / exp % 10] - 1] = arr[i];
            count[arr[i] / exp % 10]--;
        }
        for (int i = 0; i < n; i++) {
            arr[i] = output[i];
        }
        exp *= 10;
    }
}
vscode代码整合

 
                   
                   
                   
                   
       
           
                 
                 
                 
                 
                 
                
               
                 
                 
                 
                 
                
               
                 
                 扫一扫
扫一扫
                     
              
             
                   895
					895
					
 被折叠的  条评论
		 为什么被折叠?
被折叠的  条评论
		 为什么被折叠?
		 
		  到【灌水乐园】发言
到【灌水乐园】发言                                
		 
		 
    
   
    
   
             
            


 
            