• 8. 排序总结


    一. 时空复杂度

    转自:http://blog.chinaunix.net/uid-21457204-id-3060260.html

    二、实现代码

    http://blog.csdn.net/touch_2011/article/details/6767673  漫谈经典排序算法  对《数据结构》上的算法总结得很好,主要对着实现了一遍。

    与书一致,为了叙述方便,以下源代码中均不考虑A[0],默认下标从1开始。

    //-------------插播一段交换int a, b两个数据值的方法------------------------------------

    方法一:使用异或^操作符。

    void swap(int &a, int &b)

    {

        a = a^b;

        b = a^b;

        a = a^b;

    }

    方法二:使用加法

       a = a+b; // 可能溢出,方法一则不会,且位操作要比加减法 操作要快些

       b = a - b;

       a = a -b;

    //-------------end--------------------------------------

      (1) 插入排序、希尔 排序  --- 基于插入思想

    // 直接插入排序
    // 最好 O(n), 最差 O(n^2), 平均 O(n^2), 稳定
    void Sort::insertSort(int* a, int n)
    {
        if(a == NULL || n < 1) return;
        for(int i = 2; i <= n; i++)
        {
            a[0] = a[i]; // 哨兵
            int j;
            for(j = i - 1; j > 0; j--)
            {
                if(a[0] < a[j]) // move back
                {
                    a[j+1] = a[j];
                }
                else
                    break;
            }
            a[j+1] = a[0];
        }
    }
    
    int Sort::binarySearch(int *a, int start, int end, int value)
    {
        if(start > end) // 没找到
            return start;
        int mid = (start + end) / 2;
        if(a[mid] == value)
            return mid;
        else if( a[mid] < value)
            return binarySearch(a, mid + 1, end, value);
        else
            return binarySearch(a, start, mid -1, value);
    }
    // 二分查找插入排序
    // 最好 O(nlogn), 最差 O(n^2), 平均 O(n^2), 非稳定
    // 只在数据量较大,且 比较无序的 情况下 优于 直接插入排序
    void Sort::binaryInsertSort(int *a, int n)
    {
        if(a == NULL || n < 1) return;
        int pos;
        for(int i = 2; i <= n; i++)
        {
            a[0] = a[i];
            pos = binarySearch(a, 1, i - 1, a[0]);
            for(int j = i - 1; j >= pos; j--)
                a[j+1] = a[j];
            a[pos] = a[0];
        }
    }
    
    // 表插入排序
    // 最好 O(n), 最差 O(n^2), 平均 O(n^2), 稳定
    // 只是用改变存储结构,用改变连接关系代替实际移动,比较次数
    // 没有变化; 且无法随机访问,比较无用。(用双向链表, 循环数组会好一点)
    void Sort::tableInsertSort(PNode list, int n)
    {
        int p, ahead; // head 插入位置的前一个结点
        int i;
        // 初始化
        list[0].next = 1;
        list[1].next = 0;
    
        // 逐个插入
        for(i = 2; i <= n; i++)
        {
            ahead = 0;
            p = list[0].next; // 从表头遍历
            // 遍历链表,寻找插入位置
            while(p != 0 && list[p].key <= list[i].key)
            {
                ahead = p;
                p = list[p].next;
            }
            list[ahead].next = i;
            list[i].next = p;
        }
    
        arrange(list, n);
    }
    // 将表中元素按增序重排到a[1,n]
    void Sort::arrange(PNode list, int n)
    {
        int p = list[0].next;
        int q;
        Node tmp;
        for(int i = 1; i < n; i++)
        {
            while(p < i) p = list[p].next; // [1, i-1] 有序,得到原来在[1, i-1]间元素的新位置
            q = list[p].next;
            if(p != i) // 交换到当前位置i
            {
                tmp.key = list[p].key;
                tmp.next = list[p].next;
                list[p] = list[i];
                list[i] = tmp;
                list[i].next = p; // 指回到原来的位置p
            }
            p = q; // 下一个 元素i+1的位置
        }
        cout << "tableInsertSort" << endl;
       for(int i = 1; i <= n; i++)
       {
            cout << list[i].key << " ";
        }
        cout << endl;
    }
    // 希尔排序
    // 与增量递减序列的取值有关, 基本 O(n^2),  不稳定
    void Sort::shellSort(int* a, int n)
    {
        int shellTable[] = {5, 3, 1};// 增量递减序列(序列中的值互质),末尾元素为1
        for(int i = 0; i < sizeof(shellTable) / sizeof(int); i++)
        {
            shellInsertSort(a, n, shellTable[i]);
        }
    }
    // 希尔排序中所采用的插入排序,元素间隔为dk
    void Sort::shellInsertSort(int* a, int n, int dk)
    {
        if(dk >n)
            throw std::exception("invalid input");
        for(int t = 1; t <= dk; t++)
        {
            for(int i = dk + t; i <= n; i+= dk)
            {
                a[0] = a[i]; // 哨兵暂存
                int j;
                for(j = i - dk; j > 0; j -= dk)
                {
                    if(a[0] < a[j])
                        a[j + dk] = a[j];
                    else
                        break;
                }
                a[j+dk] = a[0];
            }
        }
    }

     (2)  简单选择排序,堆排序--- 基于选择思想

    // 简单选择排序
    // 最好、最差、平均 都是 O(n^2) 稳定
    void Sort::selectSort(int* a, int n)
    {
        int index;
        for(int i = 1; i < n; i++) // 进行n-1次,每次选出最小的
        {
            index = i;
            int j;
            for(j = i + 1;  j <= n; j++)
            {
                if(a[index] > a[j])
                {
                    index = j; // 记录最小的下标为index
                }
            }
            if(index != i) // 交换
            {
                swap(a[index], a[i]);
            }
        }
    }
    
    // 递归选择排序
    // 最好、最差、平均 都是 O(n^2) 稳定
    void Sort::recursionSelectSort(int* a, int n)
    {
        if(n == 1) // 退出
            return ; 
        // 1. 选择最小的元素
        int index = 1;
        for(int i = 1; i <= n; i++)
        {
            if(a[index] > a[i])
                index = i;
        }
        // 2. 交换 1 和index
        if(index != 1)
        {
            swap(a[index], a[1]);
        }
        // 3. 子序列[a+i, a+n]递归
        recursionSelectSort(a + 1, n - 1);
    }
    
    // 堆调整  a[i, n]为除 i 外,其他符合堆性质的序列,本质上是使用堆结构,逐次从中选择最大的元素,是选择排序
    // 最好、最差、平均 都是 O(nlog(n))  不稳定
    void Sort::heapAdjust(int* a, int i, int n) // 调整堆,保持大顶堆的性质,参数i指向根结点 
    {
        int tmp = a[i];
        int left, right, largest; // 记录 根左孩子右孩子 这三者中的最大者index
        largest = left = i * 2;
        if(left > n) return;
        right = left + 1;
        if(right <= n && a[right] > a[left]) largest = right;
        if(a[largest] > a[i]) // 根结点小于子结点的最大值,需交换根与最大子结点
        {
         swap(a[largest], a[i]);
    heapAdjust(a, largest, n); // 调整子堆 } } // 创建堆, 即对[n/2, n]进行调整,得到大顶堆 void Sort::createHeap(int* a, int n) { // n/2表示,由n个元素构建的完全二叉树,最后一个非叶子结点的下标为n/2(取下整) // 若取大于 n/2的结点,都是叶子结点不用adjust for(int i = n/2; i >= 1; i--) { heapAdjust(a, i, n); } } // 堆排序 void Sort::heapSort(int* a, int n) { createHeap(a, n); // 构建大顶堆 // 排序,每次选最大的数 a[1](大顶堆的根结点)交换放到序列最后一个位置上 for(int i = n; i >= 2; i--) // 元素数量 { // 堆顶元素 和 最后一个位置 交换 swap(a[1], a[i]); heapAdjust(a, 1, i - 1); } }

     (3) 冒泡排序, 快速排序--- 基于交换思想

    // 冒泡排序
    // 最好、最差、平均 都是 O(n^2) 稳定
    void Sort::bubbleSort(int* a, int n)
    {
        for(int i = 1; i < n; i++)
            for(int j = 1; j < n - i + 1; j++)
            {
                if(a[j + 1] < a[j]) // swap
                {
       swap(a[j+1], a[j]);
    }
    // 快速排序 不稳定
    // 最好(递归树,每次划分都对称)的复杂O(nlog(n)), 最坏时(每次划分都不对称,如顺序和逆序时,退化为冒泡)O(n^2), 平均O(nlogn)
    void Sort::quickSort(int* a, int low, int high)
    {
        if(low < high)
        {
            int position = partition(a, low, high);
            quickSort(a, low , position - 1);
            quickSort(a, position + 1, high);
        }
    }
    
    int Sort::partition(int* a, int low, int high)
    {
        int pivotkey = a[low]; // 枢轴
        // 注意:low < high 的条件很重要
        while(low < high)
        {
            while(low < high && a[high] >= pivotkey) high--;
            a[low] = a[high]; // low=high 或 a[high] < pivotkey 时,将小于枢轴值放于低位
            while(low < high && a[low] <= pivotkey) low++;
            a[high] = a[low]; // 放于高位
        }
        // 结束时,有 low = high 为枢轴的下标
        a[low] = pivotkey; // 赋值 枢轴
        return low; // 返回枢轴下标
    }
    
    // 非递归版
    void Sort::quickSortStack(int* a, int low, int high) 
    {
        if(low < high)
        {
            std::stack<int> callStackLow;
    std::stack<int> callStackHigh;
         callStackLow.push(low);
    callStackHigh.push(high);
    while(!callStackLow.empty()) { int curLow = callStackLow.top(); int curHigh = callStackHigh.top(); callStack.pop(); // 求 partition if(curLow < curHigh) { int position = partition(a, curLow, curHigh); callStackLow.push(curLow);
    callStackHigh.push(position
    - 1);
    callStackLow.push(position+1); // 二者无顺序性 callStackHigh.push(j); } } } }

     (4) 归并排序

    //----------------------基于分制思想----------------------------------------
    // 归并排序 递归,先自顶向下分解,再由递归退栈,自底向上合并
    // 最好、最坏、平均 复杂度 都为 O(nlog(n)) (递归树)
    void Sort::mergeSort(int* a, int start, int end)
    {
        if(start < end)
        {
            int mid = (start + end) / 2 ; // 分成两部分
            // 先分解
            mergeSort(a, start, mid);
            mergeSort(a, mid + 1, end);
            // 再合并
            merge(a, start, mid, end);
        }
    }
    
    //将有序的src[start, mid] 和 src[mid+1,..n] 归并为有序的 src[start, end]
    void Sort::merge(int* a, int start, int mid, int end)
    {
        int i, j, k;
        // 申请辅助数组
        int length1 = mid - start + 1;
        int length2 = end - mid;
        int* array1 = new int[length1]; //
        int* array2 = new int[length2]; //
        // 把内容copy到辅助数组中
        std::memcpy(array1, a + start, sizeof(int) * length1);
        std::memcpy(array2, a + mid + 1, sizeof(int) * length2);
        // 归并
        for(i = 0, j = 0, k = start; i < length1 && j < length2;)
        {
            if(array1[i] <= array2[j]) 
                a[k++] = array1[i++];
            else
                a[k++] = array2[j++];
        }
        if(i < length1)
            std::memcpy(a + k, array1 + i, sizeof(int) * (length1 - i)); // 复制剩余的src[start, mid] 到 tr
        if(j < length2)
            std::memcpy(a + k, array2 + j, sizeof(int) * (length2 - j)); // 复制剩余的src[mid+1, end] 到 tr
        delete[] array1;
        delete[] array2;
    }
    // 归并排序 非递归 ---直接自底向上合并
    void Sort::mergeSortNoRecusion(int* a ,int n)
    {
        int step = 1, i; // 要合并的组长度为step
       
        while(step < n)
        {
            i = 1;
            while(i + 2 * step - 1 <= n) // 查看是否可以合并 a[i,i+step-1] , a[i+step, i+2*step-1]
            {
                merge(a, i, i + step - 1, i + 2 * step -1);
                i = i + 2 * step; // 下一组
            }
            // 处理尾部剩余部分
            if(i < n)
            {
                merge(a, i, i + step/2 -1, n); //有序数列 a[i, i+step/2-1], a[i+step/2, n] 合并
            }
            step *= 2; // 2倍增长
        }
    }

    (5) 计数排序、基数排序、桶排序

    /------------------------线性时间排序--------------------------------------
    // 计数排序,要求a中的元素都为非负整数
    // 最快、最差、平均 时间复杂度 o(n+k), n为元素个数,k为a中的最大值。需辅助空间 n+k 稳定
    void Sort::countingSort(int* a, int n)
    {
        if(a == NULL || n < 1) return;
       // 找到最大的元素
        int maxValue = a[1];
        for(int i = 2; i <= n; i++)
        {
            if(maxValue < a[i])
                maxValue = a[i];
        }
        // 初始化计数数组 count 
        int* count = new int[maxValue + 1];
        memset(count, 0, (maxValue + 1) * sizeof(int)); 
        // 计算等于 a[i] 的记录个数
        for(int i = 1; i <= n; i++)
        {
            count[a[i]]++;
        }
        // 计算 小于等于 a[i] 的记录个数,确定其顺序
        for(int i = 1; i <= maxValue; i++)
        {
            count[i] += count[i-1];
        }
        // 从后往前扫描a数组(为了保持稳定性),把各个元素放在临时数组对应的位置上
        int* tmp = new int[n+1];
        for(int i = n; i > 0; i--)
        {
            tmp[count[a[i]]] = a[i];
            count[a[i]]--;
        }
        // 把有序数组的元素拷贝到a中
        memcpy(a, tmp, (n+1) * sizeof(int));
        delete[] count;
        delete[] tmp;
    }
    
    // 简单整数基数排序, 每一位都用一次计数排序
    // 最坏、最好、平均都是 O(n), 稳定排序, 辅助空间k+n
    void Sort::radixSort(int* a, int n)
    {
        int i;
        int maxValue = a[1]; // 统计最大数
        for(i = 2; i <= n; i++)
        {
            if(maxValue < a[i])
                maxValue = a[i]; // 最大值
        }
        int d = 0; //统计最大数的位数
        if(maxValue > 0)
            d = 1;
        while(maxValue / 10 != 0)
        {
            d++;
            maxValue/=10;
        }
        for(int i = 1; i <= d; i++)
            countingSort(a, n, 9, i); // 从最低位开始排序
    }
     // 计数排序, n为数组a的记录个数,radix为记录中最大值,按第d为排序
    void Sort::countingSort(int* a, int n, int radix ,int d) // radix表示基数的取值最大值,十进制默认为9. [0,9]
    {
       int* count = new int[radix + 1];
       int* b = new int[n + 1];
       memset(count, 0, (radix + 1) * sizeof(int));
       // a[i] 在 d 位 的值为 a[i] / (int)(pow(10, d-1) % 10, 在count中记录其出现的个数
       for(int i = 1; i <= n; i++)
       {
           int v = a[i]/(int)(std::pow(10.0, d-1)) % 10;
           count[v]++;
       }
       // 统计 小于等于 该值的个数
       for(int i = 1; i <= radix; i++)
       {
           count[i] += count[i-1];
       }
       // 从后往前 扫描数组,把各原始放在有序序列中相应的位置上
       for(int i = n; i > 0; i--)
       {
           int v = a[i]/(int)std::pow(10.0, d-1) % 10;
           b[count[v]] = a[i];
           count[v]--;
       }
       // 排序结果 拷回 a中
       memcpy(a, b, (n+1) * sizeof(int));
       delete[] count;
       delete[] b;
    }
    
    // 桶排序,感觉和基数排序挺相似,不过基数排序的radix为每位的取值范围,有多位,
    // 而桶则是直接根据取值范围进行等间隔划分
    // 复杂度为O(n + k), k为桶的个数 最好情况(数均匀分布于各桶中) O(n), 最坏情况O(n^2), 
    void Sort::bucketSort(int* a, int n, int k)
    {
        // 建立链表 (也可以用 k * n 的二维数组,但比较耗费空间)
        // 链表结点描述
        typedef struct Node
        {
            double key;
            struct Node* next;
        };
        // 辅助数组元素描述
        typedef struct Head
        {
            Node* next;
        };
        int i, j;
        Node* p;
        Node* q;
        Node* node;
        // 求出最大、最小值,对桶范围进行划分
        int maxValue = a[1];
        int minValue = a[1];
        for(int i = 2; i <= n; i++)
        {
            if(maxValue < a[i]) maxValue = a[i];
            if(minValue > a[i]) minValue = a[i];
        }
        Head* heads = new Head[k];
        for(int i = 0; i < k; i++)
            heads[i].next = NULL;
       
        for(int i = 1; i <= n; i++)
        {
            node = new Node();
            node->key = a[i];
            node->next = NULL;
            // 判断a[i]该进入哪个桶
            int index;
            if(a[i] == maxValue)
                index = k - 1;
            else
                index = (double)(a[i] - minValue) / (maxValue - minValue) * k;
            p = q = heads[index].next;
            if(p == NULL) // 该桶中尚未有元素
            {
                heads[index].next = node;
                continue;
            }
            else
            {
                // 对已有元素进行排序(也可以全部分配完在排序)
                // 这里直接使用进行插入排序
                while(p)
                {
                    if(a[i] < p->key)
                        break;
                    q = p; // 记录前一个结点
                    p = p->next;
                }
                if(p == NULL)
                    q->next = node; // 插到尾部
                else
                {
                    node->next = p;
                    q->next = node;
                }
            }
            
        }
        int t = 1;
        // 将排序后的结果拷到a中
        for(int i = 0; i < k; i++)
        {
            p = heads[i].next;
            while(p)
            {
                a[t++] = p->key;
                q = p;
                p = p->next;
                delete q; // 删除链表
            } 
        }
        delete[] heads; 
    
    }

     鸽巢原理及应用

    http://blog.csdn.net/xuhx/article/details/5928079 很有意思的一道题。

  • 相关阅读:
    ALV_TREE(二:cl_gui_simple_tree…
    ALV_TREE(一:cl_gui_alv_tree_si…
    SQLPlus命令详细说明
    PL/SQL中,declare定义变量和variable定义变量的区别?
    Oracle 多表视图更新(待看完触发器后再来看)
    Oracle 函数 Translate 的用法
    Merge into
    savepoint(回退点)
    Oracle之分页查询
    对于package中全局变量的一点点初级理解
  • 原文地址:https://www.cnblogs.com/wenshanzh/p/3298886.html
Copyright © 2020-2023  润新知