面试常用算法之排序

1:冒泡排序(bubbleSort)
冒泡排序是每次将乱序中的最大的数字通过两两交换的方式往后移动,直到序列有序为止。犹如水中的气泡从下往上浮时,越来越大。该算法共执行了n趟,每趟执行n-i次比较,所以其复杂度为O(n^2)。

基本的冒泡排序算法程序如下所示:

//冒泡排序
void bubbleSort(int a[],int n)
{
    int i,j;
    for(i=0;i<n;++i)//共执行n趟
        for(j=1;j<n-i;++j)//每趟执行n-i次比较,选出一个最大值
        {
            if(a[j]<a[j-1])//通过两两比较使得大的数据“上浮”
                swap(a[j],a[j-1]);
        }
}
2.快速排序法

算法思想:通过一趟排序将待排的记录分割成独立的两个部分,其中一部分记录的关键字不大于(或不小于)另一部分记录的任意关键字,然后再分别对着两部分记录继续进行排序,以达到整个序列有序:

//快排
void quickSort(int a[],int first,int last)
{
    int i=first,j=last;
    if(i>j)
        return;
    while(i<j)
    {       
        while(i<j&&a[j]>=a[first])//从后往前找小于基准数的位置
            j--;
        while(i<j&&a[i]<=a[first])//从前往后找大于基准数的位置
            i++;
        if(i<j)//注意,i,j不能相遇或交叉
            swap(a[i],a[j]);
    }
    swap(a[first],a[j]);
    quickSort(a,first,j-1);
    quickSort(a,j+1,last);
}
3. 直接选择排序(selectSort)
选择排序简单的说就是每次找到序列中的最小值,然后将该值放在有序序列的最后一个位置,以形成一个更大的有序序列。选择排序进行n趟,每趟从i+1开始,每趟找到最小值下标min_index,再将a[min_index]与a[i]交换。

选择排序程序如下所示:

//直接选择排序
void selectSort(int a[],int n)
{
    int i,j,min_index;
    for(i=0;i<n;++i)//找到未排序数组中最小的那个元素,放在已排序部分的后面
    {
        min_index=i;
        for(j=i+1;j<n;++j)
            if(a[j]<a[min_index])
                min_index=j;
        swap(a[min_index],a[i]);
    }
}

4.堆排序

堆排序是指利用堆这种数据结构所设计的一种选择排序算法。堆是一种近似完全二叉树的结构(通常堆是通过一维数组来实现的),并满足性质:以最大堆(也叫大根堆、大顶堆)为例,其中父结点的值总是大于它的孩子节点。

  我们可以很容易的定义堆排序的过程:

  1. 由输入的无序数组构造一个最大堆,作为初始的无序区
  2. 把堆顶元素(最大值)和堆尾元素互换
  3. 把堆(无序区)的尺寸缩小1,并调用heapify(A, 0)从新的堆顶元素开始进行堆调整
  4. 重复步骤2,直到堆的尺寸为1

  堆排序的代码如下:


#include <stdio.h>

// 分类 -------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- O(nlogn)
// 最优时间复杂度 ---- O(nlogn)
// 平均时间复杂度 ---- O(nlogn)
// 所需辅助空间 ------ O(1)
// 稳定性 ------------ 不稳定


void Swap(int A[], int i, int j)
{
    int temp = A[i];
    A[i] = A[j];
    A[j] = temp;
}

void Heapify(int A[], int i, int size)  // 从A[i]向下进行堆调整
{
    int left_child = 2 * i + 1;         // 左孩子索引
    int right_child = 2 * i + 2;        // 右孩子索引
    int max = i;                        // 选出当前结点与其左右孩子三者之中的最大值
    if (left_child < size && A[left_child] > A[max])
        max = left_child;
    if (right_child < size && A[right_child] > A[max])
        max = right_child;
    if (max != i)
    {
        Swap(A, i, max);                // 把当前结点和它的最大(直接)子节点进行交换
        Heapify(A, max, size);          // 递归调用,继续从当前结点向下进行堆调整
    }
}

int BuildHeap(int A[], int n)           // 建堆,时间复杂度O(n)
{
    int heap_size = n;
    for (int i = heap_size / 2 - 1; i >= 0; i--) // 从每一个非叶结点开始向下进行堆调整
        Heapify(A, i, heap_size);
    return heap_size;
}

void HeapSort(int A[], int n)
{
    int heap_size = BuildHeap(A, n);    // 建立一个最大堆
    while (heap_size > 1)           // 堆(无序区)元素个数大于1,未完成排序
    {
        // 将堆顶元素与堆的最后一个元素互换,并从堆中去掉最后一个元素
        // 此处交换操作很有可能把后面元素的稳定性打乱,所以堆排序是不稳定的排序算法
        Swap(A, 0, --heap_size);
        Heapify(A, 0, heap_size);     // 从新的堆顶元素开始向下进行堆调整,时间复杂度O(logn)
    }
}

int main()
{
    int A[] = { 5, 2, 9, 4, 7, 6, 1, 3, 8 };// 从小到大堆排序
    int n = sizeof(A) / sizeof(int);
    HeapSort(A, n);
    printf("堆排序结果:");
    for (int i = 0; i < n; i++)
    {
        printf("%d ", A[i]);
    }
    printf("\n");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_32418175/article/details/80713115
今日推荐