排序算法

        排序算法 就不多说了,通常我们是从最容易理解的冒泡排序入门,常用的排序算法 包括:

• 插入排序(1)

        插入排序 基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法。插入排序的基本思想是:每步将一个待排序的纪录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。

       常用的插入排序分为 直接插入排序(1.1)二分插入排序(1.2)  以及希尔排序(1.3) 

       希尔排序(Shell Sort) 是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

• 交换排序(2)

      冒泡排序(2.1)(Bubble Sort),是一种计算机科学领域的较简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端。

      快速排序

      快速排序(2.2)(Quicksort)是对冒泡排序的一种改进。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

• 选择排序(3)

       选择排序 是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法。

      选择排序分为 直接选择排序(3.1)堆排序(3.2)

• 归并排序(4)

      归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

• 基数排序(5)


       代码更容易理解,Show Me The Code!

/*******************************************************************************
* 版权所有 (C) linolzhang 2009
* 
* 文件名称:main.c
* 内容说明:排序算法实现汇总:
*     1.直接插入排序
*     2.二分插入排序
*     3.希尔排序
*     4.冒泡排序
*     5.快速排序
*     6.直接选择排序
*     7.堆排序
*     8.归并排序
*     9.基数排序
*******************************************************************************/
#include<stdio.h>
#include<string.h>

#define SWAP(a,b) { int tmp=a; a=b; b=tmp; }

/**************************************************************
1.直接插入排序 - 插入排序
    时间复杂度:O(n^2)
    空间复杂度:O(1)
    算法描述:遍历数组,将待排序元素逐个插入到适当的位置
**************************************************************/
void insertSort(int data[], int n)  
{
    int j, curr;
    for(int i=1; i<n; i++)
    {
        curr = data[i]; // 记录当前数据

	// 前向遍历,数据后移
        j = i - 1;
        while(j>=0 && curr<data[j])
        {
            data[j+1] = data[j];
            j--;
        }
        data[j+1] = curr; // 插入当前数据
    }
}

/**************************************************************
2.二分插入排序 - 插入排序
    时间复杂度:O(n^2)
    空间复杂度:O(1)
    算法描述:遍历数组,将待排序元素逐个插入到适当的位置,
             该位置的查找方式通过二分查找(区别于直接插入的顺序查找),
             只是查找比对次数降低,数据交换次数未改变
**************************************************************/
void biInsertSort(int data[], int n)  
{  
    int j, curr;
    int start, end, mid;
    for(int i=1; i<n; i++)
    {
        curr = data[i]; // 记录当前数据

        // 折半查找插入位置
        start = 0, end = i - 1;
        while(start <= end)
        {
            mid = (start + end) / 2;
            if(curr < data[mid])
                end = mid - 1;
            else
                start = mid + 1;
        }
        // 前向遍历,数据后移
        for(j=i-1; j>=end+1; j--)
            data[j+1] = data[j];
        // 插入当前数据
        data[end+1] = curr;
    }
}

/**************************************************************
3.希尔排序 - 插入排序
    时间复杂度:O(n^1.3)
    空间复杂度:O(1)
    算法描述:先对数据分组,组内采用直接插入,
             逐渐减少分组数,直到组间距降为1,排序完成
**************************************************************/
void shellSort(int data[], int n)  
{
    int k = n / 2; // 分组数
    while(k > 0)
    {
        // 组内执行多次插入排序,k=1时为直接插入排序
        int j, curr;
        for(int i=k; i<n; i++)
        {
            curr = data[i];
            j = i - k;
            while(j>=0 && curr<data[j])
            {
                data[j+k] = data[j];
                j -= k;
            }
            data[j+k] = curr;
        }
        k /= 2;
    }
}

/**************************************************************
4.冒泡排序 - 交换排序
    时间复杂度:O(n^2)
    空间复杂度:O(1)
    算法描述:遍历n次,每次得到一个最大(最小)值,
             将该数据放到已完成排序数组的尾部
             --------  >  o*------  >  oo*-----  >  ooo*----
             oooo*---  >  ooooo*--  >  oooooo*-  >  oooooooo
**************************************************************/
void bubbleSort(int data[], int n)
{
    bool bSorted; // 排序完成标记
    for(int i=n-1; i>0; i--) // 遍历n次
    {
        bSorted = true;
        for(int j=0; j<i; j++) // 每次遍历将最大值交换到最后
        {
            if(data[j] > data[j+1])
            {
                SWAP(data[j], data[j+1]);
                bSorted = false;
            }
        }
        if(bSorted)
            break;
    }
}

/**************************************************************
5.快速排序 - 交换排序
    时间复杂度:O(nlogn)
    空间复杂度:O(nlogn)
    算法描述:定义递归函数 - 开始下标i,结束下标j,
             选参照元素(第一个元素or中间元素)作为划分依据,
             比它小的元素放到它的左边,比它大的放在右边,
             分别递归处理左、右两个子数组,直到排序完成
**************************************************************/
void quickSort(int data[], int i, int j)
{
    int start = i, end = j;
    if(start >= end) // 排序完成
        return;

    int refData = data[start]; // 以第一个元素作为参考基准(该位空置)
    while(start != end)
    {
        while(start<end && data[end]>=refData) // 从右到左找第一个<refData的数据
            end--;
        if(start < end) 
            data[start++] = data[end]; // 交换到左侧空置位

        while(start<end && data[start]<refData) // 从左到右找第一个>=refData的数据
            start++;
        if(start < end)
            data[end--] = data[start]; // 交换到右侧空置位
    }
    data[start] = refData;      // 参考基准位置

    quickSort(data, i,start-1); // 递归左子数组
    quickSort(data, start+1,j);	// 递归右子数组
}

/**************************************************************
6.直接选择排序 - 选择排序
    时间复杂度:O(n^2)
    空间复杂度:O(1)
    算法描述:每一次遍历选择关最小的数据,放在排序完成数组的最后, 
             适用于大量数据中仅选择极小部分数据的情况
**************************************************************/
void selectSort(int data[], int n)  
{
    int minIndex;
    for(int i=0; i<n-1; i++)  
    {  
        minIndex = i; // 记录最小值索引
        for(int j=i+1; j<n; j++)  
        {
            if(data[j] < data[minIndex])  
                minIndex = j;
        }
        if(minIndex != i)
            SWAP(data[i], data[minIndex]);
    }
}  

/**************************************************************
7.堆排序 - 选择排序
    时间复杂度:O(nlogn)
    空间复杂度:O(1)
    算法描述:借助二叉树思想(堆),堆顶元素为数组的极值,
             也就是说,父节点的值一定小于(大于)子节点的值,
             算法分为两个主要部分:堆调整(筛选算法)和 排序
**************************************************************/
void heapAdjust(int data[], int start, int end)  
{
    // 建立大根堆,每次将最大的元素移动到末尾
    int refData = data[start];
    for(int i=2*start+1; i<=end; i*=2)
    {
        if(i<end && data[i]<data[i+1])
            i++;
        if(refData > data[i])
            break;
        data[start] = data[i];
        start = i;
    }
    data[start] = refData;
}
void heapSort(int data[], int n)  
{
    // 堆调整,建立初始堆
    for(int i=n/2; i>=0; i--)
        heapAdjust(data, i, n-1);

    for(int i=n-1; i>=0; i--)
    {
        SWAP(data[0], data[i]);   // 交换堆顶和最后一个元素
        heapAdjust(data, 0, i-1); // 堆调整 - 保持堆特征
    }
}  

/**************************************************************
8.归并排序
    时间复杂度:O(nlogn)
    空间复杂度:O(n)
    算法描述:采用分治法,先进行递归分割,直到每个子序列只剩一个元素,
             然后将已排序的序列合并成一个序列,
             归并排序要借助辅助数组实现,作为结果暂存
**************************************************************/
void merge(int data[], int start,int mid,int end, int tmp[])
{
    // 将有序数组[start,mid]与(mid,end]合并 
    int i=start, j=mid+1, k=0;
    while(i<=mid && j<=end)
    {
        if(data[i] <= data[j])
            tmp[k++] = data[i++];
        else
            tmp[k++] = data[j++];
    }
    while(i <= mid)
        tmp[k++] = data[i++];
    while(j <= end)
        tmp[k++] = data[j++];

    memcpy( &data[start],tmp, (end-start+1)*sizeof(int) );
}
void mergeSort(int data[], int start,int end, int tmp[])
{
    if(start < end)
    {
        int mid = (start + end) / 2;

        // 划分子区间,分别对左右子区间排序
        mergeSort(data, start,mid, tmp);
        mergeSort(data, mid+1,end, tmp);
		
        // 归并已经排好序的的数据[start,end]
        merge(data, start,mid,end, tmp);
    }
}

/**************************************************************
9.基数排序
    时间复杂度:O(n)
    空间复杂度:O(n)
    算法描述:将整形10进制按每位拆分,从低位到高位依次排序,
             分为两个过程:
                (1)分配,根据位值(0-9)放到0~9号桶中
                (2)收集,将放置在0~9号桶中的数据按顺序放到数组中
             重复(1)(2)过程,直到排序完成
**************************************************************/
void radixSort(int data[], int n, int tmp[])
{
    int max = data[0]; // 计算数组中最大值位数
    for(int i=1; i<n; i++)
        max = max<data[i] ? data[i] : max;

    for(int radix=1; max/radix>0; radix*=10) // 从个位开始,对数组按照“位数”进行排序
    {
        int buckets[10] = {0}; // 声明桶,对应数值0~9
        for(int i=0; i<n; i++)
            buckets[ (data[i]/radix)%10 ]++; // 每个桶中的记录数

        for(int i=1; i<10; i++) 
            buckets[i] += buckets[i-1];  // 与前面累加,得到排序位置索引

        for(int i=n-1; i>=0; i--)
            tmp[ --buckets[ (data[i]/radix)%10 ] ] = data[i];
        memcpy( data,tmp, n*sizeof(int) );
    }
}


// -------------------------------------------------------------
// 打印输出结果
void printResult(const char *str,int data[], int n)
{
    printf("%s",str);
    for(int i=0; i<n; i++)
        printf("%d ",data[i]);
    printf("\n");

    static int a[12];
    static int firstTime = 0;
    if(0 == firstTime++)
        memcpy( a,data, n*sizeof(int) );
    else
        memcpy( data,a, n*sizeof(int) );
}
int main(int argc,char *argv[])
{
    //int data[] = { 6, 7, 1, 4, 2, 6, 3, 3, 8, 6, 4, 0 };
    int data[] = { 226, 371, 311, 564, 1022, 635, 304, 31, 8, 326, 11114, 20 };
    int n=12, result[12];

    printResult("初始排序数组:",data, n);
    printf("-------------------------------------------------\n");

    insertSort(data, n);
    printResult("直接插入排序:",data, n);
    
    biInsertSort(data, n);
    printResult("二分插入排序:",data, n);
  
    shellSort(data, n);
    printResult("希尔排序:    ",data, n);

    bubbleSort(data, n);
    printResult("冒泡排序:    ",data, n);
  
    quickSort(data, 0,n-1);
    printResult("快速排序:    ",data, n);

    selectSort(data, n);
    printResult("直接选择排序:",data, n);

    heapSort(data, n);
    printResult("堆排序(大):",data, n);

    mergeSort(data, 0,n-1, result);
    printResult("归并排序:    ",data, n);

    radixSort(data, n, result);
    printResult("基数排序:    ",data, n);

    printf("\nPress Any Key To Continue...");
    getchar();
	
    return 0;  
}

        运行结果:

                

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值