关于排序算法的一些实现

220 阅读4分钟

排序算法,基本是面试必考的点,个人总结了一些排序算法的实现

1. 冒泡排序

  • 从起始点开始,比较相邻两个成员的大小,满足条件的,就交换位置,直到全部遍历完
    void TestSort::BubbleSort(int* arr, int size)
    {
        if (arr == nullptr) {
            return;
        }
    
        for (int i = 0; i < size; ++i) {
            for (int j = 0; j < size - 1 - i; ++j) {
                if (arr[j] > arr[j + 1]) {
                    int tmp = arr[j + 1];
                    arr[j + 1] = arr[j];
                    arr[j] = tmp;
                }
            }
        }
    }
    

2. 选择排序

  • 第一次,从 a[1] ~ a[n-1] 找出最小值,然后跟 a[0] 比较,如果小于 a[0],则交换
  • 第二次,从 a[2] ~ a[n-1] 找出最小值,然后跟 a[1] 比较,如果小于 a[1],则交换
  • ...
  • 第n-1次,比较 a[n-2] a[n-1],如果后者小于前者,则交换
    void TestSort::SelectSort(int* arr, int size)
    {
        if (arr == nullptr) {
            return;
        }
    
        for (int i = 0; i < size - 1; ++i) {
            int min = i;
            for (int j = i + 1; j < size; ++j) {
                if (arr[j] < arr[i]) {
                    min = j;
                }
            }
    
            if (i != min) {
                int tmp = arr[i];
                arr[i] = arr[min];
                arr[min] = tmp;
            }
        }
    }
    

3. 插入排序

  • 主要的思路就是不断构造一个有序的序列
  • a[0] 开始,然后将 a[1] a[0] 比较,如果小于,则交换
  • 此时 a[0], a[1] 为有序序列
  • 接着就是 a[2]a[0], a[1] 中找合适位置
  • 直到 a[n-1]a[0] ... a[n-2] 中找合适位置
    void TestSort::InsertSort(int* arr, int size)
    {
        if (arr == nullptr || size <= 1) {
            return;
        }
    
        for (int i = 1; i < size; ++i) {
            int j = i;
            int tmp = arr[i];
            while (j >= 1 && arr[j - 1] >= tmp) {
                arr[j] = arr[j - 1];
                --j;
            }
    
            if (i != j) {
                arr[j] = tmp;
            }
        }
    }
    

4. 归并排序

  • 将数组平均分为左右两部分,再递归分下去,直到成员为1个为止
  • 然后开始合并左右两个数组,使其成为一个有序的序列
  • 然后回到上一层,将左右两个有序的序列再次合并,使其成为有序的序列,直到回到最上层
    void TestSort::Merge(int* arr, int low, int mid, int high)
    {
        int old = low;
        const int size = high - low + 1;
        int* tmp = new int[size];
        memset(tmp, 0, sizeof(int) * size);
    
        // 合并左右两个数组
        int mrg = mid + 1;
        int index = 0;
        while (low <= mid && mrg <= high) {
            if (arr[low] < arr[mrg]) {
                tmp[index++] = arr[low++];
            } else {
                tmp[index++] = arr[mrg++];
            }
        }
    
        // 左边剩下的
        while (low <= mid) {
            tmp[index++] = arr[low++];
        }
    
        // 右边剩下的
        while (mrg <= high) {
            tmp[index++] = arr[mrg++];
        }
    
        // 赋值
        memcpy(&arr[old], tmp, sizeof(int) * size);
        delete tmp;
    }
    
    void TestSort::MergeSort(int* arr, int low, int high)
    {
        if (arr == nullptr || low >= high) {
            return;
        }
    
        int mid = (low + high) / 2;
        MergeSort(arr, low, mid);
        MergeSort(arr, mid + 1, high);
        Merge(arr, low, mid, high);
    }
    

5. 堆排序

  • 主要就是利用二叉堆堆顶最大或最小的特性来排序
  • 先将数组构建为二叉堆,此时堆顶为最大值,将其与 a[n-1] 交换,此时 a[n-1] 是有序的
  • 因为交换后,堆顶就不是最大值了,所以需要调整二叉堆,将剩下的成员里最大的移到堆顶,然后和 a[n-2] 交换,此时 a[n-2] a[n-1] 是有序的
  • 重复操作,直到最后一个
  • 其实,所谓的创建二叉堆和调整二叉堆,都是移动成员,使堆顶成为最大值或最小值,操作的方法是一样的,就是比较父节点和子节点,然后将最大值放到父节点,然后递归到最底层
    void TestSort::HeapMax(int* arr, int size, int index)
    {
        int left = (2 * index) + 1;
        int right = left + 1;
        int max = index;
        
        // 比较左子节点
        if (left < size && arr[left] > arr[index]) {
            max = left;
        }
    
        // 比较右子节点
        if (right < size && arr[right] > arr[max]) {
            max = right;
        }
    
        // 交换
        if (index != max) {
            Swap(arr[index], arr[max]);
            HeapMax(arr, size, max);
        }
    }
    
    void TestSort::HeapSort(int *arr, int size)
    {
        if (arr == nullptr || size <= 0) {
            return;
        }
    
        // 创建一个大根堆
        int indexLast = size - 1;
        int indexStart = (indexLast - 1) / 2;
        for (int i = indexStart; i >= 0; --i) {
            HeapMax(arr, size, i);
        }
    
        // 排序大根堆
        for (int i = size - 1; i >= 0; --i) {
            Swap(arr[0], arr[i]);
            HeapMax(arr, i, 0);
        }
    }
    

6. 快速排序

  • a[0] 来做分割,将数组分成左 a[0] ~ a[mid] 和右 a[mid+1] ~ a[n-1]
  • 然后递归对左右两边进行上面的操作,直到数组被分割到为1个成员
  • 然后写代码的思路是
    • 假如数组的第一位的索引为 low,数组的最后以为的索引为 high
    • a[0] 来做比较值,设 tmp = a[0]
    • 然后 high 开始,在 low < high 的条件下, 如果 a[high] 大于等于 tmp,则 --high,否则,a[low] = a[high]
    • 然后 low 开始,在 low < high 的条件下, 如果 a[low] 小于等于 tmp,则 ++low,否则,a[high] = a[low]
    • 直到 low == higha[low] = tmp,此时数组就被分成了左右两个,左边的 a[low] ~ a[mid] 都是小于等于 tmp
    • 右边的 a[mid+1] ~ a[high] 都是大于 tmp
    • 再对这两个数组执行上述的操作,直到数组被分割到只剩下1个成员为止
    int TestSort::GetIndex(int* arr, int low, int high)
    {
        if (arr == nullptr) {
            return -1;
        }
    
        int tmp = arr[low];
        while (low < high) {
            while (low < high && arr[high] >= tmp) {
                --high;
            }
    
            if (low >= high) {
                break;
            } else {
                arr[low] = arr[high];
            }
    
            while (low < high && arr[low] <= tmp) {
                ++low;
            }
    
            if (low >= high) {
                break;
            } else {
                arr[high] = arr[low];
            }
        }
    
        arr[low] = tmp;
        return low;
    }
    
    void TestSort::QuickSort(int* arr, int low, int high)
    {
        if (low >= high) {
            return;
        }
    
        int index = GetIndex(arr, low, high);
        if (index == -1) {
            return;
        }
    
        QuickSort(arr, low, index - 1);
        QuickSort(arr, index + 1, high);
    }