zoukankan      html  css  js  c++  java
  • 内排序<一>

    概述

    内排序顾名思义待排序元素总数相对与内存而言较小,整个排序过程可以在内存中进行。反之,如果待排序元素总数较多,不能全部放入内存,排序过程需要访问外存,称之为外排序。内排序算法有序多下面是较常见的几种排序算法:

    按照时间复杂度来划分的话,主要有两种:

    非线性时间复杂度,下面列举的排序算法的是基于关键字比较和移动两种操作实现的称为“比较排序”,《算法导论》证明过对于任何比较排序在最坏情况下的要Ω(nlgn)次比较来进行排序。

    1、简单选择排序算法

    2、直接插入排序算法

    3、冒泡排序

    4、快速排序

    5、两路合并排序

    6、堆排序

    线性时间排序,下面的三种算法的用非比较的一些操作来确定顺序。

    1、计数排序

    2、基数排序

    3、桶排序

    另外,排序算法的稳定性是指序列中有关键字值相同元素,排序后原来的相对位置不变。

    还有就是原地排序(In place)即在任何时候,数组中只有常数个元素存储在输入数组以外。

    实现

    下面对于常见的六种比较排序用C++分别实现

      1 #include<iostream>
    2 #include<string.h>
    3 #include<memory.h>
    4 using namespace std;
    5
    6
    7 template <class T>
    8 inline void Swap(T &a,T &b){
    9 T temp = a;
    10 a = b;
    11 b = temp;
    12
    13 }
    14 //简单的冒泡排序算法
    15 /**
    16 *冒泡排序思想:对于从小到大排序,第一趟在序列(A[0]~A[n-1])中从前往后进行两个相邻的元素比较,如果后者小
    17 *则交换,比较n-1次。第一趟结束后最大的元素被交换(沉)到底部了。因而下一趟排序只需在子序列(A[0]~A[n-2])中进行!
    18 *冒泡排序最多进行n-1趟
    19 *分析:O(n^2)
    20 */
    21
    22 template <typename T>
    23 void BubbleSortCustom(T *p,int size){
    24 if (size <=1) return ;
    25 int i ,j,temp;
    26 for(i = 0;i<size;i++) {
    27 for(j = 0;j<size-1-i;j++){
    28 if(p[j]>p[j+1]){
    29 Swap(p[j],p[j+1]);
    30 }
    31 }
    32 }
    33 }
    34 //冒泡排算法改进版
    35 /**
    36 *用上面的冒泡排序趟数(n-1)次和第i趟比较次n-i次,的是固定,与初始序列无关
    37 *这应该是最坏的情况,因为有些序列可能没有交换元素,即提前排好序了
    38 *下面的实现最多进行(n-1)趟,
    39 *分析:冒泡排序偏爱基于有序的序列
    40 *最好的情况即有序 n-1比较,最坏的进行n-1趟,第i趟比较n-i次。n(n-i)/2 O(n^2);
    41 */
    42 template<typename T>
    43 void BubbleSort(T *p,int size){
    44 int i,j,last,temp;
    45 i = size-1; //最多进行size-1 趟
    46 while(i>0){
    47 last = 0;
    48 for(j=0; j<i; j++){
    49 if(p[j]>p[j+1]){
    50 Swap(p[j],p[j+1]);
    51
    52 last = j; //如果后面没有交换,说明后面已经排序好了,下一趟只需要比较last次
    53 }
    54 }
    55 i = last; //如果一趟中没有交换元素last为0结束循环
    56 }
    57
    58 }
    59 //简单选择排序
    60 /**
    61 *在一个子序列中选择一个最小(大)元素与待排序列第一个元素交换
    62 *算法执行时间与初始序列无关
    63 *分析:最好,最坏和平均情况时间复杂度的为O(n^2)a;
    64 *不稳定排序
    65 */
    66 template <class T>
    67 void SelectSort(T *p,int size){
    68 int small;
    69 int i,j,temp;
    70 for(i = 0; i< size-1;i++){
    71 small = i;
    72 for(j=i+1;j<size;j++ ){ //获取最小元素下标
    73 if(p[j]<p[small]){
    74 small = j ;
    75 }
    76 }
    77 //最小元素与待排序列第一个元素交换
    78 Swap(p[i],p[small]);
    79 }
    80
    81 }
    82
    83 //插入排序
    84 /**
    85 *将序列的第一个元素作为一个有序序列,然后将剩下的n-1个元素按照大小
    86 *依次插入到该有序序列,每插入一个元素后依然保持该序列有序
    87 *插入排序可以将有序序列放在新的空间中,不过通常是在原有序列上修改
    88 *分析:直接插入排序必须进行n-1趟,最好的情况下每趟比较1次
    89 *最坏情况下(非递增),第i趟比较i次 复杂度为O(n^2)
    90 */
    91 template <typename T>
    92 void InsertSort(T *p,int size){
    93 int i,j;
    94 for(i = 1;i<size;i++){
    95 T temp = p[i];
    96 //在长度为i的有序序列中查找插入位置
    97 j = i;
    98 while(j>0 && temp < p[j-1]){
    99 p[j] = p[j-1]; //p[j-1]元素后移
    100 j--;
    101 }
    102 //找到插入位置后
    103 p[j] = temp;
    104
    105 }
    106 }
    107 //两路合并排序
    108 /**
    109 *将有n个元素的序列看成是n个长度为1的有序子序列,得到n/2(取上)个长度为2或1的有序子序列
    110 *再两两合并,直到得到一个长度为n的有序序列时结束。
    111 */
    112 //合并两个相邻有序子序列,合并后的结果依然是有序的
    113 //q指向n大小的临时内存
    114 template<class T>
    115 void Merge(T *p,int i1,int j1,int i2,int j2,T *q){
    116 int i = i1,j = i2,k = 0;
    117 while(i<=j1 && j<=j2){
    118 if(p[i] < p[j])
    119 q[k++] = p[i++];
    120 else
    121 q[k++] = p[j++];
    122 }
    123 //将剩余的放入q中
    124 while(i<=j1) q[k++] = p[i++];
    125 while(j<=j2) q[k++] = p[j++];
    126 //导入到p中
    127 for(i = 0;i<k;i++) p[i1++] = q[i];
    128 }
    129 //迭代实现合并排序
    130 template <class T>
    131 void MergeSort(T *p,int size){
    132 T *q = new T[size]; //申请一块Size大小的内存
    133 int i1,j1,i2,j2;
    134 int num = 1;
    135 //确定子序列的边界
    136 while(num < size){
    137 i1 = 0;
    138 while(i1+num <size){
    139 i2 = i1+num;
    140 j1 = i2-1;
    141 if(i2+num>size)
    142 j2 = size-1;
    143 else
    144 j2 = i2+num -1;
    145 Merge(p,i1,j1,i2,j2,q);
    146 i1 = j2+1;//下一次合并第一个序列的起始
    147 }
    148 num *= 2; //元素个数扩大一倍
    149 }
    150
    151 delete[] q;
    152 }
    153
    154 /**
    155 *
    156 *
    157 */
    158 //递归实现快速排序
    159 /**
    160 *数组A[p,r] 被分割成两个(可能空)字数组A[p..q-1] 和A[q+1..r]使得A[p,q-1]中的每一个
    161 *元素的小于等于A[q],而且,小于等于A[q+1,r]中的元素,不稳定排序
    162 *快速排序要点在于如何分割序列,下面提供两种方法
    163 */
    164 //第一种分割方法,以A[0]为分割标志
    165 template <class T>
    166 void QSort(T *p,int left,int right){
    167 int i,j;
    168 if(left<right){
    169 i = left;
    170 j = right + 1;
    171 do{
    172 do i++; while(p[i]<p[left]);
    173 do j--; while(p[j]>p[left]);
    174 if(i<j) Swap(p[i],p[j]);
    175
    176 }while(i<j);
    177 //j为分割点
    178 Swap(p[left],p[j]);
    179
    180 QSort(p,left,j-1);
    181 QSort(p,j+1,right);
    182
    183 }
    184
    185 }
    186 template <class T>
    187 void QuickSort1(T *p,int size){
    188 QSort(p,0,size-1);
    189 }
    190
    191 //快排第二种分割方法
    192 template <class T>
    193 int Partition(T *p,int left,int right){
    194 T x = p[right];
    195 int i = left - 1;
    196 int j;
    197 for(j=left;j<right;j++){
    198 if(p[j]<=x){
    199 i = i+1;
    200 Swap(p[i],p[j]);
    201 }
    202
    203 }
    204 Swap(p[i+1],p[right]);
    205 return i+1;
    206 }
    207 template <class T>
    208 void QSort2(T *p,int left ,int right){
    209 if(left<right){
    210 int q = Partition(p,left,right);
    211 QSort2(p,left,q-1);
    212 QSort2(p,q+1,right);
    213 }
    214
    215 }
    216 template <class T>
    217 void QuickSort2(T *p,int size){
    218 QSort2(p,0,size-1);
    219
    220 }
    221
    222 //堆排序
    223 /**
    224 *利用最小堆或者是最大堆来实现排序,时间复杂度为nlgn
    225 *堆排序和插入排序一样,是一种原地(in place)排序算法
    226 *二叉堆数据结构是一种数组对象,是数组抽象出来的。
    227 *对于完全二叉树,数组的第i节点的父节点为i/2 左子节点 2i,右子节点2i+1
    228 *最大堆是父不小于子,最小堆是父不大于子
    229 */
    230 //保持堆的性质
    231 //对于最大堆,插入一个节点时,其左右子树的是最大堆。为了保证插入新节点后依然能够保持
    232 //最大堆的性质,就需要调整堆
    233 template <class T>
    234 void MaxHeapIFY(T *p,int i, int heapsize){
    235 int l = i<<1; //左子
    236 int r = (i<<1)+1;//右子
    237 int largest = 0;
    238 if(l<heapsize && p[l]>p[i])
    239 largest = l;
    240 else
    241 largest = i;
    242 if(r<heapsize && p[r]>p[largest])
    243 largest = r;
    244 if(largest != i){
    245 Swap(p[i],p[largest]);
    246 MaxHeapIFY(p,largest,heapsize);
    247 }
    248
    249
    250 }
    251 //建堆排序
    252 //堆排序通过删堆来实现,从根节点来删除,将要删除的根节点(A[0])与堆的最后一个元素(A[n-1])互换
    253 //然后删除堆的尾部即减小堆的大小为n-1,然后再从新调整i=0节点 ,保持堆的性质。
    254 template <class T>
    255 void HeapSort(T *p,int size){
    256 //建最大堆
    257 for(int i=size/2; i>=1;i--){
    258 MaxHeapIFY(p,i,size);
    259 }
    260 //排序
    261 int heapsize = size;
    262 for(int j = size-1;j>0;j--){
    263 Swap(p[0],p[j]);
    264 heapsize = heapsize -1;
    265 MaxHeapIFY (p,0,heapsize);
    266 }
    267
    268 }
    269 int main(){
    270
    271 double a[] = { 29,18,9,12,10,23,4,5.3};
    272 // InsertSort(a,8);
    273 //SelectSort(a,8);
    274 //MergeSort(a,8);
    275 //QuickSort2(a,8);
    276 HeapSort(a,8);
    277 for(int i =0;i<8;i++)
    278 cout<<a[i]<<" ";
    279
    280 cout<<endl;
    281
    282 return 0;
    283 }


    #include<iostream>
    #include<string.h>
    #include<memory.h>
    using namespace std;


    template <class T>
    inline void Swap(T &a,T &b){
    T temp = a;
    a = b;
    b = temp;

    }
    //简单的冒泡排序算法
    /*
    *
    *冒泡排序思想:对于从小到大排序,第一趟在序列(A[0]~A[n-1])中从前往后进行两个相邻都元素比较,如果后者小
    *则交换,比较n-1次。第一趟结束后最大都元素被交换(沉)到底部了。因而下一趟排序只需在子序列(A[0]~A[n-2])中进行!
    *冒泡排序最多进行n-1趟
    *分析:O(n^2)
    */

    template <typename T>
    void BubbleSortCustom(T *p,int size){
    if (size <=1) return ;
    int i ,j,temp;
    for(i = 0;i<size;i++) {
    for(j = 0;j<size-1-i;j++){
    if(p[j]>p[j+1]){
    Swap(p[j],p[j+1]);
    }
    }
    }
    }
    //冒泡排算法改进版
    /*
    *
    *用上面的冒泡排序趟数(n-1)次和第i趟比较次n-i次,都是固定,与初始序列无关
    *这应该是最坏的情况,因为有些序列可能没有交换元素,即提前排好序了
    *下面的实现最多进行(n-1)趟,
    *分析:冒泡排序偏爱基于有序都序列
    *最好的情况即有序 n-1比较,最坏都进行n-1趟,第i趟比较n-i次。n(n-i)/2 O(n^2);
    */
    template<typename T>
    void BubbleSort(T *p,int size){
    int i,j,last,temp;
    i = size-1; //最多进行size-1 趟
    while(i>0){
    last = 0;
    for(j=0; j<i; j++){
    if(p[j]>p[j+1]){
    Swap(p[j],p[j+1]);

    last = j; //如果后面没有交换,说明后面已经排序好了,下一趟只需要比较last次
    }
    }
    i = last; //如果一趟中没有交换元素last为0结束循环
    }

    }
    //简单选择排序
    /*
    *
    *在一个子序列中选择一个最小(大)元素与待排序列第一个元素交换
    *算法执行时间与初始序列无关
    *分析:最好,最坏和平均情况时间复杂度都为O(n^2)a;
    *不稳定排序
    */
    template <class T>
    void SelectSort(T *p,int size){
    int small;
    int i,j,temp;
    for(i = 0; i< size-1;i++){
    small = i;
    for(j=i+1;j<size;j++ ){ //获取最小元素下标
    if(p[j]<p[small]){
    small = j ;
    }
    }
    //最小元素与待排序列第一个元素交换
    Swap(p[i],p[small]);
    }

    }

    //插入排序
    /*
    *
    *将序列都第一个元素作为一个有序序列,然后将剩下都n-1个元素按照大小
    *依次插入到该有序序列,每插入一个元素后依然保持该序列有序
    *插入排序可以将有序序列放在新都空间中,不过通常是在原有序列上修改
    *分析:直接插入排序必须进行n-1趟,最好都情况下每趟比较1次
    *最坏情况下(非递增),第i趟比较i次 复杂度为O(n^2)
    */
    template <typename T>
    void InsertSort(T *p,int size){
    int i,j;
    for(i = 1;i<size;i++){
    T temp = p[i];
    //在长度为i都有序序列中查找插入位置
    j = i;
    while(j>0 && temp < p[j-1]){
    p[j] = p[j-1]; //p[j-1]元素后移
    j--;
    }
    //找到插入位置后
    p[j] = temp;

    }
    }
    //两路合并排序
    /*
    *
    *将有n个元素都序列看成是n个长度为1的有序子序列,得到n/2(取上)个长度为2或1的有序子序列
    *再两两合并,直到得到一个长度为n的有序序列时结束。
    */
    //合并两个相邻有序子序列,合并后都结果依然是有序的
    //q指向n大小都临时内存
    template<class T>
    void Merge(T *p,int i1,int j1,int i2,int j2,T *q){
    int i = i1,j = i2,k = 0;
    while(i<=j1 && j<=j2){
    if(p[i] < p[j])
    q[k++] = p[i++];
    else
    q[k++] = p[j++];
    }
    //将剩余都放入q中
    while(i<=j1) q[k++] = p[i++];
    while(j<=j2) q[k++] = p[j++];
    //导入到p中
    for(i = 0;i<k;i++) p[i1++] = q[i];
    }
    //迭代实现合并排序
    template <class T>
    void MergeSort(T *p,int size){
    T *q = new T[size]; //申请一块Size大小都内存
    int i1,j1,i2,j2;
    int num = 1;
    //确定子序列都边界
    while(num < size){
    i1 = 0;
    while(i1+num <size){
    i2 = i1+num;
    j1 = i2-1;
    if(i2+num>size)
    j2 = size-1;
    else
    j2 = i2+num -1;
    Merge(p,i1,j1,i2,j2,q);
    i1 = j2+1;//下一次合并第一个序列都起始
    }
    num *= 2; //元素个数扩大一倍
    }

    delete[] q;


    /**
    *
    *
    */
    //递归实现快速排序
    /*
    *
    *数组A[p,r] 被分割成两个(可能空)字数组A[p..q-1] 和A[q+1..r]使得A[p,q-1]中的每一个
    *元素都小于等于A[q],而且,小于等于A[q+1,r]中的元素,不稳定排序
    *快速排序要点在于如何分割序列,下面提供两种方法
    */
    //第一种分割方法,以A[0]为分割标志
    template <class T>
    void QSort(T *p,int left,int right){
    int i,j;
    if(left<right){
    i = left;
    j = right + 1;
    do{
    do i++; while(p[i]<p[left]);
    do j--; while(p[j]>p[left]);
    if(i<j) Swap(p[i],p[j]);

    }while(i<j);
    //j为分割点
    Swap(p[left],p[j]);

    QSort(p,left,j-1);
    QSort(p,j+1,right);

    }

    }
    template <class T>
    void QuickSort1(T *p,int size){
    QSort(p,0,size-1);
    }

    //快排第二种分割方法
    template <class T>
    int Partition(T *p,int left,int right){
    T x = p[right];
    int i = left - 1;
    int j;
    for(j=left;j<right;j++){
    if(p[j]<=x){
    i = i+1;
    Swap(p[i],p[j]);
    }

    }
    Swap(p[i+1],p[right]);
    return i+1;
    }
    template <class T>
    void QSort2(T *p,int left ,int right){
    if(left<right){
    int q = Partition(p,left,right);
    QSort2(p,left,q-1);
    QSort2(p,q+1,right);
    }

    }
    template <class T>
    void QuickSort2(T *p,int size){
    QSort2(p,0,size-1);

    }

    //堆排序
    /*
    *
    *利用最小堆或者是最大堆来实现排序,时间复杂度为nlgn
    *堆排序和插入排序一样,是一种原地(in place)排序算法
    *二叉堆数据结构是一种数组对象,是数组抽象出来的。
    *对于完全二叉树,数组都第i节点都父节点为i/2 左子节点 2i,右子节点2i+1
    *最大堆是父不小于子,最小堆是父不大于子
    */

    int main(){

    double a[] = { 29,18,9,12,10,23,4,5.3};
    int *p = NULL;
    // BubbleSort(p,0);
    // InsertSort(a,8);
    // cout<<x<<" "<<y <<endl;
    //SelectSort(a,8);
    //MergeSort(a,8);
    QuickSort2(a,8);
    for(int i =0;i<8;i++)
    cout<<a[i]<<" ";

    cout<<endl;

    return 0;
    }


    总结分析

     对于上面已经实现的几种比较排序算法总结如下:

        简单选择排序: 算法执行时间与初始序列无关,最好、最坏、平均时间复杂度的为O(n^2),是不稳定排序方法

      直接插入排序:与初始序列排列有关,最好的情况下(顺序)时间复杂度为O(n),最坏(反序)是O(n^2)

      冒泡排序       :最好是O(n),最坏O(n^2)

      快速排序        : 快速排序适合元素多的情形,偏爱无序的序列,分割元素的选择直接会影响到快排的效率,如果每一层递归上做的划分的是最坏的划分,时间复杂度为O(n^2),平均情况下快排的时间复杂度为O(nlgn),快速排序适用与已知第K(k比较小)个小元素A在表中的位置,要尽可能短的时间内在较多的元素中找出前K个小元素,且找出的元素需要按照非递减排列,这样可以用A作为分割元素进行一趟快速排序即可划分出前k个元素了,然后简单排序即可。

        两路合并排序,不是原地排序,却是稳定排序,额外的空间复杂度为O(n) ,时间复杂度为O(nlgn)与初始序列无关。

        堆排序: 对排序是原地排序,不稳定排序,时间复杂度为O(nlgn),建堆时间为O(n),排序n-1次调整代价为O(lgn).

       

  • 相关阅读:
    JavaWeb学习笔记——开发动态WEB资源(七)bookapp
    JavaWeb学习笔记——开发动态WEB资源(六)ServletConfig和ServletContext
    Eclipse学习笔记——快捷键
    JavaWeb学习笔记——开发动态WEB资源(五)servlet身份验证
    JavaWeb学习笔记——开发动态WEB资源(四)打印当前使用的是get方法
    JavaWeb学习笔记——开发动态WEB资源(三)显示当前时间
    JavaWeb学习笔记——开发动态WEB资源(二)HelloWord
    JavaWeb学习笔记——开发动态WEB资源(一)Java程序向浏览器输出数据
    JavaWeb学习笔记——Tomcat相关
    JavaWeb学习笔记——访问静态HTML网页
  • 原文地址:https://www.cnblogs.com/xddy/p/2193597.html
Copyright © 2011-2022 走看看