面试常考之排序算法

总结一下面试常常遇到的基础排序算法(默认升序),方便接下来复习:

1.选择排序

思想:每一趟选出最小值
复杂度分析:最坏情况O(n^2),最好情况O(n^2),平均情况O(n^2),额外空间O(1)
稳定性:不稳定
例子:9 3 6 2 8 7

  • 2 3 6 9 8 7 (第一趟)
  • 2 3 6 9 8 7 (第二趟)
  • 2 3 6 9 8 7 (第三趟)
  • 2 3 6 7 8 9 (第四趟)
  • 2 3 6 7 8 9 (第五趟)
  • 2 3 6 7 8 9 (第六趟)

代码

void selectSort(int *a,int len)
{
    if(len <= 0) return;
    for(int i=0;i<len-1;i++)
    {
        int min = a[i];
        //这个循环选出第i趟最小值
        for(int j=i+1;j<len;j++)
        {
            if(min>a[j])
            {
                min = a[j];
            }
        }
        if(min != a[i])
        {
            swap(a,min,a[i]);
        }
    }
}

2.插入排序

思想:插入第i个元素时,与前面已经排好序的i-1个元素比较,如果它不是插入到最后一个位置,那么大于它的序列就要顺序向后移动一个位置。
复杂度分析:最坏情况O(n^2),最好情况O(n),平均情况O(n^2),额外空间O(1)
稳定性:稳定
例子:9 3 6 2 8 7

  • 9 3 6 2 8 7(第一趟)
  • 3 9 6 2 8 7(第二趟)
  • 3 6 9 2 8 7(第三趟)
  • 2 3 6 9 8 7(第四趟)
  • 2 3 6 8 9 7(第五趟)
  • 2 3 6 7 8 9(第六趟)

代码

void insertSort(int *a,int len)
{
    if(len <= 0) return;
    for(int i=1;i<len;i++)
    {
        int j = i-1; //要插入的元素
        int temp = a[i];
        while(j>=0 && temp<a[j])
        {
            //后移
            a[j+1] = a[j];
            j--;
        }
        a[j+1] = temp; //多执行了一次j--,所以要+1
    }
}

3.希尔排序(递减增量排序)

思想:相距增量(d)的元素为一组,内部采用插入排序方法使组内有序。然后增量减小一个单位,再组内排序,迭代直到增量为1。
复杂度分析:最坏情况O(n^1.3),最好情况O(n),平均情况O(n^2),额外空间O(1)
稳定性:不稳定
例子:9 3 6 2 8 7 (初始:元素个数为6,设置d= 6/2=3,那么9和2为一组,3和8为一组,6和7为一组)

  • 2 3 6 9 8 7(第一趟d=3,9和2交换位置,其他两组已经有序)
  • 2 3 6 7 8 9(第二趟d=2,2 6 8一组,3 9 7一组,9和7交换)
  • 2 3 6 7 8 9(第三趟d=1,只剩一组,已经有序)

代码

void shellSort(int *a,int len)
{
    if(len <= 0) return;
    for(int d=len/2;d>=1;d--)
    {
        for(int i=d;i<len;i++)
        {
            //每一组内部采取插入排序
            if(a[i-d]>a[i])
            {
                int temp = a[i];
                int k = i-d;
                while(k>=0 && a[k]>= temp)
                {
                    a[k+d] = a[k];
                    k = k-d;
                }
                a[k+d] = temp;
            }
        }
    }
}

4.冒泡排序

思想:每一趟相邻元素两两比较,逆序则交换(想象成泡泡往上冒),每一次保证最大的到达顶部,排好序的最大元素不纳入下一趟排序。
复杂度分析:最坏情况O(n^2),最好情况O(n),平均情况O(n^2),额外空间O(1)
稳定性:稳定
例子:9 3 6 2 8 7

  • 3 6 2 8 7 9(第一趟)
  • 3 2 6 7 8 9 (第二趟)
  • 2 3 6 7 8 9 (第三趟)
  • 2 3 6 7 8 9 (第四趟)
  • 2 3 6 7 8 9 (第五趟)
  • 2 3 6 7 8 9 (第六趟)

代码

void bubbleSort(int *a,int len)
{
    if(len <= 0) return;
    for(int i=len-1;i>0;i--)
    {
        for(int j=0;j<=i;j++)
        {
            if(a[j]>a[j+1])
            {
                swap(a,a[j],a[j+1]);
            }
        }
    }
}

5.快速排序

思想:确定pivot之后(一般取第一位元素作为pivot),扫描一趟,比pivot小的放左边,比pivot大的放右边。再对左子序列和右子序列分别调用函数本身(递归),直到不能再分出左子序列和右子序列。
复杂度分析:最坏情况O(n^2),最好情况O(nlogn),平均情况O(nlogn),额外空间O(nlogn)
稳定性:不稳定
例子:4 8 2 6 5 3

  • 2 3 4 8 6 5(第一趟)
  • 2 3 4 6 5 8(第二趟)
  • 2 3 4 5 6 8(第三趟)

代码

void quickSort(int *a,int left,int right)
{
    if(right <= left) return;
    int pivot = a[left];
    int i = left;
    int j = right;
    while(i < j)
    {
        while(j>i && a[j]>= pivot) //从右到左找比pivot小的
        {
            j--;
        }
        if(j>i)
        {
            a[i] = a[j];
            i++;
        }
        while(j>i && a[i]<= pivot) //从左到右找比pivot小的
        {
            i++;
        }
        if(j>i)
        {
            a[j] = a[i];
            j--;
        }
    }
    a[i] = pivot; //完成一趟
    quickSort(a,l,i-1); //左子序列
    quickSort(a,i+1,r); //右子序列
}

6.归并排序

思想
复杂度分析:最坏情况O(nlog),最好情况O(nlogn),平均情况O(nlogn),额外空间O(n)
稳定性:稳定
例子:4 8 2 6 5 3

  • 4 8 2  | 6 5 3
  • 4 |  8 2  | 6  |  5 3

代码

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); //对右子序列排序
        mergeArray(a,first,mid,last,temp); //合并
    }
}

//两个有序子序列合并成一个新的序列
void mergeArray(int *a,int left,int mid,int right,int *temp)
{
    int i = left, j = right;
    int k = 0;
    //循环挑出相对较小的放入临时数组temp中从而保证有序
    while(i<=mid && j<=right)
    {
        if(a[i] < a[j])
        {
            temp[k++] = a[i++];
        }else
        {
            temp[k++] = a[j++];
        }
    }
    while(i <= mid) //如果左子序列还有剩,按序填入
    {
        a[k++] = a[i++];
    }
    while(j <= right) //如果右子序列还有剩,按序填入
    {
        a[k++] = a[j++];
    }
    //把temp中放回原数组
    for(int m=0; m<k;m++)
    {
        a[first+m] = temp[m];
    }
}

7.堆排序

思想
复杂度分析:最坏情况O(nlogn),最好情况O(nlogn),平均情况O(nlogn),额外空间O(1)
稳定性:不稳定
性质
1.节点的性质:i表示当前节点,则父节点为(i-1)/2,左孩子节点为(2*i+1),右孩子节点为(2 *i+2);
2.最大堆的父节点要大于等于子节点,(最小堆相反);
3.在新建堆的过程中,采用AdjustDown堆化数组,从n/2节点开始,因为大于n/2的是叶子节点。

代码

//以最大堆为例
//堆调整
void AdjustDown(int a[],int k,int len)//将元素a[k]向下调整
{
    a[0] = a[k];     //a[0]暂存,相当于temp
    for(int i=2*k; i<=len; i*=2)
    {
        if(i<len && a[i]<a[i+1])
        {
            i++;      //取值较大的i的下标
        }
        if(a[0]>=a[i])
        {
            break;     //a[0]>子节点中最大的值,结束筛选
        }else
        {
            a[k] = a[i]; //否则交换
            k = i;       //跟踪k的下标
        }
    }
    a[k] = a[0];         //调整结束后,把a[k]的值补上
}

//新建堆
void BuildMaxHeap(int a[],int len)
{
    for(int i=len/2;i>0;i--)    //从i=len/2 - 1,反复调整堆
    {
        AdjustDown(a,i,len);    //从上往下调整直到子树满足最大堆
    }
}

//堆排序--调用前两个函数
void HeapSort(int a[],int len)
{
    BuildMaxHeap(a,len);   //初始建堆
    for(int i=len;i>1;i--)
    {
        swap(a[i],a[1]);   //可得到堆顶元素
        AdjustDown(a,1,i-1);   //整理剩余的i-1个元素
    }
}

写到后面有点晕掉了,…(◎ロ◎)… ,休息一下…

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值