408---必须能手搓的算法

发布时间 2023-12-19 17:19:50作者: TLSN

一、快速排序

无需多言

// 2023-12-19
#include <iostream>
#include <cstring>
using namespace std;


void debug(int A[],int n){
    for(int i=0;i<n;i++) printf("%d ",A[i]);
    puts("");
}


void Qsort(int A[],int left,int right){
    if(left >= right) return ;
    int l,r,pivot;
    l=left,r=right,pivot = A[left];
    while(l<r){
        while(l<r && A[r] >= pivot) r--;
        A[l] = A[r];
        while(l<r && A[l] <= pivot) l++;
        A[r] = A[l];
    }
    A[l] = pivot;
    
    Qsort(A,left,l-1);
    Qsort(A,l+1,right);

}


int main(){
    int A[20] = {0};    
    for(int i=0;i<=19;i++) A[i]=50-i+1;
    debug(A,20);
    Qsort(A,0,19);
    debug(A,20);

    return 0;
}

 

 

二、归并排序

// 2023-12-19
#include <iostream>
#include <cstring>
using namespace std;

void debug(int A[],int n){
    for(int i=0;i<n;i++) printf("%d ",A[i]);
    puts("");
}


void TMerge(int A[],int left,int mid,int right){
    int i,j,k;
    int *B = (int *)malloc(sizeof(int) * 20);
    
    i=left,j = mid+1,k = left;
    while(i<=mid && j<=right){
        if(A[left] < A[right]) B[k++] = A[i++];
        else B[k++] = A[j++];
    }
    while(i<=mid) B[k++] = A[i++];
    while(j<=right) B[k++] = A[j++];
    for(i=left;i<=right;i++){
        A[i] = B[i];
        B[i] = 0;
    }

    free(B);
}

void Merge(int A[],int left,int right){
    if(left >= right) return ;

    int mid = left+right >> 1;
    Merge(A,left,mid);
    Merge(A,mid+1,right);

    TMerge(A,left,mid,right);
}


int main(){
    int A[20];
    for(int i=0;i<=19;i++) A[i] = 50-i;
    debug(A,20);
    Merge(A,0,19);
    debug(A,20);
    
    return 0;
}

三、并查集及其优化

#include <stdio.h>
#include <math.h>
int find(int A[],int m);
void Init(int A[],int len){
    for(int i=0;i<len;i++)A[i]=-1; 
}

void debug(int A[],int len){
    for(int i=0;i<len;i++) printf("%3d ",A[i]);
    puts("");
}
void Union(int A[],int x1,int x2){
    int root1 = find(A,x1);
    int root2 = find(A,x2);
    if(root1 != root2){
        A[root1] = root2;        // 把x1合并到x2中 
    }
}

int find(int A[],int m){
    if(A[m] < 0) return m;
    return find(A,A[m]);
}


int optfind(int A[],int m){
    if(A[m] < 0) return m;
    int root = find(A,A[m]);
    A[m] = root;               // 让路过的元素都指向root
    return root;    
}
void opt1Union(int A[],int x1,int x2){
    int root1 = optfind(A,x1);
    int root2 = optfind(A,x2);
    if(root1 != root2){
        A[root1] = root2;        // 把x1合并到x2中 
    }
}

void opt2Union(int A[],int x1,int x2){      // 结点小的树合并到结点多的树,俗称大树合并到小树
    int root1 = optfind(A,x1);
    int root2 = optfind(A,x2);
    if(root1 == root2) return ;
    if(abs(A[root1]) > abs(A[root2])){  // 越大的结点越多
        A[root1] += A[root2];
        A[root2] = root1;         
    }else{
        A[root2] += A[root1];
        A[root1] = root2;  
    }
}


int main(){
    int A[20] = {0};
    Init(A,20);


    // Union(A,1,2);
    // debug(A,10);
    // Union(A,3,2);
    // debug(A,10);
    // Union(A,5,6);
    // debug(A,10);
    // Union(A,7,6);
    // debug(A,10);
    // Union(A,2,4);
    // debug(A,10);
    // Union(A,6,4);    
    // debug(A,10);

/*
           4

    2              6

1       3       5       7

*/
/*
 -1   2  -1  -1  -1  -1  -1  -1  -1  -1 
 -1   2  -1   2  -1  -1  -1  -1  -1  -1 
 -1   2  -1   2  -1   6  -1  -1  -1  -1 
 -1   2  -1   2  -1   6  -1   6  -1  -1 
 -1   2   4   2  -1   6  -1   6  -1  -1 
 -1   2   4   2  -1   6   4   6  -1  -1 
*/


    // opt1Union(A,1,2);
    // debug(A,10);
    // opt1Union(A,3,2);
    // debug(A,10);
    // opt1Union(A,5,6);
    // debug(A,10);
    // opt1Union(A,7,6);
    // debug(A,10);
    // opt1Union(A,2,4);
    // debug(A,10);
    // opt1Union(A,1,6);
    // debug(A,10);




/*
    4
            
    2                            6

1       3                   5       7

*/
// =========> opt1Union(1,6)
/*

                6

1       2       4        5      7
        
        3

*/

/*
 -1   2  -1  -1  -1  -1  -1  -1  -1  -1 
 -1   2  -1   2  -1  -1  -1  -1  -1  -1 
 -1   2  -1   2  -1   6  -1  -1  -1  -1 
 -1   2  -1   2  -1   6  -1   6  -1  -1 
 -1   2   4   2  -1   6  -1   6  -1  -1 
 -1   4   4   2   6   6  -1   6  -1  -1 
*/
// 在把1合并到6结点的时候可以发现,find函数并不是把1所在的子树的所有结点都连接到6结点下,而是先把1所在子树的结点都指向1所在子树的根,再把根指向6所在结点的根
// 可以看这种图: https://img2023.cnblogs.com/blog/2433096/202311/2433096-20231130194814117-562472168.png
    


    opt2Union(A,1,2);
    debug(A,10);
    opt2Union(A,3,2);
    debug(A,10);
    opt2Union(A,5,6);
    debug(A,10);
    opt2Union(A,7,6);
    debug(A,10);
    opt2Union(A,2,4);
    debug(A,10);
    opt2Union(A,1,6);
    debug(A,10);

/*

 -1   2  -2  -1  -1  -1  -1  -1  -1  -1 
 -1   2  -3   2  -1  -1  -1  -1  -1  -1 
 -1   2  -3   2  -1   6  -2  -1  -1  -1 
 -1   2  -3   2  -1   6  -3   6  -1  -1
 opt2Union(A,2,4); //我们原本想让2合并到4,但由于2是大树,故只能4合并到2
 -1   2  -4   2   2   6  -3   6  -1  -1 
 -1   2  -7   2   2   6   2   6  -1  -1 

*/


    return 0;
}

 

简易版(无分析数据版

// 2023-12-19
// 估计是考前最后一次手搓并查集及其优化了

#include <iostream>
#include <cstring>
using namespace std;



void Init(int A[],int n){
    for(int i=0;i<n;i++) A[i] = -1;
}


int find(int A[],int x){
    return A[x] > 0 ? A[x] : x;
}

int optfind(int A[],int x){
    if(A[x] < 0) return x;
    int Root = optfind(A,A[x]);
    A[x] = Root;                // 比起朴素的find操作,增添了这项代码
    return Root;
}

void Union(int A[],int x1,int x2){   // x1的根合并到x2中
    int Root1 = find(A,x1); int Root2 = find(A,x2);
    if (Root1 == Root2) return ;
    A[Root1] = Root2;
    
    return ;
}

void opt1Union(int A[],int x1,int x2){
    int Root1 = optfind(A,x1); int Root2 = optfind(A,x2);
    if (Root1 == Root2) return ;
    A[Root1] = Root2;
    
    return ;
}

int abs(int m){// 取绝对值
    return m>0 ? m : -m;
}
void opt2Union(int A[],int x1,int x2){          // 小树合并到大树上
    int Root1 = optfind(A,x1); int Root2 = optfind(A,x2);
    if(Root1 == Root2) return ;
    if(abs(A[Root1]) > abs(A[Root2])){
        A[Root1] += A[Root2];
        A[Root2] = Root1;
    }else{
        A[Root2] += A[Root1];
        A[Root1] = Root2;
    }
    
}


void debug(int A[],int n){
    for(int i=0;i<n;i++) printf("%3d ",A[i]);
    puts("");
}


int main(){
    int A[20] = {0};
    Init(A,20);
    // debug(A,20);

// 朴素算法
// 一次Find 最坏时间复杂度 : O(n)
// 总体是O(n^2)的时间复杂度
   
   
    // Union(A,1,2);
    // debug(A,10);
    // Union(A,3,2);
    // debug(A,10);
    // Union(A,5,6);
    // debug(A,10);
    // Union(A,7,6);
    // debug(A,10);
    // Union(A,2,4);
    // debug(A,10);
    // Union(A,6,4);
    // debug(A,10);

// 优化一---find路径压缩
// 一次Find 最坏时间复杂度 : O(α(n))
// 总体是 : O(n · α(n))  

    // opt1Union(A,1,2);
    // debug(A,10);
    // opt1Union(A,3,2);
    // debug(A,10);
    // opt1Union(A,5,6);
    // debug(A,10);
    // opt1Union(A,7,6);
    // debug(A,10);
    // opt1Union(A,2,4);
    // debug(A,10);
    // opt1Union(A,1,6);
    // debug(A,10);



// 优化二---大树合并小树
// 一次Find 最坏时间复杂度 : $O(log_2n)$
// 总体是 : $O(log_2n)$

    opt2Union(A,1,2);
    debug(A,10);
    opt2Union(A,3,2);
    debug(A,10);
    opt2Union(A,5,6);
    debug(A,10);
    opt2Union(A,7,6);
    debug(A,10);
    opt2Union(A,2,4);
    debug(A,10);
    opt2Union(A,1,6);
    debug(A,10);


    
    
    return 0;
}