内部排序法总结

简介:

1.冒泡排序(Bubble Sort)

冒泡排序方法是最简单的排序方法。这种方法的基本思想是,将待排序的元素看作是竖着排列的“气泡”,较小的元素比较轻,从而要往上浮。在冒泡排序算法中我们要对这个“气泡”序列处理若干遍。所谓一遍处理,就是自底向上检查一遍这个序列,并时刻注意两个相邻的元素的顺序是否正确。如果发现两个相邻元素的顺序不对,即“轻”的元素在下面,就交换它们的位置。显然,处理一遍之后,“最轻”的元素就浮到了最高位置;处理二遍之后,“次轻”的元素就浮到了次高位置。在作第二遍处理时,由于最高位置上的元素已是“最轻”元素,所以不必检查。一般地,第i遍处理时,不必检查第i高位置以上的元素,因为经过前面i-1遍的处理,它们已正确地排好序。
冒泡排序是稳定的。算法时间复杂度是O(n^2)。

复制代码
//冒泡排序(相邻比较法)
void BubbleSort(int a[],int n)
{
    int i,j;
    int temp;
    for(i = 0; i < n-1; i++)
        for(j = 0; j < n-i-1; j++)
            if(a[j] > a[j+1])
            {
                temp = a[j];
                a[j] = a[j+1];
                a[j+1] = temp;
            }
}
//改进的冒泡排序
void ImprovedBubbleSort(int a[], int n)
{
    int i,j;
    int temp;
    bool change = false;
    for(i = 0; i < n-1; i++)
    {
        change = false;
        for(int j = 0; j < n-i-1; j++)
        if(a[j] > a[j+1])
        {
            temp = a[j];
            a[j] = a[j+1];
            a[j+1] = temp;
            change = true;
        }
        if ( !change ) break;
    }
}
//双向冒泡排序(鸡尾酒排序)
void CocktailSort(int a[], int n)
{
    int top = n - 1;
    int bottom = 0;
    bool flag = true;
    int i, j;
    while(flag)
    {
        flag = false;
        //从小到大,升序
         for(i = bottom; i < top; i++)
        {
            if(a[i] > a[i+1])
            {
                swap(a[i], a[i+1]);
                flag = true;
            }
        }
        top--;

        //从大到小,降序
        for(j = top; j > bottom; j--)
        {
            if(a[j] < a[j-1])
            {
                swap(a[j], a[j-1]);
                flag = true;
            }
        }
        bottom++;
    }
}
复制代码

An example of bubble sort. Starting from the beginning of the list, compare every adjacent pair, swap their position if they are not in the right order (the latter one is smaller than the former one). After each iteration, one less element (the last one) is needed to be compared until there are no more elements left to be compared.

2.选择排序(Selection Sort)

选择排序的基本思想是对待排序的记录序列进行n-1遍的处理,第i遍处理是将L[i..n]中最小者与L[i]交换位置。这样,经过i遍处理之后,前i个记录的位置已经是正确的了。
选择排序是不稳定的。算法复杂度是O(n^2 )。

复制代码
//选择排序
void SelectSort(int a[],int n)
{
    int temp;
    int i,j;
    for(i = 0; i < n; i++)
    {
        int k = i;
        for(j = i+1; j < n; j++)
            if(a[j] < a[k]) k = j;
        if(k != i)
        {
           temp = a[i];
           a[i] = a[k];
           a[k] = temp;
        }
    }
}
复制代码

Selection sort animation. Red is current min. Yellow is sorted list. Blue is current item.

3.插入排序(Insertion Sort)

插入排序的基本思想是,经过i-1遍处理后,L[1..i-1]己排好序。第i遍处理仅将L[i]插入L[1..i-1]的适当位置,使得L[1..i]又是排好序的序列。要达到这个目的,我们可以用顺序比较的方法。首先比较L[i]和L[i-1],如果L[i-1]<=L[i],则L[1..i]已排好序,第i遍处理就结束了;否则交换L[i]与L[i-1]的位置,继续比较L[i-1]和L[i-2],直到找到某一个位置j(1<=j<=i-1),使得L[j] <=L[j+1]时为止。
直接插入排序是稳定的。算法时间复杂度是O(n^2)。

复制代码
//插入排序
void InsertSort(int a[], int n)
{
    int i, j;
    int temp;
    for(i = 1; i < n; i++)
    {
        temp = a[i];
        j = i-1;
        while(j >= 0 && a[j] > temp;)
        {
            a[j+1] = a[j];
            --j;
        }
        a[j+1] = temp;
    }
}

//递归的插入排序
void RecursiveInsertSort(int a[], int n)
{
    int i, j, key;
    if(n > 1)
    {
        RecursiveInsertSort(a,n-1);
    }
    key = a[n-1];
    i = n-2;
    while(i >= 0 && a[i] > key)
    {
        a[i+1] = a[i];
        i--;
    }
    a[i+1] = key;
}

//折半插入排序
void BinInsertSort(int a[], int n)
{
    int i,j;
    for(i = 1; i < n; i++)
    {
        // 在a[0..i-1]中折半查找插入位置使a[high]<=a[i]<a[high+1..i-1]
        int low = 0, high = i-1, m = 0;
        while(low <= high)
        {
            m = m + (high-low)/2;
            if (a[i] < a[m]) high = m-1;
            else low = m+1;
        }
        // 向后移动元素a[high+1..i-1],在a[high+1]处插入a[i]
        int x = a[i];
        for (j = i-1; j > high; j--)
            a[j+1] = a[j];
        a[high+1] = x;     // 完成插入
    }
}
复制代码

A graphical example of insertion sort.

4.希尔排序(Shell Sort)

先将待排序列分割成若干个子序列,分别进行直接插入排序,基本有序后再对整个序列进行直接插入排序。
希尔排序是不稳定的。时间复杂度大约为O(n^3/2)。

复制代码
//希尔排序:shell排序的核心仍然使用插入排序
void ShellSort(int a[], int n)
{
    int dk = n/2;
    int i,j;
    while(dk >= 1)
    {
        // 一趟希尔排序,对dk个序列分别进行插入排序
        for(i = dk; i < n; ++i)
        {
            int x = a[i];
            for (j = i-dk; j >= 0 && a[j] > x; j -= dk )
                a[j+dk] = a[j];
            a[j+dk] = x;
        }
        dk = dk/2;  // 缩小增量
    }
}
复制代码

5.堆排序(Heap Sort)

堆排序是一种树形选择排序,在排序过程中,将A[n]看成是完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小的元素。对N个元素从小到大排序建立大根堆,然后交换堆顶与最后一个元素,将剩下的N-1个元素调整为大根堆,执行N-1此这样的操作。
堆排序是不稳定的。算法时间复杂度O(nlogn)。

复制代码
//调整大根堆
void HeapAdjust(int data[],int nStart, int nLen)
{
    int nMaxChild = 0;
    int Temp;

    while((2*nStart+1) < nLen)
    {
        nMaxChild = 2*nStart+1;
        if((2*nStart+2) < nLen)
        {
            //比较左子树和右子树,记录最大值的Index
            if (data[2*nStart+1] < data[2*nStart+2])
            {
                nMaxChild = 2*nStart+2;
            }
        }
        //change data
        if(data[nStart] < data[nMaxChild])
        {
            //交换nStart与nMaxChild的数据
              Temp = data[nStart];
            data[nStart] = data[nMaxChild];
            data[nMaxChild] = Temp;

            //堆被破坏,需要重新调整
              nStart = nMaxChild;
        }
        else
        {
            //比较左右孩子均小则堆未破坏,不再需要调整
              break;
        }
    }
}

//堆排序 从小到大排序建立大顶堆
void HeapSort(int data[],int nLen)
{
    int i;
    int nTemp;
    //建立堆
    for(i = nLen/2-1; i >= 0; i--)
    {
        HeapAdjust(data, i, nLen);
    }
    for(i = nLen-1; i > 0; i--)
    {
        //交换堆顶元素和最后一个元素
         nTemp = data[0];
        data[0] = data[i];
        data[i] = nTemp;

        //将data[0...i]重写建成大根堆
         HeapAdjust(data, 0, i);
    }
}
复制代码

6.快速排序(Quick Sort)

快速排序是对冒泡排序的一种本质改进。它的基本思想是通过一趟扫描后,使得排序序列的长度能大幅度地减少。在冒泡排序中,一次扫描只能确保最大数值的数移到正确位置,而待排序序列的长度可能只减少1。快速排序通过一趟扫描,就能确保某个数(以它为基准点吧)的左边各数都比它小,右边各数都比它大。然后又用同样的方法处理它左右两边的数,直到基准点的左右只有一个元素为止。

快速排序是不稳定的。最理想情况算法时间复杂度O(nlog2n),最坏O(n^2)。

复制代码
//快速排序
void QuickSort(int a[], int low, int high)
{
    if(low < high)
    {
        // 划分
        int pivot = a[low];
        int i = low; int j = high;
        while(i < j)
        {
            while(i<j && a[j] >= pivot)  j--;
            a[i] = a[j];
            while(i<j && a[i] <= pivot)  i++;
            a[j] = a[i];
        }
        a[i] = pivot;
        // 对子序列快排
        QuickSort(a, low, i-1);
        QuickSort(a, i+1, high);
    }
}

//快速排序法的划分操作
int Partition(int vec[],int low,int high)
{
     //任选元素作为轴,这里选首元素
     int pivot = vec[low];
     while(low < high)
     {
         while(low < high && vec[high] >= pivot)
             high--;
         vec[low] = vec[high];
         while(low < high && vec[low] <= pivot)
             low++;
         vec[high] = vec[low];
     }
     //此时low==high
     vec[low] = pivot;
     return low;
}

//in-place partition algorithm
//http://en.wikipedia.org/wiki/Quicksort
int in_place_partition(int* array, int left, int right)
{
    int pivot_index = (right-left)/2;
    int pivot = array[pivot_index];
    swap(array[pivot_index], array[right]);
    int index = left;
    for(int i = left; i < right; i++)
    {
        if (array[i] < pivot)    //升序
         {
            swap(array[index], array[i]);
            ++index;
        }
    }
    swap(array[right], array[index]);
    return index;
}

void Qsort(int* array, int left, int right)
{
    if (left >= right)
        return;
    int index = Partition(array, left, right);
    Qsort(array, left, index - 1);
    Qsort(array, index + 1, right);
}
复制代码

非递归的快速排序:用栈保存每个待排序子串的首尾元素下标,下一次循环时取出这个范围,对这段子序列进行partition操作,直到所有的子串都排好序,栈也为空了。

复制代码
//非递归快速排序
void QuickSort2(int vec[],int low,int high)
{
    stack<int> st;
    if(low >= high) return;

    int mid = Partition(vec,low,high);
    if(low < mid-1)
    {
        st.push(low);
        st.push(mid-1);
    }
    if(mid+1 < high)
    {
        st.push(mid+1);
        st.push(high);
    }
    //用栈保存每个待排序子串的首尾元素下标,下一次循环时取出这个范围,对这段子序列进行partition操作
    while(!st.empty())
    {
        int q = st.top();
        st.pop();
        int p=st.top();
        st.pop();
        mid = Partition(vec,p,q);
        if(p < mid-1)
        {
            st.push(p);
            st.push(mid-1);
        }
        if(mid+1 < q)
        {
            st.push(mid+1);
            st.push(q);
        }
    }
}
复制代码

7.归并排序(Merge Sort)

基本思想是合并两个有序表,设有两个有序(升序)序列存储在同一数组中相邻的位置上,不妨设为A[l..m],A[m+1..h],将它们归并为一个有序数列,并存储在A[l..h]。
归并排序的时间复杂度无论是在最好情况下还是在最坏情况下均是O(nlog2n)。

归并排序是稳定的排序算法。

复制代码
//将有序序列a[low..mid]和a[mid+1..high]归并到a[low..high]。
void Merge(int a[], int low, int mid, int high)
{
    // 归并到b[]
    int i = low;
    int j = mid+1;
    int k = 0;
    int *b = new int[high-low+1];
    while(i <= mid && j <= high)
    {
        if (a[i] <= a[j]) { b[k++] = a[i++]; }
        else  { b[k++] = a[j++]; }
    }
    // 归并剩余元素
    while(i <= mid)  b[k++] = a[i++];
    while(j <= high)  b[k++] = a[j++];
    // 从b[]复制回a[]
    for(i = 0; i <= high-low; ++i)
        a[low+i] = b[i];
    delete []b;
}

//归并排序
//http://en.wikipedia.org/wiki/Mergesort
void MergeSort(int a[], int low, int high)
{
    if(low >= high)  return;
    else
    {
        int mid = (low+high)/2;
        MergeSort(a,low,mid);
        MergeSort(a,mid+1,high);
        Merge(a,low,mid,high);
    }
}

//自底向上的归并排序
void MergeSort2(int a[], int n)
{
    int s,i,t = 1;
    while(t < n)
    {
        s = t;  t = s*2;
        for(i=0; i+t<=n; i+=t)
            Merge(a,i,i+s-1,i+t-1);
        if(i+s < n)
            Merge(a,i,i+s-1,n-1);
    }
}
复制代码

归并排序在最坏的情况下都是O(NlogN)的时间复杂度,缺点是Merge的时候要有O(N)的额外的空间,如何改进?

使用原地归并排序 In-place Merge Sort: http://www.ahathinking.com/archives/103.html

复制代码
//reverse array
void reverse(int arr[], int size)
{
    int left = 0;
    int right = size -1;
    while(left < right)
    {
        int temp = arr[left];
        arr[left++] = arr[right];
        arr[right--] = temp;
    }
}

// swap [arr,arr+headSize) and [arr+headSize,arr+headSize+endSize)
void SwapMemory(int arr[], int headSize, int endSize)
{
    reverse(arr, headSize);
    reverse(arr + headSize, endSize);
    reverse(arr, headSize + endSize);
}

//原地归并
void Inplace_Merge(int arr[], int beg, int mid, int end)
{
    int i = beg;     // 指示有序串1
    int j = mid + 1; // 指示有序串2
    while(i < j && j <= end)
    {
        while(i < j && arr[i] <= arr[j])
        {
            ++i;
        }
        int index = j;
        while(j <= end && arr[j] <= arr[i])
        {
            ++j;
        }
        SwapMemory(&arr[i], index-i, j-index);//swap [i,index) and [index,j)
        i += (j-index);
    }
}

//原地归并排序
void Inplace_MergeSort(int arr[], int beg, int end)
{
    if(beg < end)
    {
        int mid = (beg + end) / 2;
        Inplace_MergeSort(arr, beg, mid);
        Inplace_MergeSort(arr, mid+1, end);
        Inplace_Merge(arr, beg, mid, end);
    }
}
复制代码

 

An example of merge sort. First divide the list into the smallest unit (1 element), then compare each element with the adjacent list to sort and merge the two adjacent lists. Finally all the elements are sorted and merged.

 

参考:

http://en.wikipedia.org/wiki/Sorting_algorith


    本文转自阿凡卢博客园博客,原文链接:http://www.cnblogs.com/luxiaoxun/archive/2012/09/01/2666677.html,如需转载请自行联系原作者


相关文章
|
4天前
|
搜索推荐 编译器 Linux
一个可用于企业开发及通用跨平台的Makefile文件
一款适用于企业级开发的通用跨平台Makefile,支持C/C++混合编译、多目标输出(可执行文件、静态/动态库)、Release/Debug版本管理。配置简洁,仅需修改带`MF_CONFIGURE_`前缀的变量,支持脚本化配置与子Makefile管理,具备完善日志、错误提示和跨平台兼容性,附详细文档与示例,便于学习与集成。
293 116
|
19天前
|
域名解析 人工智能
【实操攻略】手把手教学,免费领取.CN域名
即日起至2025年12月31日,购买万小智AI建站或云·企业官网,每单可免费领1个.CN域名首年!跟我了解领取攻略吧~
|
7天前
|
数据采集 人工智能 自然语言处理
Meta SAM3开源:让图像分割,听懂你的话
Meta发布并开源SAM 3,首个支持文本或视觉提示的统一图像视频分割模型,可精准分割“红色条纹伞”等开放词汇概念,覆盖400万独特概念,性能达人类水平75%–80%,推动视觉分割新突破。
451 44
Meta SAM3开源:让图像分割,听懂你的话
|
13天前
|
安全 Java Android开发
深度解析 Android 崩溃捕获原理及从崩溃到归因的闭环实践
崩溃堆栈全是 a.b.c?Native 错误查不到行号?本文详解 Android 崩溃采集全链路原理,教你如何把“天书”变“说明书”。RUM SDK 已支持一键接入。
684 222
|
1天前
|
Windows
dll错误修复 ,可指定下载dll,regsvr32等
dll错误修复 ,可指定下载dll,regsvr32等
133 95
|
11天前
|
人工智能 移动开发 自然语言处理
2025最新HTML静态网页制作工具推荐:10款免费在线生成器小白也能5分钟上手
晓猛团队精选2025年10款真正免费、无需编程的在线HTML建站工具,涵盖AI生成、拖拽编辑、设计稿转代码等多种类型,均支持浏览器直接使用、快速出图与文件导出,特别适合零基础用户快速搭建个人网站、落地页或企业官网。
1677 158
|
存储 人工智能 监控
从代码生成到自主决策:打造一个Coding驱动的“自我编程”Agent
本文介绍了一种基于LLM的“自我编程”Agent系统,通过代码驱动实现复杂逻辑。该Agent以Python为执行引擎,结合Py4j实现Java与Python交互,支持多工具调用、记忆分层与上下文工程,具备感知、认知、表达、自我评估等能力模块,目标是打造可进化的“1.5线”智能助手。
925 61