数据结构与算法(六)

数据结构与算法

第一章 绪论
第二章 线性表
第三章 树与二叉树
第四章 图
第五章 查找
第六章 排序



第六章 内部排序

一、基本概念

排序算法的稳定性:假定在待排序的记录集中,存在多个具有相同关键字值的记录,若经过排序,这些记录的相对次序仍然保持不变,即在原序列中,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((n1)log2k)+O(k)=O(nlog2k)
归并趟数: 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(nlog2klogkm)=O(nlog2m)
所以K路归并的CPU时间与K无关
置换-选择排序
减少初始归并段个数m也可以减少归并趟数S
最佳归并树
使外存读写次数最少
最佳归并树应该是一棵“正则树”
若初始归并段不足以构成一棵严格k 叉树时,需要添加长度为0 的“虚段”,按照哈夫曼树的原则,权为0的叶子离树根最远。因此,最佳归并树如上所示。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值