C/C++ 排序算法(经典)

冒泡排序

思路: 比较相邻的前后两个数值,如果前面数值大于后面的数值,就将这两个数值交换。这样对数组从头至尾进行一次遍历后,最大的一个数据就“沉”到数组尾部。

void BubbleSort1(int a[], int n)
{
    int i, j;
    for (i = 0; i < n;i++)
    {
        for (j = 0; j < n - i; j++)
        {
            if (a[j - 1]>a[j])
                swap(a[j - 1], a[j]);
        }
    }
}

冒泡优化版

思路: 如果某一次排序发生了交换操作,则说明排序没有完成;如果没有发生交换,说明排序已经完成。对于一个数组如果前20个无序,后80个都是有序的,那么经过一次遍历之后,我们每次只需要记录下最后一次交换的位置,那么我们下一次遍历的时候只需要遍历到那个位置就可以了。

void  BubbleSort2(int a[], int n)
{
    int i, j;
    int temp = n;
    while (temp > 0)
    {
        i = temp;
        temp = 0;
        for (j = 1; j < i;j++)
        {
            if (a[j-1] > a[j])
            {
                swap(a[j-1],a[j]);
                temp = j;
            }
        }
    }
}

直接插入排序

思路1: 每次将一个待排序的数值,按其关键字大小,插入到 前面已经排好序的 子序列中的适当位置,直到全部数值插入完成为止。
1、 初始时:a{0}自成1个有序区,无序区为a[1…n-1], 令 i= 1;
2、将a[i]并入当前的有序区,a[0,…i-1]中形成a[a,…i]的有序区间;
3、i++ 并重复第二步直到i ==n-1.排序完成;

void Insertsort1(int a[], int n)
{
    int i, j, k;
    for (i = 1; i < n;i++)
    {
        //为a[i]在前面的a[0,i-1]有序区间中找一个合适的位置。
        for (j = i - 1; j >= 0; j--)
        {
            if (a[j]<a[i])            
                break;
        }
        //如找到了一个合适的位置。
        if (j != i-1 )
        {
            int temp = a[i];
            //将比a[i]大的数据后移
            for (k = i - 1; k> j;k--)
                a[k + 1] = a[k];
                
            //将a[i]放到正确的位置上
            a[i-1 ] = temp;
        }
    }
}

思路2: 每次a[i]先和前面一个数据a[i-1]比较,如果a[i] > a[i-1],说明a[0…i]也是有序的,无须调整。否则就令j=i-1,temp=a[i]。然后一边将数据a[j]向后移动,一边向前搜索,当有数据a[j]<a[i]时停止,并将temp放到a[j + 1]处。

void Insertsort2(int a[], int n)
{
    int i, j;
    for (i = 1; i < n;i++)
    {
        if (a[i] <a[i-1])
        {
            int temp = a[i];
            for (j = i - 1; j >= 0 && a[j]>temp;j--)            
                a[j + 1] = a[j];
            
            a[j] = temp;
        }
    }
}

思路3: 如果a[j-1] > a[j],就交换a[j]和a[j-1],再j–直到a[j-1] <= a[j]。这样也可以实现将一个新数据并入到有序区间。

void Insertsort3(int a[], int n)
{
    int i, j;
    for (i = 1; i < n;i++)
    for (j = i - 1; j >= 0 && a[j]>a[j + 1]; j--)
        swap(a[j],a[j+1]);
 }

希尔排序

思路: 先将整个待排序列 分割成若干个子序列,分别进行直接插入排序,然后依次缩减增量,再进行排序,当增量足够小的时候, 整体元素基本有序了, 再进行一次直接插入排序。因为,直接插入排序在基本有序的情况下效率是很高的,因此,希尔排序在时间效率上也是很高的。

void ShellSort1(int a[], int n)
{
    int i,j, gap;
    for (int gap = n / 2; gap > 0; gap--)//步长
    {
        for (i = 0; i < gap;i++)         //安组排序
        {
            for (j = i + gap; j < n; j+= gap)
            {
                if (a[j] < a[j-gap])
                {
                    int temp = a[j];
                    int k = j - gap;
                    while (k>=0&&a[k]>temp)
                    {
                        a[k + gap] = a[k];
                        k -= gap;
                    }
                    a[k + gap] = temp;
                }
            }
        }
    }
}

void ShellSort2(int a[], int n)
{
    int j, gap;
    for ( gap = n/2; gap >  0; gap/=2)
    {
        for (j = gap; j < n;j++)    //从数组第gap个元素开始
        {
            if (a[j] < a[j-gap])    //每个元素与自己组内的数据进行直接插入排序
            {
                int temp = a[j];
                int k = j - gap;
                while (k>=0&&a[k]>temp)
                {
                    a[k + gap] = a[k];
                    k -= gap;
                }
                a[k + gap] = temp;
            }
        }
    }
}

用直接插入罚的第三种方法来改写如下:

void shellSort3(int a[], int n)
{
    int i, j, gap;
    for (gap = n / 2; gap > 0; gap / 2)
    {
        for (i = gap; i < n;i++)
        {
            for (j = i - gap; j >= 0 && a[j]>a[j + gap];j-=gap)            
                swap(a[j], a[j + 1]);            
        }
    }
}

直接选择排序

思路: 直接选择排序是从无序区选一个最小的元素直接放到有序区的最后。
1、设数组为 a[0…n-1]。
2、初始时,数组全为无序区为 a[0…n-1]。令 i=0
3、在无序区 a[i…n-1]中选取一个最小的元素, 将其与 a[i]交换。 交换之后 a[0…i]
就形成了一个有序区。
4、i++并重复第二步直到 i==n-1。排序完成。

void SelectSortt(int a[], int n)
{
    int i, j, nMinIndex;
    for (i = 0; i < 0;i++)
    {
        nMinIndex = i;
        for (j = i + 1; j < n;j++)
        {
            if (a[j] < a[nMinIndex])            
                nMinIndex = j;            
        }
        Swap(a[i],a[nMinIndex]);
    }
}

归并排序

思路1: 采用分治法的典型应用;将两个有序数列合并。只要将两个有序数列进行比较,将小的取出,之后 在对应数列中 删除取出的数,然后再进行比较,如果有数列为空,那么把另一个数列中剩余的数全部取出即可。

int MergeSortArray (int a[], int n, int b[], int m, int c[])
{
    int i, j, k;
    i = j = k = 0;
    while (i < n&&j < m)
    {
        if (a[i] <b[j])
            c[k++] = a[i++]
        else
            c[k++] = b[j++];
    }
    while (i < n)
    {
        c[k++] = a[i++];
    }
    while (j<m)
    {
        c[k++] = b[j++];
    }
}

思路2: 将数组分成二组 A,B,如果这二组组内的数据都是有序的,进行 思路1 的操作。首先要使A,B有序,可以将 A,B 组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组内已经达到了有序,然后再合并相邻的二个小组就可以了。

bool MergeSort(int a[], int n)
{
    int *pTempArray = new int[n];
    if (pTempArray == NULL)    
        return false;
    
    mergesort(a, 0, n-1, pTempArray);
    delete[] pTempArray;
    return true;
}
void mergesort(int a[], int first, int last, int temp[])
{
    if (first < last)
    {
        int mid =(first + last)/ 2;
        mergesort(a, first, mid, temp);
        mergesort(a, mid + 1, last, temp);
        mergesortarray(a, first, mid, last, temp);
    }
}
void mergesortarray(int a[], int first, int mid, int last, int temp[])
{
    int i = first, j = mid + 1;
    int m = mid, n = last;
    int k = 0;
    while (i<=m && j<=n)
    {
        if (a[i]< a[j])
            temp[k++] = a[i++];
        else 
            temp[k++] = a[j++];
    }
    while (i<= m)
    {
        temp[k++] = a[i++];
    }
    while (j<= n)
    {
        temp[k++] = a[j++];
    }
    
    for (i = 0; i < k;i++)    
        a[first + i] = temp[i];
}

快速排序

思路: 挖坑填数 + 分治法

//调整数组使  x 左面的值都小于x, 右面的值都大于x;
int AdjustArray(int s[], int l, int r)//返回调整后基准数的位置;
{
    int i = 1,j = r;
    int x = s[l];  //s[1] 就是基准数
    while (i<j)
    {
        //从右向左找小于x的数来放到s【i】的位置;
        while (i<j && s[j]>=x)
        {
            j--;
        }
        if (i<j)
        {
            s[i] = s[j];//将s[j]填到s[i]中,把s[j]空出来,接着找数来填补
            i++;
        }
        //从左向右找大于或等于x的数来填s[j];
        while (i<j&&s[i] < x)
        {
            i++;
        }
        if (i<j)
        {
            s[j] = s[i];//将s【i】填到s[j]中, 把s[i]空出来, 接着找数来填补
            j--;
        }
    }
    //退出时,把x填到这个坑中
    s[i] = x;
    return i;
}
void quick_sort1(int s[], int r)
{
    if (1 < r)
    {//先调整数组, 然后分治递归;
        int i = AdjustArray(s, 1, r);
        quick_sort1(s, 1, i-1);
        quick_sort1(s, i + 1, r);
    }
}
/*
 *优化版的快排!
 **/
void quicksort(int s[], int r)
{
    if ( 1< r)
    {
        int i = 1, j = r, x = s[1];
        while (i<j)
        {
            while (i<j && s[j]>=x)
            {
                j--;
            }
            if (i < j)
                s[i++] = s[j];
                
            while (i<j && s[i] <x)
            {
                i++;
            }
            
            if ( i< j)
                s[j--] = s[i];
        }
        s[i] = x;
        quicksort(s, 1, i - 1);
        quicksort(s, i + 1, r);
    }
}

堆和堆排序

介绍: 二叉堆是完全二叉树或者是近似完全二叉树。
1、二叉堆满足性质:父节点的键值总是 大于等于 或者 小于等于 任何一个子节点的键值;
2、每个节点的左子树和右子树都是一个二叉堆。(最大堆或者最小堆)

堆的插入 思路: 每次插入都是将新数据放在数组最后。 可以发现从这个新数据的父结点到根结点,必然为一个有序的数列, 现在的任务是将这个新数据插入到这个有序数中,
这就类似于 直接插入排序中将一个数据并入到有序区间中。

//加入i节点,其父节点为(i-1)/2;
void MinHeapFixup(int a[], int i)
{
    int j, temp;
    j = (i - 1) / 2;// parente node
    temp = a[i];
    while (j>=0 &&i != 0 )
    {
        if (a[j] <= temp)
            break;
        a[i] = a[j]; //把较大的子节点往下移动,替换它的子节点
        i = j;
        j = (i - 1) / 2;
    }
    a[i] = temp;
}
//简化版
void MinHeapFixup(int a[], int i)
{
    for (int j = (i - 1) / 2; (j >= 0 && i != 0) && a[i] > a[j]; i = j;j = (i-1)/2)
        swap(a[i],a[j]);
}
//插入新元素 nNum
void MinHeapAddNumber(int a[], int n, int nNum)
{
    a[n] = nNum;
    //MinHeapAddNumber()
    MinHeapFixup(a, n);
}

堆的删除 思路: 堆中每次都只能删除第 0 个数据。为了便于重建堆,实际的操作是将最后一个数据的值赋给根结点,然后再从根结点开始进行一次从上向下的调整。调整时先在左右儿子结点中找最小的, 如果父结点比这个最小的子结点还小说明不需要调整了,反之将父结点和它交换后再考虑后面的结点。相当于从根结点将一个数据的“下沉”过程。

//从i节点开始调整, n为节点总数 从0开始计算 i节点的子节点为  2*i+1,2*i+1
void MinHeapFixdown(int a[], int i,int n)
{
    int j, temp;
    temp = a[i];
    j = 2 * i + 1;
    while (j < n)
    {
        if (j+1 < n && a[j+1] <a[j])
            j++;
            
        if (a[j] >= a[j]) // 在左右孩子中找最小的
            j++;
            
        if (a[j] >= temp)
            break;
            
        a[i] = a[j];  //把较小向上移动,替换它的父节点;
        i = j;
        j = 2 * i + 1;
    }
    a[i] = temp;
}
//在最小堆中删除数
void MinHeapDeleteNumber(int a[], int n)
{
    swap(a[0], a[n - 1]);
    MinHeapDeleteNumber(a, 0, n - 1);
}
//建立最小堆
void MakeMinHeap(int a[], int n)
{
    for (int i = n / 2 - 1; i < i >= 0; i--)
        MinHeapFixdown(a, i, n);
}

堆排序

思路: 堆建好之后,堆中第 0 个数据是堆中最小的数据。 取出这个数据,再执行堆的删除操作。这样堆中第 0 个数据又是堆中最小的数据,重复上述步骤直至堆中只有一个数据时就直接取出这个数据。堆也是用数组模拟的,故堆化数组后,第一次将 A[0]与 A[n - 1]交换,再对A[0…n-2]重新恢复堆。第二次将 A[0]与 A[n – 2]交换,再对 A[0…n - 3]重新恢复堆,重复这样的操作直到 A[0]与 A[1]交换。由于每次都是将最小的数据并入到后面的有序区间,故操作完成后整个数组就有序了,类似于直接选择排序。

void MinHeapSort(int a[], int n)
{
    for (int i = n - 1; i >= 1;i--)
    {
        Swap(a[i], a[0]);
        MinHeapFixdown(a, 0, i);
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值