数据结构与算法
第一章 绪论
 第二章 线性表
 第三章 树与二叉树
 第四章 图
 第五章 查找
 第六章 排序
文章目录
第六章 内部排序
一、基本概念
排序算法的稳定性:假定在待排序的记录集中,存在多个具有相同关键字值的记录,若经过排序,这些记录的相对次序仍然保持不变,即在原序列中,ki=kj且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。
二、冒泡排序
将待排序的记录看作是竖着排列的“气泡”,关键字较小的记录比较轻,从而要往上浮。
 对这个“气泡”序列进行n-1遍(趟)处理。所谓一遍(趟)处理,就是自底向上检查一遍这个序列,并注意两个相比较的关键字的顺序是否正确。如果发现顺序不对,即“轻”的记录在下面,就交换它们的位置。
void BubbleSort(int n, LIST &A){
    for(int i =1;i<=n-1;i++){
        int sp = 0;
        for (int j = n;j>=i+1;j--){
            if(A[j].key < A[j-1].key){
                swap(A[j],A[j-1]);
                sp = 1;
            }
        }
        if(sp == 0){
            return ;//若标志位为0,说明已经有序,则直接返回
        }
    }
}
稳定性:稳定的排序方法
 时间复杂度:
    
     
      
       
        O
       
       
        (
       
       
        
         n
        
        
         2
        
       
       
        )
       
      
      
       O(n^2)
      
     
    O(n2)
 空间复杂度:
    
     
      
       
        O
       
       
        (
       
       
        1
       
       
        )
       
      
      
       O(1)
      
     
    O(1)
三、快速排序
void QuickSort(int i ,int j){
    keytype pivot;
    int k;
    int pivotindex;
    pivotindex = FindPivot(i,j);
    if(pivotindex !=0){
        pivot = A[pivotindex].key;
        k = Partition(i,j,pivot);
        QuickSort(i,k-1);
        QuickSort(k,j)
    }
}
int FindPivot(int i,int j){
    keytype firstkey = A[i].key;
    for(int k = i+1;k<=j;k++){
        if(A[k].key > firstkey){
            return k;
        }
        else if(A[k].key<firstkey) {
            return i;
        }
    }
    return 0;//若A[i]到A[j]全都相同,则返回0,否则返回前两个不同关键字中较大的。
}
int Partition(int i,int j, keytype pivot){
    int l = i;r = j;
    do{
        while(A[r].key >= pivot){
            r--;
        }
        while(A[l].key < pivot){
            l++;
        }
        if(l<=r){
            swap(A[l],A[r]);
        }
    }while(l <= r)
    return l;
}
稳定性:不稳定
 时间复杂度:
    
     
      
       
        O
       
       
        (
       
       
        n
       
       
        
         
          log
         
         
          
         
        
        
         2
        
       
       
        n
       
       
        )
       
      
      
       O(n \log_{2}{n})
      
     
    O(nlog2n)
 空间复杂度:
    
     
      
       
        O
       
       
        (
       
       
        
         
          log
         
         
          
         
        
        
         2
        
       
       
        n
       
       
        )
       
      
      
       O(\log_{2}{n})
      
     
    O(log2n)
 最坏情况:
 时间复杂度:
    
     
      
       
        O
       
       
        (
       
       
        
         n
        
        
         2
        
       
       
        )
       
      
      
       O(n^2)
      
     
    O(n2)
 空间复杂度:
    
     
      
       
        O
       
       
        (
       
       
        n
       
       
        )
       
      
      
       O(n)
      
     
    O(n)
四、直接选择排序
直接选择排序与冒泡排序的区别在:冒泡排序每次比较后,如果发现顺序不对立即进行交换,而选择排序不立即进行交换,而是找出最小关键字记录后再进行交换。
void SelectSort(int n, LIST A){
    keytype lowkey;
    int i,j,lowindex;
    for(int i = 1;i<n;i++){
        lowindex = i;
        lowkey = A[i].key;
        for(j = i+1;j<=n;j++){
            if(A[j].key<lowkey){
                lowkey = A[j];
                lowindex = j;
            }
        }
        if(i != lowindex){
            swap(A[i],A[lowindex]);
        }
    }
}
稳定性:不稳定排序
 时间复杂度:
    
     
      
       
        O
       
       
        (
       
       
        
         n
        
        
         2
        
       
       
        )
       
      
      
       O(n^2)
      
     
    O(n2)
 空间复杂度:
    
     
      
       
        O
       
       
        (
       
       
        1
       
       
        )
       
      
      
       O(1)
      
     
    O(1)
五、堆排序
首先将待排序的记录序列用完全二叉树表示;
 然后完全二叉树构造成一个堆,此时,选出了堆中所有记录关键字的最小者;
 最后将关键字最小者从堆中移走,并将剩余的记录再调整成堆,这样又找出了次小的关键字记录,以此类推,直到堆中只有一个记录。
void HeapSort(int n,LIST A){
    int i;
    for(i = n/2;i>=1;i--){
        PushDown(i,n);
    }
    for(i = n;i<=2;i--){
        PushDowm(1.i-1);
    }
}
void PushDown(int first, int last){
    int r = first;
    while(r <= last/2){
        if((r == last/2) && (last%2 == 0)){
            if(A[r].key > A[2*r].key){
                swap(A[r],A[2*r]);
            }
            r = last;
        }else if((A[r].key>A[2*r].key)&& (A[2*r].key<=A[2*r+1].key)){
            swap(A[r],A[2*r]);
            r = 2*r;
        }else if ((A[r].key>A[2*r].key)&& (A[2*r].key>A[2*r+1].key)){
            swap(A[r],A[2*r+1]);
            r = 2*r+1;
        }else{
            r = last;
        }
    }
}
稳定性:不稳定
 时间复杂度:
    
     
      
       
        O
       
       
        (
       
       
        
         n
        
        
         2
        
       
       
        )
       
      
      
       O(n^2)
      
     
    O(n2)
 空间复杂度:
    
     
      
       
        O
       
       
        (
       
       
        1
       
       
        )
       
      
      
       O(1)
      
     
    O(1)
六、插入排序
void InsertSort(int n,LIST A){
    int i,j;
    A[0].key = -infi;
    for(i = 1;i<=n;i++){
        j = i;
        while(A[j].key<A[j-1].key){
            swap(A[j].A[j-1]);
            j = j-1;
        }
    }
}
稳定性:稳定
 时间复杂度:
    
     
      
       
        O
       
       
        (
       
       
        
         n
        
        
         2
        
       
       
        )
       
      
      
       O(n^2)
      
     
    O(n2)
 空间复杂度:
    
     
      
       
        O
       
       
        (
       
       
        1
       
       
        )
       
      
      
       O(1)
      
     
    O(1)
七、希尔排序
将整个待排序记录分割成若干个子序列,在子序列内分别进行直接插入排序,使整个序列逐步向基本有序发展;待整个序列中的记录基本有序时,对全体记录进行直接插入排序。
void ShellSort(int n,LIST A){
    int i,j,d;
    for(d=n/2;d>=1;d=d/2){
        for(i = d+1;i<=n;i++){
            A[0].key = A[i].key;
            j = i-d;
            while(j>0 && A[0].key <A[j].key){
                A[j+d] = A[j];
                j = j-d;
            }
            A[j+d] = A[0];
        }
    }
}
希尔排序的时间性能在O(n2)和O(nlog2n)之间
 希尔排序所需的比较次数和记录的移动次数
 约为O(n1.3 )
八、归并排序
void MergeSort(LIST A,LIST B,int low, int high){
    int mid = (low+high)/2;
    if(low<high){
        MergeSort(A,B,low,mid);
        MergeSort(A,B,mid+1,high);
        Merge(low,mid,high,A,B);
    }
}
void Merge(int s,int m,int t,LIST A,LIST B){
    int i = s,j=m+1k=s;
    while(i<=m && j<=t){
        B[k++] = (A[i].key<=A[j].key) ? A[i++] : A[j++];
    }
    while(i<= m){
        B[K++] = A[i++];
    }
    while(j <= t){
        B[K++] = A[j++];
    }
}
时间复杂度: O ( n log  2 n ) O(n \log_{2}{n}) O(nlog2n)
九、基数排序(桶排序)
void RadixSort(int figure, QUEUE &A){
    QUEUE Q[10];
    records data;
    int pass,r,i;
    for(pass = 1;pass<=figure;pass++){
        for(int i = 0;i<=9;i++){
            MAKENULL(Q[i]);
        }
        while(!empty(A)){
            data = front(A);
            dequeue(A);
            r = Radix(data.key,pass);
            enqueue(data,Q[r]);
        }
        for(i = 0;i<=9;i++){
            while(!empty(Q[i])){
                data = front(Q[i]);
                dequeue(Q[i]);
                enqueue(data,A);
            }
        }
    }
}
时间复杂度:
    
     
      
       
        O
       
       
        (
       
       
        d
       
       
        (
       
       
        n
       
       
        +
       
       
        r
       
       
        )
       
       
        )
       
      
      
       O(d(n+r))
      
     
    O(d(n+r))
 空间复杂度:
    
     
      
       
        O
       
       
        (
       
       
        n
       
       
        +
       
       
        r
       
       
        )
       
      
      
       O(n+r)
      
     
    O(n+r)
第七章 外部排序
外部排序主要考虑访问磁盘的次数,即I/O次数
 第一阶段:初始归并段形成
 第二阶段:多路归并
若把内存区域等份地分为3个缓冲区。其中的两个为输入缓冲区, 一个为输出缓冲区, 可以在内存中利用简单2路归并函数MergeSort()实现2路归并。
 当输出缓冲区装满250个记录时,就输出到磁盘。
 如果归并期间某个输入缓冲区空了,就立即向该缓冲区继续装入所对应归并段的一块记录信息,使之与另一个输入缓冲区的剩余记录归并,
 K路平衡归并与败者树
 K路归并树n个记录处理时间为
    
     
      
       
        O
       
       
        (
       
       
        (
       
       
        n
       
       
        −
       
       
        1
       
       
        )
       
       
        ∗
       
       
        
         
          log
         
         
          
         
        
        
         2
        
       
       
        k
       
       
        )
       
       
        +
       
       
        O
       
       
        (
       
       
        k
       
       
        )
       
       
        =
       
       
        O
       
       
        (
       
       
        n
       
       
        ⋅
       
       
        
         
          log
         
         
          
         
        
        
         2
        
       
       
        k
       
       
        )
       
      
      
       O((n-1)*\log_{2}{k})+O(k) = O(n·\log_{2}{k})
      
     
    O((n−1)∗log2k)+O(k)=O(n⋅log2k)
 归并趟数:
    
     
      
       
        O
       
       
        (
       
       
        
         
          log
         
         
          
         
        
        
         k
        
       
       
        m
       
       
        )
       
      
      
       O(\log_{k}{m})
      
     
    O(logkm)
 总时间为
    
     
      
       
        O
       
       
        (
       
       
        n
       
       
        ⋅
       
       
        
         
          log
         
         
          
         
        
        
         2
        
       
       
        k
       
       
        ⋅
       
       
        
         
          log
         
         
          
         
        
        
         k
        
       
       
        m
       
       
        )
       
       
        =
       
       
        O
       
       
        (
       
       
        n
       
       
        ⋅
       
       
        
         
          log
         
         
          
         
        
        
         2
        
       
       
        m
       
       
        )
       
      
      
       O(n·\log_{2}{k}·\log_{k}{m})=O(n·\log_{2}{m})
      
     
    O(n⋅log2k⋅logkm)=O(n⋅log2m)
 所以K路归并的CPU时间与K无关
 置换-选择排序
 减少初始归并段个数m也可以减少归并趟数S
 最佳归并树
 使外存读写次数最少
 最佳归并树应该是一棵“正则树”
 若初始归并段不足以构成一棵严格k 叉树时,需要添加长度为0 的“虚段”,按照哈夫曼树的原则,权为0的叶子离树根最远。因此,最佳归并树如上所示。










