四大排序(C语言学习)

排序的概念

排序是将一组无序的数据按照某种规则(如升序或降序)重新排列的过程。排序算法是计算机科学中的基础算法,直接影响数据处理效率。根据时间复杂度、空间复杂度、稳定性等特性,排序算法可分为多种类型。

四大常见排序分类

  1. 插入排序:直接插入排序、希尔排序
  2. 选择排序:直接选择排序、堆排序
  3. 交换排序:冒泡排序、快速排序
  4. 归并排序

插入排序

直接插入排序

思想:将待排序元素插入到已排序序列的适当位置,逐步构建有序序列。

C代码实现

void InsertSort(int* arr, int n) {  
    for (int i = 1; i < n; i++) {  
        int key = arr[i]; // 当前待插入元素  
        int j = i - 1;    // 从已排序序列末尾开始比较  
        while (j >= 0 && arr[j] > key) {  
            arr[j + 1] = arr[j]; // 元素后移  
            j--;  
        }  
        arr[j + 1] = key; // 插入到正确位置  
    }  
}  

特性

  • 时间复杂度:O(n²)(最坏情况)
  • 空间复杂度:O(1)
  • 稳定性:稳定
希尔排序

思想:通过分组插入排序逐步缩小增量(预处理),最终完成整体排序(直接插入排序)。

C代码实现

void ShellSort(int* arr, int n) {  
    int gap = n / 2; // 初始增量  
    while (gap > 0) {  
        for (int i = gap; i < n; i++) {  
            int key = arr[i];  
            int j = i - gap;  
            while (j >= 0 && arr[j] > key) {  
                arr[j + gap] = arr[j];  
                j -= gap;  
            }  
            arr[j + gap] = key;  
        }  
        gap /= 2; // 缩小增量  
    }  
}  

预处理

思想

先选较大步长,将数组分成多个子序列,对各子序列分别进行插入排序;之后逐步缩小步长,重复此过程,直到步长为 1,对整个数组进行最后一次插入排序,使数组有序。

步骤

特性

  • 时间复杂度:O(n¹·³)(平均)
  • 空间复杂度:O(1)
  • 稳定性:不稳定

选择排序

直接选择排序

思想:每次从未排序序列中选择最小(或最大)元素,放到已排序序列末尾。

C代码实现

void SelectSort(int* arr, int n) {  
    for (int i = 0; i < n - 1; i++) {  
        int min_idx = i;  
        for (int j = i + 1; j < n; j++) {  
            if (arr[j] < arr[min_idx])  
                min_idx = j; // 更新最小值下标  
        }  
        if (min_idx != i) {  
            int tmp = arr[i];  
            arr[i] = arr[min_idx];  
            arr[min_idx] = tmp;  
        }  
    }  
}  

特性

  • 时间复杂度:O(n²)
  • 空间复杂度:O(1)
  • 稳定性:不稳定
堆排序

思想:利用堆数据结构(完全二叉树)选择最大/最小元素,通过反复调整堆完成排序。

C代码实现

void AdjustHeap(int* arr, int parent, int n) {  
    int child = 2 * parent + 1; // 左孩子  
    while (child < n) {  
        if (child + 1 < n && arr[child] < arr[child + 1])  
            child++; // 选择较大的孩子  
        if (arr[parent] < arr[child]) {  
            swap(&arr[parent], &arr[child]);  
            parent = child;  
            child = 2 * parent + 1;  
        } else break;  
    }  
}  

void HeapSort(int* arr, int n) {  
    // 建堆(从最后一个非叶子节点开始调整)  
    for (int i = n / 2 - 1; i >= 0; i--)  
        AdjustHeap(arr, i, n);  
    // 排序  
    for (int i = n - 1; i > 0; i--) {  
        swap(&arr[0], &arr[i]); // 交换堆顶与末尾元素  
        AdjustHeap(arr, 0, i);  // 重新调整堆  
    }  
}  

特性

  • 时间复杂度:O(n log n)
  • 空间复杂度:O(1)
  • 稳定性:不稳定

交换排序

冒泡排序

思想:通过相邻元素比较和交换,将最大(或最小)元素逐步“冒泡”到序列末尾。

C代码实现

void BubbleSort(int* arr, int n) {  
    for (int i = 0; i < n - 1; i++) {  
        int flag = 0; // 优化:记录是否发生交换  
        for (int j = 0; j < n - 1 - i; j++) {  
            if (arr[j] > arr[j + 1]) {  
                swap(&arr[j], &arr[j + 1]);  
                flag = 1;  
            }  
        }  
        if (!flag) break; // 无交换则提前结束  
    }  
}  

特性

  • 时间复杂度:O(n²)(优化后最好情况O(n))
  • 空间复杂度:O(1)
  • 稳定性:稳定
快速排序
Hoare版本

思想:选定基准值(通常为首元素),通过双指针从两端向中间扫描并交换,最终将序列分为两部分递归排序。

C代码实现

int PartitionHoare(int* arr, int left, int right) {  
    int pivot = arr[left];  
    int i = left, j = right;  
    while (i < j) {  
        while (i < j && arr[j] >= pivot) j--;  
        while (i < j && arr[i] <= pivot) i++;  
        if (i < j) swap(&arr[i], &arr[j]);  
    }  
    swap(&arr[left], &arr[i]);  
    return i;  
}  

void QuickSortHoare(int* arr, int left, int right) {  
    if (left >= right) return;  
    int pos = PartitionHoare(arr, left, right);  
    QuickSortHoare(arr, left, pos - 1);  
    QuickSortHoare(arr, pos + 1, right);  
}  

挖坑法

思想:将基准值视为“坑”,通过填坑和挖坑完成分区。

C代码实现

int PartitionHole(int* arr, int left, int right) {  
    int pivot = arr[left];  
    while (left < right) {  
        while (left < right && arr[right] >= pivot) right--;  
        arr[left] = arr[right]; // 右端填左坑  
        while (left < right && arr[left] <= pivot) left++;  
        arr[right] = arr[left]; // 左端填右坑  
    }  
    arr[left] = pivot; // 基准值填入最终坑位  
    return left;  
}  

Lomuto前后指针法

思想:使用单指针遍历数组,将小于基准值的元素交换到前部。

C代码实现

int PartitionLomuto(int* arr, int left, int right) {  
    int pivot = arr[right];  
    int i = left - 1;  
    for (int j = left; j < right; j++) {  
        if (arr[j] < pivot) {  
            i++;  
            swap(&arr[i], &arr[j]);  
        }  
    }  
    swap(&arr[i + 1], &arr[right]);  
    return i + 1;  
}  

非递归版本

思想:利用栈模拟递归过程,避免递归调用栈溢出。

C代码实现

#include <stack>  
void QuickSortNonRecursive(int* arr, int left, int right) {  
    std::stack<int> st;  
    st.push(left);  
    st.push(right);  
    while (!st.empty()) {  
        int r = st.top(); st.pop();  
        int l = st.top(); st.pop();  
        int pos = PartitionHoare(arr, l, r);  
        if (pos - 1 > l) {  
            st.push(l);  
            st.push(pos - 1);  
        }  
        if (pos + 1 < r) {  
            st.push(pos + 1);  
            st.push(r);  
        }  
    }  
}  

特性

  • 时间复杂度:O(n log n)(平均),O(n²)(最坏)
  • 空间复杂度:O(log n)(递归栈)
  • 稳定性:不稳定

归并排序

思想:分治法,将序列递归拆分为子序列,合并时按顺序拼接。

C代码实现

void Merge(int* arr, int left, int mid, int right) {  
    int* tmp = (int*)malloc((right - left + 1) * sizeof(int));  
    int i = left, j = mid + 1, k = 0;  
    while (i <= mid && j <= right) {  
        if (arr[i] <= arr[j]) tmp[k++] = arr[i++];  
        else tmp[k++] = arr[j++];  
    }  
    while (i <= mid) tmp[k++] = arr[i++];  
    while (j <= right) tmp[k++] = arr[j++];  
    for (int p = 0; p < k; p++) arr[left + p] = tmp[p];  
    free(tmp);  
}  

void MergeSort(int* arr, int left, int right) {  
    if (left >= right) return;  
    int mid = (left + right) / 2;  
    MergeSort(arr, left, mid);  
    MergeSort(arr, mid + 1, right);  
    Merge(arr, left, mid, right);  
}  

特性

  • 时间复杂度:O(n log n)
  • 空间复杂度:O(n)(临时数组)
  • 稳定性:稳定
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值