• 排序小结


      插入排序,冒泡排序和选择排序是基本的排序方法,平均情况下时间复杂度是O(n2)。

      插入排序对于规模很小的元素序列(n<=25)很有效,最好情况下只需要n-1次比较,不需要交换操作。在平均情况和最差情况下,比较和交换都是O(n2)的。

      改进的冒泡排序在最好情况下只需要一次冒泡过程,n-1次比较。

      选择排序的比较操作与初始排列无关,比较次数总是O(n2),最好情况下,不移动,最差情况移动不超过3(n-1)次。

      三种基本排序方法只需要一个辅助元素,主要用于元素个数n<10K的情况。

      归并排序的一个特性是性能与输入元素序列无关,时间复杂度总是O(nlgn)。主要缺点是直接执行需要O(n)的附加内存空间。另一个优势是稳定排序算法。

      堆排序是一种高效的内部排序算法,平均情况时间复杂度为O(nlgn),没有什么最坏情况会导致堆排序的运行明显变慢,基本不需要额外空间。但是堆排序不太可能提供比快速排序更好的平均性能,不稳定算法。

      快速排序是最通用的高效内部排序算法,平均情况时间复杂度为O(nlgn),所需要的额外内存是O(lgn)。不稳定算法,在元素序列有序是会退化,时间复杂度增加到O(n2),空间复杂度增加到O(n)。可以用找三个元素中位数来避免。

      希尔排序的时间复杂度介于基本排序算法和高效算法之间,代码简单,不需要什么额外内存,不稳定算法。对于中等规模(n<=1000),希尔排序是一种很好的选择。

      计数排序的时间复杂度为O(k+n),当k=O(n)时,为O(n)。计数排序的下界优于O(nlgn),因为它不是一个比较排序算法,计数排序是用来输入元素的实际值来确定它们在数组中的位置。计数排序的一个重要性质就是他是稳定的,它经常用作基数排序算法的一个子过程。

      基数排序的运行时间为O(n),这看上去要比快速排序的平均情况时间O(nlgn)更好一些。然而,在这两个时间中,隐含在O几号中的常数因子是不同的。对于要处理的n个关键字,尽管基数排序执行的遍数可能比快速排序要少,但每一遍所需的时间都要长得多。哪一个排序算法更好取决于底层机器的实现特性(例如,快速排序通常可以比基数排序更为有效地利用硬件缓存),同时还取决于输入的数据。此外,利用计数排序作为中间稳定排序的技术排序不是原地排序,而很多O(nlgn)时间的比较排序算法则可以做到原地排序。当内存比较宝贵时,快速排序这样的原地排序更为可取。

    1、冒泡排序

    //-------------冒泡排序begin-----------------
    template <class T>
    void babbleSort(T a[], int n)
    {
    for (int i = 0; i < n; i++)
    {
    for (int j = n - 1; j > i; j--)
    {
    if (a[j] < a[j - 1])
    {
    T tmp = a[j];
    a[j] = a[j - 1];
    a[j - 1] = tmp;
    }
    }
    }
    }
    //-------------冒泡排序end-----------------

    2、插入排序

    //-------------插入排序begin-----------------
    template <class T>
    void insertSort(T a[], int n)
    {
    for (int i = 1; i < n; i++)
    {
    T elem = a[i];
    int j = 0;
    for (j = i; j > 0; j--)
    {
    if (a[j - 1] < elem)
    {
    break;
    }
    a[j] = a[j - 1];
    }
    a[j] = elem;
    }
    }
    //-------------插入排序end-----------------

    3、选择排序

    //-------------插入排序begin-----------------
    template <class T>
    void insertSort(T a[], int n)
    {
    for (int i = 1; i < n; i++)
    {
    T elem = a[i];
    int j = 0;
    for (j = i; j > 0; j--)
    {
    if (a[j - 1] < elem)
    {
    break;
    }
    a[j] = a[j - 1];
    }
    a[j] = elem;
    }
    }
    //-------------插入排序end-----------------

    4、归并排序

    //-------------递归归并排序begin-----------------
    template<class T>
    void merge(T a[], int beg, int mid, int end)
    {
    T *tmp = new T[end - beg + 1];
    int beg1 = beg;
    int end1 = mid;
    int beg2 = mid + 1;
    int end2 = end;
    int k = 0;
    while(beg1 <= end1 && beg2 <= end2)
    {
    if (a[beg1] <= a[beg2])
    {
    tmp[k++] = a[beg1++];
    }
    else
    {
    tmp[k++] = a[beg2++];
    }
    }

    while (beg2 <= end2)
    {
    tmp[k++] = a[beg2++];
    }

    while (beg1 <= end1)
    {
    tmp[k++] = a[beg1++];
    }
    memcpy(a + beg, tmp, (end - beg + 1) * sizeof(T));
    delete []tmp;
    }
    template<class T>
    void mergeSort(T a[], int beg, int end)
    {
    int mid = int((beg + end)/2);
    if (beg < end)
    {
    mergeSort(a, beg, mid);
    mergeSort(a, mid + 1, end);
    merge(a, beg, mid, end);
    }
    }
    //-------------递归归并排序end-----------------
    //-------------非递归归并排序begin-----------------
    template <class T>
    void merge(T src[], T dst[], int beg, int mid, int end)
    {
    int beg1 = beg;
    int end1 = mid;
    int beg2 = mid + 1;
    int end2 = end;
    int k = beg;
    while(beg1 <= end1 && beg2 <= end2)//选择两个序列中较小的
    {
    if (src[beg1] <= src[beg2])
    {
    dst[k++] = src[beg1++];
    }
    else
    {
    dst[k++] = src[beg2++];
    }
    }
    while (beg2 <= end2)//复制两序列中剩余部分
    {
    dst[k++] = src[beg2++];
    }
    while (beg1 <= end1)
    {
    dst[k++] = src[beg1++];
    }
    }
    template <class T>
    void merge_pass(T src[], T dst[], int step, int len)
    {
    int i = 0;

    //按照step划分子序列进行归并,step=2时 src划分为[X1 X2][X3 X4]......
    for (i = 0; i <= len - 2 * step; i += 2 * step)
    {
    merge(src, dst, i, i + step - 1, i + 2 * step - 1);
    }

    //最后剩余不足两个子序列,但是足够一个子序列,进行归并
    if (i + step < len)
    {
    merge(src, dst, i, i + step - 1, len - 1);
    }

    //将剩余的复制到尾部
    else
    {
    for (int j = i; j < len; j++)
    {
    dst[j] = src[j];
    }
    }

    }
    template <class T>
    void mergeSortUnrecursive(T a[], int len)
    {
    int step = 1;
    T *tmp = new T[len];
    while (step < len)
    {
    //交替使用a和tmp,进行偶数次归并保证最后将结果放在a中
    merge_pass(a, tmp, step, len);
    step *= 2;
    merge_pass(tmp, a, step, len);
    step *= 2;
    }
    }
    //-------------非递归归并排序end-----------------

    5、堆排序

    //-------------堆排序begin-----------------
    template <class T>
    void maxHeapify(T a[], int i, int size)
    {
    //左子女
    int left = 2 * i + 1;
    T tmp = a[i];
    while (left <= size)
    {
    //存在右子女并且大于左子女
    if (left < size && a[left] < a[left+1])
    left++;

    //子女不大于当前值
    if (tmp >= a[left])
    break;
    else
    {
    //子女向上移动
    a[i] = a[left];
    i = left;
    left = 2 * i + 1;
    }
    }

    //最开始的结点值保存到最终位置
    a[i] = tmp;
    }
    template <class T>
    void heapSort(T a[], int size)
    {
    //建立最大堆
    for (int i = size/2 - 1; i>=0; i--)
    maxHeapify(a, i, size);

    //交换第一个和最后一个,然后调整为最大堆
    for (int i = size - 1; i > 0; i--)
    {
    T tmp = a[0];
    a[0] = a[i];
    a[i] = tmp;
    maxHeapify(a, 0, i - 1);
    }
    }
    //-------------堆排序end-----------------

    6、快速排序

    //-------------快速排序(原始)begin-----------------
    template <class T>
    int partition(T a[], int low, int high)
    {
    int pos = low;
    T posElem = a[low];
    for (int i = low + 1; i <= high; i++)
    {
    //小于基准元素的交换到左侧
    if (a[i] < posElem)
    {
    pos++;
    if (pos != i)
    {
    T tmp = a[i];
    a[i] = a[pos];
    a[pos] = tmp;
    }
    }
    }
    a[low] = a[pos];
    a[pos] = posElem;
    return pos;
    }
    template <class T>
    void quickSort(T a[], int low, int high)
    {
    if (low < high)
    {
    int pos = partition(a, low, high);
    quickSort(a, low, pos - 1);
    quickSort(a, pos + 1, high);
    }
    }
    //-------------快速排序(原始)end-----------------

      研究表明,序列长度为5到25是,采用直接插入排序比快速排序至少快10%,当排序序列小于某规模时直接使用插入排序

    //-------------快速排序(小规模插入排序)begin-----------------
    template <class T>
    void quickSort_insert(T a[], int low, int high)
    {
    //小于一个规模直接使用插入排序
    if (high - low <= 80)
    {
    insertSort(a, low, high);
    }
    else
    {
    int pos = partition(a, low, high);
    quickSort_insert(a, low, pos - 1);
    quickSort_insert(a, pos + 1, high);
    }
    }
    //-------------快速排序(小规模插入排序)end-----------------

      取基准对象是采用在序列左端low,右端high和中点位置mid=(low+high)/2中去中间值,并且交换到high位置。这样可以避免出现退化的情况。

      研究表明,将三个元素的中间元素法和小规模终止两个改进措施结合起来,可以讲递归实现的快速排序效率提高20%到25%。

    //-------------快速排序(小规模停止后插入排序+三者取中)begin-----------------
    template <class T>
    T &median3(T a[], int low, int high)
    {
    int mid = (low + high)/2;
    int k = low;

    //三者选最小为k
    if (a[mid] < a[k])
    k = mid;
    if (a[high] < a[k])
    k = high;

    //最小值交换到low
    if (k != low)
    {
    T tmp = a[k];
    a[k] = a[low];
    a[low] = tmp;
    }

    //mid为中间值,交换到high
    if (mid != high && a[mid] < a[high])
    {
    T tmp = a[mid];
    a[mid] = a[high];
    a[high] = tmp;
    }
    return a[high];
    }
    template <class T>
    int partition_median3(T a[], int low, int high)
    {
    int pos = low;

    //去三者中值
    T posElem = median3(a, low, high);
    for (int i = low; i < high; i++)
    {
    if (a[i] < posElem)
    {
    pos++;
    if (pos != i)
    {
    T tmp = a[i];
    a[i] = a[pos];
    a[pos] = tmp;
    }
    }
    }
    a[high] = a[pos];
    a[pos] = posElem;
    return pos;
    }
    template <class T>
    void quickSortHybrid(T a[], int low, int high)
    {
    //小于某规模停止
    if (high - low <=200)
    return;

    int pos = partition_median3(a, low, high);
    quickSort(a, low, pos - 1);
    quickSort(a, pos + 1, high);
    }
    template <class T>
    void quickSort_insertHybrid(T a[], int low, int high)
    {
    quickSort(a, low, high);

    //对基本有序的序列进行插入排序
    insertSort(a, low, high);
    }
    //-------------快速排序(小规模停止后插入排序+三者取中)end-----------------

      当待排序元素序列有大量重复排序码是,快速排序算法的效率将会降到非常之低,三路划分的中间部分为与基准元素等值的。

    //-------------快速排序(三路划分)begin-----------------

    template <class T>
    void quickSort3Partition(T a[], int low, int high)
    {
    if (low >= high)
    return ;
    T posElem = a[high];
    int i = low - 1, j = high, p = low - 1, q = high;
    int k = 0;
    while (true)
    {
    while (a[++i] < posElem && i < j);
    while (a[--j] > posElem && i < j);
    if (i >= j)
    break;
    swap(a[i], a[j]);
    if (a[i] == posElem)
    swap(a[i], a[++p]);
    if (a[j] == posElem)
    swap(a[j], a[--q]);
    }
    if (a[i] > a[high])
    {
    swap(a[i], a[high]);
    k = high - 1;
    }
    else
    k = high - 1;
    //j--;
    i++;
    while (k >= q)
    swap(a[k--], a[i++]);
    for (k = low; k <= p;)
    swap(a[k++], a[j--]);

    for (int m = low ; m<=high; m++)
    {
    cout << a[m] << " ";
    }cout <<"i=" << i << "j=" <<j << "p=" << p << "q=" << q << "k=" << k;
    cout <<endl;
    quickSort3Partition(a, low, j);
    quickSort3Partition(a, i, high);
    }
    //-------------快速排序(三路划分)end-----------------

      一般书上的快速排序法都是用递归实现的,递归是编译器自动用栈来实现的。当递归层次比较深时,需要占用比较大的进程栈空间,还有进程栈溢出的危险。很多时候将递归转化为非递归算法,更省时间和空间。其实原理很简单,即自己用栈来模拟递归的过程。每次从栈顶取出一部分做划分后,都把新的两部分的起始位置分别入栈。

    //-------------快速排序(非递归)begin-----------------
    template <class T>
    void quickSortUnrecursive(T a[], int size)
    {
    int s = int(4 * log((double)size)/log(2.0));
    int *stack = new int[s];
    int top = 0;
    T tmp;
    int low, high, pivot, i, j;
    //首先把整个数组入栈
    stack[top++] = size - 1;
    stack[top++] = 0;
    while(top != 0)
    {
    //取出栈顶元素,进行划分
    low = stack[--top];
    high = stack[--top];
    pivot = high;

    //保证i之前的元素的不大于轴
    i = low;

    //j从前往后滑动
    for (j = low; j < high; j++)
    {
    //如果碰到某个元素不大于轴,则与a[i]交换
    if (a[j] <= a[pivot])
    {
    tmp = a[i];
    a[i] = a[j];
    a[j] = tmp;
    i++;
    }
    }

    //i为分界点,交换a[i]和轴
    if (i != pivot)
    {
    tmp = a[i];
    a[i] = a[pivot];
    a[pivot] = tmp;

    }

    //判断小于轴的部分元素如果多于一个的话, 则入栈
    if (i - low > 1)
    {
    stack[top++] = i - 1;
    stack[top++] = low;
    }

    //判断大于轴的部分元素如果多于一个的话, 则入栈
    if (high - i > 1)
    {
    stack[top++] = high;
    stack[top++] = i + 1;
    }
    }
    delete []stack;
    }
    //-------------快速排序(非递归)end-----------------

    7、希尔排序

    //-------------希尔排序begin-----------------
    template <class T>
    void shellSort(T a[], int low, int high)
    {
    int gap = high - low + 1;
    T tmp;
    do
    {
    //求下一增量
    gap = gap / 3 + 1;

    //各子序列交替处理
    for (int i = low + gap; i <= high; i++)
    {
    //逆序,插入排序
    if (a[i] < a[i - gap])
    {
    tmp = a[i];
    int j = i - gap;

    //找到插入位置
    do
    {
    a[j + gap] = a[j];
    j = j - gap;
    } while (j > low && tmp < a[j]);

    //插入元素
    a[j + gap] = tmp;
    }
    }
    } while (gap > 1);
    }
    //-------------希尔排序end-----------------

    8、计数排序

    //-------------计数排序begin-----------------
    template <class T>
    void countSort(T a[], int size, int k)
    {
    k++;
    int *b = new int[size];
    int *c = new int[k];
    memset(c, 0, k * sizeof(int));

    //c[i]包含等于a[i]的元素个数
    for (int i = 0; i < size; i++)
    {
    c[a[i]]++;
    }

    //c[i]包含小于或等于i的元素个数
    for (int i = 1; i < k; i++)
    {
    c[i] += c[i - 1];
    }

    //c[a[i]]即为a[i]的最终位置
    for (int i = size - 1; i >= 0; i--)
    {
    b[c[a[i]] - 1] = a[i];
    c[a[i]]--;
    }
    memcpy(a, b, size * sizeof(T));
    delete []b;
    delete []c;
    }
    //-------------计数排序end-----------------

    9、基数排序

    //-------------基数排序begin-----------------
    int maxbit(int a[], int n)
    {
    int d = 1;
    int radix = 10;
    for (int i = 0; i < n; i++)
    {
    if (a[i] > radix)
    {
    radix *= 10;
    d++;
    }
    }
    return d;
    }
    void radixSort(int a[], int size)
    {
    int d = maxbit(a, size);
    int *tmp = new int[size];

    //计数器
    int *count = new int[10];
    int radix = 10;
    int k = 0;

    //进行d次排序
    for (int i = 0; i < d; i++)
    {
    //每次分配前清空计数器
    memset(count, 0, 10 * sizeof(int));
    for (int j = 0; j < size; j++)
    {
    //统计每个桶中的记录数
    k = (a[j] / radix) % 10;
    count[k]++;
    }

    //将tmp中的位置依次分配给每个桶
    for (int j = 1; j < 10; j++)
    {
    count[j] += count[j - 1];
    }

    //将所有桶中记录依次收集到tmp中
    for (int j = size - 1; j >= 0; j--)
    {
    k = (a[j] / radix) % 10;
    count[k]--;
    tmp[count[k]] = a[j];
    }

    //将临时数组的内容复制到data中
    memcpy(a, tmp, size * sizeof(int));
    radix *= 10;
    }
    delete []tmp;
    delete []count;
    }
    //-------------基数排序end-----------------














  • 相关阅读:
    全栈的苦逼和崛起
    Swift内部类调用外部类方法、属性的变通
    Assets.xcassets误删后的恢复
    UITableViewCell嵌套UITableView的正确姿势
    GPU Accelerated Computing with Python
    Windows 10创意者更新ISO发布!官方下载
    Amazing iOS Tips
    self-sizing cell的一个问题
    buf.swap32()
    buf.swap16()
  • 原文地址:https://www.cnblogs.com/yysblog/p/2269229.html
Copyright © 2020-2023  润新知