C++七种基本排序算法

目录

BubbleSort

InsertSort

二分排序

选择排序

HillSort

快速排序(递归)

快速排序(非递归)(使用栈模拟)

归并排序(递归)

归并排序(迭代)(自底向上)

堆排序



BubbleSort

// 由小到大
void BubbleSort( int arr[], int n )
{
    for (int i = 0; i < n; i++)
    {
        for (int j = n-1; j > i; j--)
        {
            if (arr[j-1] > arr[j])
            {
                swap( arr[j-1], arr[j]);
            }
        }
    }
}


InsertSort

// 找到当前数据合适插入位置进行插入
void InsertSort( int arr[], int n )
{
    int i, j;
    // 第一个元素相当于已经排好,从第二个元素开始
    for (i = 1; i < n; i++)
    {
        int tmp = arr[i];

        // 从i之前的元素开始倒序比较
        for ( j = i-1; j >= 0 && arr[j] > tmp; j--)
        {
            arr[j+1] = arr[j];
        }
        
        // arr[j] <= tmp 跳出循环, 所以arr[i]的位置为j+1
        // arr[i] 插入到j+1索引位置
        arr[j+1] = tmp;
    }
}

二分排序

二分排序就是折半插入排序,当直接插入排序进行到某一趟时,对于前面记录已经按关键字有序,此时不用直接插入排序的方法,而用折半二分查找,找出下一个元素应插入的位置,然后插入,这种方法就是折半插入排序,这种方法中比较次数,由于采用折半查而减少,为O(nlogn),但是元素交换的次数仍为O(n2),二分排序算法是稳定的。

void binary_sort(int a[],int n)
{
    int low ,high, mid;
    int tem;
    for(int i=1; i<n; i++)
    {
        tem=a[i];
        low=0;
        high=i-1;
        while(low<=high)
        {
            mid=(low+high)/2;
            if(a[mid]>tem)
            {
                high=mid-1;
            }
            else
            {
                low=mid+1;
            }
        }
        for(int j=i-1; j>high; j--)
        {
            a[j+1]=a[j];
        }
        a[high+1]=tem;
    }
}


选择排序

// 从[i...n-1]中找到最小的值,放到arr[i]的位置
void SelectionSort(int arr[], int n)
{
    int i, j;
    for ( i = 0; i < n; i++)
    {
        //将当前值作为最小值,索引值保存在minIndex中
        int minIndex = i;

        for ( j = i+1; j < n; j++)
        {
            if (arr[j] < arr[minIndex])
            {
                minIndex = j;
            }
        }

        if (minIndex != i)
        {
            swap(arr[i], arr[minIndex]);
        }
    }
}

HillSort

// 希尔排序,是插入排序的改进,它将一个数组按一定间隔分成几部分进行插入排序
void HillSort(int arr[], int n)
{
    int increament = n;
    int i, j;
    do
    {
        increament = increament/3+1;
        // i初始值从increament开始,是从每组数据的第二个开始
        // 因为每组数据第一个相当与已经排好序了
        for( i = increament; i < n; i++)
        {
            int tmp = arr[i];
            // j = i-increament 是同一组i索引的前一个元素
            for( j = i-increament; j >= 0 && tmp < arr[j]; j-=increament)
            {
                arr[j+increament] = arr[j];
            }
            // 插入位置应该为j,但是for循环中最后计算了一次j-=increament
            arr[j+increament] = tmp;
        }
    }
    while (increament > 1);
}

快速排序(递归)

// 选择一个pivot,将数组分成两段,一段比pivot小,一段比pivot大
void QuickSort( int arr[], int n)
{
    QSort( arr, 0, n-1);
}

void QSort(int arr[], int low, int high)
{
    if (low >= high) return;

    // 将arr一分为二 [low... pivot-1], [pivot+1...high]
    int pivot = Partition( arr, low, high );

    QSort( arr, low, pivot-1 );
    QSort( arr, pivot+1, high);
}

// Partition实现一(推荐)
// 将arr 以pivotkey 一分为二
// v = arr[low]  [low+1...j] < v   [j+1...high) > v
int Partition( int arr[], int low, int high){

    swap( arr[low] , arr[rand()%(high - low + 1)+low] );//随机取数作为轴
    
    int v = arr[low];
    int j = low; // 左半部(比v小)最后一个数索引
    //i = low+1 开始遍历,arr[i] > v,++i;
    //  <v,将其和第一个比v大的数(arr[j+1])交换, 再++j,++i,则 左半部分增添了一个数
    // 最后再swap(arr[low], arr[j]) 将v交换至分隔位置
    for (int i = low+1; i <= high; ++i){
        if (arr[i] < v){
            swap( arr[j+1], arr[i]);
            ++j;
        }
    }
    swap( arr[low], arr[j] );
    return j;
}


// 将arr 以pivotkey 一分为二
int Partition( int arr[], int low, int high)
{
    int pivotkey = arr[low];
    int tmp = pivotkey;
    while (low < high)
    {
        while (low < high && pivotkey <= arr[high])
            high--;
        //swap( arr[low], arr[high]);
        // 可以不使用交换,因为从最开始的tmp = pivotkey,已经将每个要交换的值进行了备份
        // ar[low] = arr[high]将arr[high]已经备份到arr[low]中,每一次覆盖前都有备份
        arr[low] = arr[high];

        while (low < high && pivotkey >= arr[low])
            low++;
        //swap( arr[low], arr[high]);
        arr[high] = arr[low];
    }
    arr[low] = tmp;
    return low;
}

快速排序(非递归)(使用栈模拟)

void QSort( int arr[], int left, int right ){
    if (left >= right)
        return ;
    
    stack<int> s;
    s.push(left);
    s.push(right); // right后入栈, 所以后边先出栈
    while (!s.empty()){
        int r = s.top();
        s.pop();
        int l = s.top();
        s.pop();

        int index = Partition(arr, l, r); // 划分 ; 之后继续将划分每一部分
        
        if (l < index-1){    // 左子序列
            s.push(left);
            s.push(index-1);
        }
        if (index+1 < r){   // 右子序列
            s.push(index+1);
            s.push(r);
        }
    }
}

归并排序(递归)

void MergeSort( int arr[], int n)
{
    MSort( arr, 0, n-1 );
}

void MSort( int arr[], int low, int high )
{
    if (low >= high)
        return;
    int middle = low + (high-low)/2; // (low+high)/2


    MSort( arr, low, middle );
    MSort( arr, middle+1, high );
    Merge( arr, low, middle, high);
}

// 将两个有序数组归并
// arr[low...middle]  arr[middle+1 ... high]
// 赋值为 tmp[0...high-low]
void Merge( int arr[], int low, int middle, int high )
{
    int tmp[high-low+1];
    // tmp 数组想对于arr的索引有一个向左的low偏移
    for (int i = low; i <= high; i++)
    {
        tmp[i-low] = arr[i];
    }

    // 两个索引,分别指向左半部开始 和 右半部开始
    int i = low, j = middle+1;
    for (int k = low; k <= high; k++)
    {
        if (i > middle)
        {
            arr[k] = tmp[j-low];
            j++;
        }
        else if (j > high)
        {
            arr[k] = tmp[i-low];
            i++;
        }
        else if (tmp[i-low] < tmp[j-low])
        {
            arr[k] = tmp[i-low];
            i++;
        }
        else
        {
            arr[k] = tmp[j-low];
            j++;
        }
    }
}

归并排序(迭代)(自底向上)

//自底向上实现:最先归并的时候,每段长度最小,都为1; 归并之后每段长度变成2
//归并一次,长度加倍
//归并的范围从[0..sz-1]与[sz+1, 2*sz-1]归并,[2*sz, 3*sz-1]与[3*sz, 4*sz-1]...

void MergeSort( int arr[], int n ){
    
    for (int sz = 1; sz <= n; sz += sz){
        for (int i = 0; i + sz < n; i += sz + sz){
             // 对 arr[i...i+sz-1] 和 arr[i+sz...i+2*sz-1]进行归并
            // i + sz < n保证i+sz-1在n的范围内
            // min( i+sz+sz-1, n-1 ) 最后一部分可能i+sz+sz-1超过了n-1,只需归并到n-1即可
            Merge( arr, i, i+sz-1, min( i+sz+sz-1, n-1) );
        }
    }
}  

堆排序

// 1. 先建堆(大顶堆,或者小顶堆)
// 2. 在将根节点值与最后位置(层序遍历)交换
void HeapSort( int arr[], int n )
{
    // n/2-1 是最后一个非叶子节点(有孩子)的索引
    // 对该节点及孩子进行调整,构成大顶堆
    // 对每一个叶子节点遍历进行操作调整
    for (int i = n/2-1; i >= 0; i--)
    {
        HeapAdjust( arr, i, n);
    }

    for (int i = n-1; i >= 0; i--)
    {
        // 交换一次,堆的节点个数减少一个,所以为 i
        swap( arr[0], arr[i] );

        // 将arr[0...i-1] 重新调整为大顶堆
        // 这里不需要和初始建堆一样加一个循环,是因为整个数组已经有序,
        // 只需对相应的节点从上到下调整即可
        HeapAdjust( arr, 0, i);
    }
}

//将arr[s...n-1]调整为大顶堆   n指arr数组长度
void HeapAdjust( int arr[], int s, int n)
{
    for (int j = 2*s+1; j < n; j=2*j+1)
    {
        // 右孩子比左孩子大,更新j,指向右孩子索引
        // j+1<n 右孩子索引在范围内
        if (j+1 < n && arr[j] < arr[j+1])
        {
            j++;
        }
        // 根节点值比左右孩子节点都大,不用交换
        if (arr[s] > arr[j])
        {
            break;
        }

        //将跟节点值与左右孩子中较大的交换
        swap( arr[s], arr[j] );
        // s指向
        s = j;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值