关于九种排序算法解析

注意: 快速排序是排序大数组的最常用算法

下表是各个排序算法的比较说明
在这里插入图片描述

插入排序:

是一个对少量元素进行排序的有效算法。实现比较简单。时间复杂度:O(n^2),空间复杂度:O(1)。是稳定的排序方法。

void InsertionSort(int *a,int n)
{
    int temp;
    for(int i = 1;i < n;++i)
    {
        temp = *(a + i);
        int j = i - 1;
        while(j >= 0 && *(a + j) > temp)
        {
            *(a + j + 1) = *(a + j);
            --j;
        }
        *(a + j + 1) = temp;
    }
}

合并排序
采用分治法。将n个元素分成各含n/2个元素的子序列,用合并排序法对两个子序列递归的排序(子序列长度为1时递归结束),最后合并两个已排序的子序列得到结果。时间复杂度:O(nlogn),空间复杂度:O(n)。是稳定的排序方法。

//合并排序

#include <iostream>
using namespace std;

#define MAX_VALUE 100000//用于设置哨兵,避免检查是否每一个堆都是空的

//合并两个子数组的函数
void Merge(int *a,int p,int q,int r)
{
    int num1,num2;
    num1 = q - p + 1;
    num2 = r - q;
    int *a1 = (int*)malloc((num1 + 1) * sizeof(int));
    int *a2 = (int*)malloc((num2 + 1) * sizeof(int));
    for(int i = 0;i < num1;++i)
        *(a1 + i) = *(a + p + i);
    *(a1 + num1) = MAX_VALUE;//设置哨兵元素
    for(int i = 0;i < num2;++i)
        *(a2 + i) = *(a + q + 1 + i);
    *(a2 + num2) = MAX_VALUE;//设置哨兵元素
    
    //进行排序
    int index1 = 0;
    int index2 = 0;
    for(int i = p;i <= r;++i)
    {
        if(*(a1 + index1) < *(a2 + index2))
        {
            *(a + i) = *(a1 + index1);
            ++index1;
        }
        else
        {
            *(a + i) = *(a2 + index2);
            ++index2;
        }
    }
    free(a1);
free(a2);
}

冒泡排序
每一趟都比较相邻两个元素,若是逆序的,则交换。结束的条件应该是“在一趟排序过程中没有进行过交换元素的操作”。时间复杂度:O(n^2),空间复杂度O(1)。是稳定的排序。

void BubbleSort(int *a,int n)
{
    int flag,temp;//标记是否进行过交换操作
    for(int i = 0;i < n - 1;++i)
    {
        flag = 0;
        for(int j = 0;j < n - 1 - i;++j)
        {
            if(*(a + j) > *(a + j + 1))
            {
                temp = *(a + j);
                 *(a + j) = *(a + j + 1);
                 *(a + j + 1) = temp;
                 flag = 1;
            }
        }
        if(flag == 0)break;
    }
}

快速排序
它是对冒泡排序的一种改进。它的基本思想是:通过一趟排序将待排序元素分成两个部分,其中一部分元素比另一部分元素小。再分别对这两部分元素进行排序。以达到整个元素序列有序。时间复杂度:O(nlogn),空间复杂度O(logn),是不稳定的算法。

int Partition(int *a,int low,int high)
{
    int pivotKey = *(a + high);
    int i = low - 1;
    for(int j = low;j <= high - 1;++j)
    {
        if (*(a + j) < pivotKey)
        {
            ++i;
            int tmp = *(a + i);
            *(a + i) = *(a + j);
            *(a + j) = tmp;
        }
    }
  
    int tmp = *(a + i + 1);
    *(a + i + 1) = *(a + high);
    *(a + high) = tmp;
  
    return (i + 1);
}
  
void QuickSort(int *a,int low,int high)
{
    if(low < high)
    {
        int PivotLoc = Partition(a,low,high);
        QuickSort(a,low,PivotLoc - 1);
        QuickSort(a,PivotLoc + 1,high);
    }
}

堆排序:
一种原地排序算法,即在任何时刻数组中只有常数个元素存储在输入数组以外

//注意:下表都以1开始,而不是0
//得到父节点索引
int getParent(int i)
{
    return i>>1;
}
  
//得到左子树索引
int getLeftSon(int i)
{
    return i<<1;
}
  
//得到右子树索引
int getRightSon(int i)
{
    return ((i<<1) + 1);
}
  
//调整以某个节点i为根节点的子树为大根堆
void MaxHeapify(int A[],int i,int HeapSize)
{
    int left = getLeftSon(i);
    int right = getRightSon(i);
    int largest = i;//记录值最大的元素的索引
  
    if (left <= HeapSize && A[left] > A[i])
    {
        largest = left;
    }
  
    if (right <= HeapSize && A[right] > A[largest])
    {
        largest = right;
    }
  
    if (largest != i)//此子树不满足大根堆的性质,需要进行调整
    {
        //进行交换
        int temp = A[i];
        A[i] = A[largest];
        A[largest] = temp;
          
        MaxHeapify(A,largest,HeapSize);//递归调用,继续调整子树
    }
}
  
//输出数组元素
void printHeap(int A[],int HeapSize)
{
    for(int i = 1;i <= HeapSize;++i)
    {
        cout << A[i] << " ";
    }
    cout << endl;
}
  
//建堆
void buildMaxHeap(int A[],int HeapSize)
{
    for (int i = (int)floor((float)HeapSize / 2);i > 0;--i)
    {
        MaxHeapify(A,i,HeapSize);
    }
  
    cout << "建成的大根堆:" << endl;
    printHeap(A,HeapSize);
}
  
//堆排序
void heapSort(int A[],int HeapSize)
{
    buildMaxHeap(A,HeapSize);
    for (int i = HeapSize;i > 0;--i)
    {
        int temp = A[1];
        A[1] = A[i];
        A[i] = temp;
        MaxHeapify(A,1,i - 1);
    }
}

计数排序
思想是对每一个输入元素x,确定出小于x的元素的个数。然后我们就可以直接把它放在嘴中输出数组中相应的位置上。 但是计数排序基于这样一个假设:n个输入元素的每一个大小范围都是[0,k]。

void CountintSort(int A[],int *B,int n,int k,int *C)
{
    //初始化C数组
    for (int i = 0;i <= k;++i)
    {
        C[i] = 0;
    }
  
    for (int i = 0;i < n;++i)
    {
        ++C[A[i]];//C[i]:值等于i的元素的个数
    }
  
    for (int i = 1;i <= k;++i)
    {
        C[i] += C[i - 1];//C[i]:值小于等于i的元素的个数
    }
      
    for (int i = n - 1;i >= 0;--i)
    {
        B[C[A[i]] - 1] = A[i];//注意:下标索引从0开始!
        --C[A[i]];
    }
}

时间复杂度是O(k + n)。一般,当k = O(n)时,常常采用计数排序。这时候的运行时间为O(n)。计数排序是稳定的排序。
基数排序
算法思想

基数排序是从低位到高位依次对所有的数进行排序。如果所有的数最高位数是d,那么先按最低有效位数字进行排序,得到一个结果。然后往高位重复这个过程。需要注意的是,按位排序必须是稳定的排序算法。经常采用的是计数排序。

/********************************************************
*函数名称:GetNumInPos
*参数说明:num 一个整形数据
*         pos 表示要获得的整形的第pos位数据
*说明: 找到num的从低到高的第pos位的数据
*********************************************************/
int GetNumInPos(int num,int pos)
{
    int temp = 1;
    for (int i = 0; i < pos - 1; i++)
        temp *= 10;

    return (num / temp) % 10;
}

/********************************************************
*函数名称:RadixSort
*参数说明:pDataArray 无序数组;
*         iDataNum为无序数据个数
*说明: 基数排序
*********************************************************/
#define RADIX_10 10 //整形排序
#define KEYNUM_31 10 //关键字个数,这里为整形位数
void RadixSort(int* pDataArray, int iDataNum)
{
    int *radixArrays[RADIX_10]; //分别为0~9的序列空间
    for (int i = 0; i < 10; i++)
    {
        radixArrays[i] = (int*)malloc((iDataNum + 1)*sizeof(int));
        radixArrays[i][0] = 0; //index为0处记录这组数据的个数
    }
    
    for (int pos = 1; pos <=KEYNUM_31; pos++) //从个位开始到KEYNUM_31位
    {
        for (int i = 0; i < iDataNum; i++) //分配过程
        {
            int num = GetNumInPos(pDataArray[i], pos);
            int index = ++radixArrays[num][0];
            radixArrays[num][index] = pDataArray[i];
        }

        int j;
        for (i = 0, j =0; i < RADIX_10; i++) //收集
        {
            for (int k = 1; k <= radixArrays[i][0]; k++)
                pDataArray[j++] = radixArrays[i][k];
            radixArrays[i][0] = 0; //复位
        }
    }
}

桶排序
当输入数据符合均匀分布时,即可以以线性期望时间运行。即使输入不满足线性关系,桶排序也仍然可以以线性时间运行。只要输入满足这样一个性质,即各个桶尺寸的平方和与总的元素数呈线性关系。

桶排序的思想:
将区间[0,1)分成n个相同大小的子区间,或称为桶。然后将n个输入元素分布到各个桶中去。每个桶中的元素用一个链表来存储

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cmath>
  
using namespace std;
  
//桶中链表节点数据结构
typedef struct StructLinkNode{
    double elem;
    struct StructLinkNode *next;
}LinkNode,*LinkNodePtr;
  
//桶排序
void BucketSort(double *a,int n);
//删除一条链表
void deleteLinkList(LinkNodePtr head);
  
int main()
{
    srand(time(NULL));
    int n = 8;
    double *a = new double[n];
    for(int i = 0;i < n;++i)
        *(a + i) = rand() * 1.0 / RAND_MAX;
  
    cout << "Before sort : " << endl;
    for(int i = 0;i < n;++i)
        cout << *(a + i) << " ";
    cout << endl;
  
    BucketSort(a,n);
  
    cout << "After sort : " << endl;
    for(int i = 0;i < n;++i)
        cout << *(a + i) << " ";
    cout << endl;
}
  
//桶排序
void BucketSort(double *a,int n)
{
    //存放链表的数组
    LinkNodePtr *linkListArr = new LinkNodePtr[n];
    //初始化
    for (int i = 0;i < n;++i)
    {
        linkListArr[i] = new LinkNode;
        linkListArr[i]->elem = -1;
        linkListArr[i]->next = NULL;
    }
  
    //将n个输入元素依次放入n个桶中
    for (int i = 0;i < n;++i)
    {
        LinkNodePtr newNode = new LinkNode;
        newNode->elem = *(a + i);
        newNode->next = NULL;
  
        //将新元素插入对应桶的链表的正确位置
        int index = floor(n * *(a + i));
        LinkNodePtr loopPtr = linkListArr[index]->next;
        LinkNodePtr prevPtr = linkListArr[index];
        while(loopPtr != NULL && *(a + i) > loopPtr->elem)
        {
            prevPtr = loopPtr;
            loopPtr = loopPtr->next;
        }
        newNode->next = loopPtr;
        prevPtr->next = newNode;
    }
  
    int count = 0;
    for (int i = 0;i < n;++i)
    {
        LinkNodePtr loopPtr = linkListArr[i]->next;
        while(loopPtr != NULL)
        {
            *(a + count) = loopPtr->elem;
            ++count;
            loopPtr = loopPtr->next;
        }
    }
  
    for (int i = 0;i < n;++i)
        deleteLinkList(linkListArr[i]);
}
  
//删除一条链表
void deleteLinkList(LinkNodePtr head)
{
    if (NULL == head)
    {
        return;
    }
    deleteLinkList(head->next);
    delete head;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值