快速排序

1. 快速排序:

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序n个元素要O(nlogn)次比较。在最坏状况下则需要O(n^2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他O(nlogn)算法更快,因为它的内部循环可以在大部分的架构上很有效率地被实现出来。  

一趟快速排序的算法是:

1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;

2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];

3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]互换;

4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;

5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。

// 分类 ------------ 内部比较排序
// 数据结构 --------- 数组
// 最差时间复杂度 ---- 每次选取的基准都是最大(或最小)的元素,导致每次只划分出了一个分区,需要进行n-1次划分才能结束递归,时间复杂度为O(n^2)
// 最优时间复杂度 ---- 每次选取的基准都是中位数,这样每次都均匀的划分出两个分区,只需要logn次划分就能结束递归,时间复杂度为O(nlogn)
// 平均时间复杂度 ---- O(nlogn)
// 所需辅助空间 ------ 主要是递归造成的栈空间的使用(用来保存left和right等局部变量),取决于递归树的深度,一般为O(logn),最差为O(n)       
// 稳定性 ---------- 不稳定

快速排序代码如下:

#include <iostream>
using namespace std;
void quicksort(int A[], int first, int last)
{
    int pivot=A[first];
    int low=first;
    int high=last;
    if(first>last)
    {
        return;
    }
    while(first<last)
    {
        while(first<last && A[last]>=pivot)
        {
            --last;
        }
        A[first]=A[last];
        while(first<last && A[first]<=pivot)
        {
            ++first;
        }
        A[last]=A[first];
    }
    A[first]=pivot;
    quicksort(A,low,first-1);
    quicksort(A,first+1,high);
    
}
int main()
{
    int arr[]={5,2,9,4,7,6,1,3,8};
    int n=sizeof(arr)/sizeof(int);
    int left=0;
    int right=n-1;
    quicksort(arr,left,right);
    for(int i=0;i<n;i++)
    {
        cout<<arr[i]<<' ';
    }
    return 0;
}
2. 插入排序:

插入排序是一种简单直观的排序算法。它的工作原理非常类似于我们抓扑克牌。

对于未排序数据(右手抓到的牌),在已排序序列(左手已经排好序的手牌)中从后向前扫描,找到相应位置并插入。

  插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

  具体算法描述如下:

  1. 从第一个元素开始,该元素可以认为已经被排序
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
  5. 将新元素插入到该位置后
  6. 重复步骤2~5
  7. // 分类 ------------- 内部比较排序
    // 数据结构 ---------- 数组
    // 最差时间复杂度 ---- 最坏情况为输入序列是降序排列的,此时时间复杂度O(n^2)
    // 最优时间复杂度 ---- 最好情况为输入序列是升序排列的,此时时间复杂度O(n)
    // 平均时间复杂度 ---- O(n^2)
    // 所需辅助空间 ------ O(1)
    // 稳定性 ------------ 稳定

  插入排序的代码如下:

#include <iostream>
using namespace std;
int main()
{
    int a[]={6,5,3,1,8,7,2,4};
    int n=sizeof(a)/sizeof(int);
    for(int i=1;i<n;i++)
    {
        int key=a[i];
        int k=i-1;
        while(k>=0 && key<a[k])
        {
            a[k+1]=a[k];
            k--;
        }
        a[k+1]=key;
        cout<<"第"<<i<<"轮输出为:";
        for(int m=0;m<n;m++)
        {
            cout<<a[m]<<' ';
        }
        cout<<endl;
    }
    return 0;
}

第1轮输出为:5 6 3 1 8 7 2 4

第2轮输出为:3 5 6 1 8 7 2 4

第3轮输出为:1 3 5 6 8 7 2 4

第4轮输出为:1 3 5 6 8 7 2 4

第5轮输出为:1 3 5 6 7 8 2 4

第6轮输出为:1 2 3 5 6 7 8 4

第7轮输出为:1 2 3 4 5 6 7 8

3. 归并排序:

归并排序是创建在归并操作上的一种有效的排序算法,效率为O(nlogn),1945年由冯·诺伊曼首次提出。

  归并排序的实现分为递归实现非递归(迭代)实现。递归实现的归并排序是算法设计中分治策略的典型应用,我们将一个大问题分割成小问题分别解决,然后用所有小问题的答案来解决整个大问题。非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。

  归并排序算法主要依赖归并(Merge)操作。归并操作指的是将两个已经排序的序列合并成一个序列的操作,归并操作步骤如下:

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
  4. 重复步骤3直到某一指针到达序列尾
  5. 将另一序列剩下的所有元素直接复制到合并序列尾

#include <iostream>
using namespace std;
void merge(int A[], int left, int mid, int right)// 合并两个已排好序的数组A[left...mid]和A[mid+1...right]
{
    int len = right - left + 1;
    int *temp = new int[len];       // 辅助空间O(n)
    int index = 0;
    int i = left;                   // 前一数组的起始元素
    int j = mid + 1;                // 后一数组的起始元素
    while (i <= mid && j <= right)
    {
        temp[index++] = A[i] <= A[j] ? A[i++] : A[j++];  // 带等号保证归并排序的稳定性
    }
    while (i <= mid)
    {
        temp[index++] = A[i++];
    }
    while (j <= right)
    {
        temp[index++] = A[j++];
    }
    for (int k = 0; k < len; k++)
    {
        A[left++] = temp[k];
    }
}
void mergerecusion(int arr[], int left, int right)
{
    if(left==right)
    {
        return;
    }
    cout<<"未合并之前输出为:"<<endl;
    for(int i=0;i<left+right+1;i++)
    {
        cout<<arr[i]<<' ';
    }
    cout<<endl;
    int mid=(left+right)/2;
    mergerecusion(arr,left,mid);
    mergerecusion(arr,mid+1,right);
    merge(arr,left,mid,right);
}
int main()
{
    int a[]={6,5,3,1,8,7,2,4};
    int n=sizeof(a)/sizeof(int);
    int left=0;
    int right=n-1;
    mergerecusion(a,left,right);
    for(int i=0;i<n;i++)
    {
        cout<<a[i]<<' ';
    }
    return 0;
}

未合并之前输出为:

6 5 3 1 8 7 2 4

未合并之前输出为:

6 5 3 1

未合并之前输出为:

6 5

未合并之前输出为:

5 6 3 1 8 7

未合并之前输出为:

1 3 5 6 8 7 2 4 7 0 8 0

未合并之前输出为:

1 3 5 6 8 7 2 4 7 0

未合并之前输出为:

1 3 5 6 7 8 2 4 7 0 8 0 0 0

1 2 3 4 5 6 7 8

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值