面试/笔试数据结构之排序算法篇

面试/笔试数据结构之排序算法篇

面试时数据结构应该是面试官们一定会问到的知识块,最常见的就是对于排序、查找等算法的考察。这里先列出一些常见易考,并且比较重要的排序算法。这里都以排成增序为例,如有错误、不好之处敬请指出。

1、 直接插入排序 
假设我们有一个序列{a0,a1,a2,a3……an},将a0看成是一个有序序列,a1~an看成是无序序列,插入排序就是将a1和a0比较,如果比a0小则将其插到a0前面,如果比a0大,则不做事情,这样一次之后前两个位置就是有序的,第三个到第n个仍是无序的。然后,再a2和前两个做比较,将其插入到合适的位置,这样之后,前三个元素都是有序的,第四个到第n个是无序的。如此往复,直到序列有序即可。

void InsertSort(int a[],int len)
{
    int j,temp;
    for (int i=1; i<len; i++)
    {
        if (a[i]<a[i-1]) {
            temp=a[i];
            for (j=i-1; a[j]>temp&&j>=0; j--) {
                a[j+1]=a[j];
            }
            a[j+1]=temp;
        }
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2、 希尔排序 
假设我们有一个序列{a0,a1,a2,a3……an},希尔排序的基本思想是会将序列分割成若干个子序列,然后再对各个子序列进行插入排序。因此希尔排序会有一个增量d,其初始值一般为序列长度的一半,即d=n/2,这样将一个序列分为两个子序列,再分别对两个子序列做插入排序。之后d取值为n/4,再对子序列做插入排序,如此往复,直到d=1,此时序列基本有序,再对其进行一次插入排序即可。

void ShellSort(int a[],int len)
{
    int d;
    for (d=len/2; d>0; d/=2) {//步长
        for (int i=0; i<d; i++) {//直接插入排序
            for (int j=i+d; j<len; j+=d) {
                if (a[j]<a[j-d]) {
                    int temp=a[j];
                    int k=j-d;
                    while (k>=0&&a[k]>temp) {
                        a[k+d]=a[k];
                        k-=d;
                    }
                    a[k+d]=temp;
                }
            }
        }
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

上面的代码是最能体现希尔排序思想的,下面给出两种稍加改进的代码。

void shellsort1(int a[], int n)
{
    int j, d;

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

void shellsort2(int a[], int n)
{
    int i, j, d;

    for (d = n / 2; d > 0; d /= 2)
        for (i = d; i < n; i++)
            for (j = i - d; j >= 0 && a[j] > a[j + d]; j -= d)
                swap(&a[j], &a[j + d]);
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

3、 冒泡排序 
两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。

void swap(int *a,int *b)
{
    int temp;
    temp=*a;
    *a=*b;
    *b=temp;
}
void BubbleSort(int a[],int len)
{
    int i,j;
    for (i=0;i<len;i++)
    {
        for (j=i+1;j<len;j++)
        {
            if (a[i]>a[j])
                swap(&a[i],&a[j]);
        }
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

下面也给出冒泡排序的另外两种实现。

void BubbleSort1(int a[],int len)
{
    for (int i=0;i<len;i++)
    {
        for(int j=len-2;j>=i;j--)
        {
            if (a[j]>a[j+1]) //前者与后者比较,这里与前面算法的区别
                swap(&a[j],&a[j+1]);
        }
    }
}
void BubbleSort2(int a[],int len)
{
    int flag=1;
    for (int i=0;i<len &&flag;i++)
    {
        flag=0;
        for (int j=len-2;j>=i;j--)
        {
            if (a[j]>a[j+1])
            {
                swap(&a[j],&a[j+1]);
                flag=1;
            }
        }
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

4、 快速排序 
假设我们有一个序列{a0,a1,a2,a3……an},在快速排序算法里,我们将第一个元素用作枢轴纪录关键字key,并有一个low指针指向第一个元素,有一个high指针指向最后一个元素,当low < high时,将a[high]和key做比较,如果a[high]>=key,则high–,否则交换low和high位置的元素;当low < high时,将a[low]和key做比较,如果a[low]<=key,则low++,否则交换low和high位置的元素。直到low>=high,一次排序结束,此时key左边的值都比它小,key右边的值都比它大。

int Partiton(int a[],int low,int high)
{
    int key;
    key=a[low];//枢轴的选取可以选取中间值
    while (low<high)
    {
        while (low<high &&a[high]>=key)
            high--;
        swap(&a[high],&a[low]);
        while (low<high && a[low]<=key)
            low++;
        swap(&a[high],&a[low]);
    }
    return low;
}
void Qsort(int a[],int low,int high)
{
    int pivot;
    if (low<high)
    {
        pivot=Partiton1(a,low,high);
        Qsort(a,low,pivot-1);
        Qsort(a,pivot+1,high);
    }

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

下面的代码是对快排的一种优化。

int Partiton1(int a[],int low,int high)/*y优化不需要的交换*/
{
    int temp,key;
    key=a[low];
    temp=key;
    while (low<high)
    {
        while (low<high &&a[high]>=key)
            high--;
        a[low]=a[high];
        while (low<high && a[low]<=key)
            low++;
        a[high]=a[low];
    }
    a[low]=temp;
    return low;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

5、 简单选择排序 
假设我们有一个序列{a0,a1,a2,a3……an},简单选择排序就是将这n个元素遍历一遍选出最小的,再和第一个交换;再从剩下2~n个元素中选出最小的和第二个交换,重复以上步骤即可。

void SelectSort(int a[],int len)
{
    int min;
    for (int i=0;i<len;i++)
    {
        min=i;
        for (int j=i+1;j<len;j++)
        {
            if (a[min]>a[j])
                min=j;
        }
        if (i!=min)
            swap(&a[min],&a[i]);
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

6、 堆排序 
输出堆顶的最小值之后,使剩下n-1个元素的序列重又建成一个堆,则得到n个元素中的次小值。如此反复执行,便能得到一个有序序列。

void HeapAdjust(int a[],int s,int m)
{
    int temp,j=0;
    temp=a[s];
    for (j=2*s+1;j<m;j=j*2+1)
    {
        if (j<m && a[j]<a[j+1])
            ++j;
        if (j<m && temp>=a[j])
            break;
        a[s]=a[j];
        s=j;
    }
    a[s]=temp;
}
void HeapSort(int a[],int len)
{
    int i;
    for (i=(len)/2-1;i>=0;i--)
        HeapAdjust(a,i,len);
    for (i=len-1;i>=1;i--)
    {
        swap(&a[0],&a[i]);
        HeapAdjust(a,0,i-1);
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

下面给出上述各种算法的时间复杂度、空间复杂度、稳定性等。 
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值