Day2:排序算法(下)

三、选择排序

1. 简单选择排序

在遍历序列时,找到其中最小的值,其最终位置应该在序列的最前面(升序)。
据此,可以在每一趟中选取当前趟的子序列中最小的元素,与子序列的首元素交换。子序列之前的序列经前几趟的选择,已经有序。当剩余子序列为空时,排序完成。(其实为1时就可以结束)

 void SelectSort(int nums[], int n)
 {
     for(int i = 0;i < n - 1;i++) {         // 每趟选择
        int min = i;                        // 记录最小下标
        for(int j = i + 1;j < n;j++){       // 遍历子序列
            if(nums[j] < nums[min]){        // 更新最小下标
                min = j;
            }
        }
        Swap(&nums[min], &nums[i]);
     }
 }

时间复杂度:O( n2)
空间复杂度:O(1)
稳定性:不稳定

2. 堆排序

堆是一种特殊的数据结构。可以看作一棵由数组表示的完全二叉树。该完全二叉树有以下特点:

  1. 根节点的值大于(或小于)其余所有节点的值(分为大根堆和小根堆)
  2. 左右子树也是堆

根据堆的性质,可以快速选择出一个最大或最小值,移动至序列的正确位置。

// 注意:堆排序的序列与完全二叉树对应,因此必须将序列变为下标从1开始
 void HeapSort(int nums[], int n)
 {
     int heap[n + 1];    // 数组化为堆数组
     for(int i = 0;i < n;i++) {
        heap[i + 1] = nums[i];
     }
     BuildMaxHeap(heap, n);             // 建堆
     for(int i = n;i > 1;i--) {         // 选择堆顶元素进行排序
        Swap(&heap[i], &heap[1]);       // 将堆顶与堆尾进行对换(最大元素移到最终位置)
        HeapAdjust(heap, 1, i - 1);     // 堆调整,使重新成堆
     }
     for(int i = 0;i < n;i++) {
        nums[i] = heap[i + 1];          // 堆数组写回数组
     }
 }

 void BuildMaxHeap(int heap[], int n)   // 建大根堆
 {
    for(int i = n / 2;i >0;i--) {       // 对所有非叶子结点做一次调整,可形成堆
        HeapAdjust(heap, i, n);
    }
 }

 void HeapAdjust(int heap[], int k, int n)         // 堆调整
 {
    heap[0] = heap[k];          // 调整元素暂存至0位置(留空)
    for(int i = 2 * k;i <= n;i *= 2) {      // 在子树中筛选
        if(i < n && heap[i] < heap[i + 1]) {    // 选中较大的子结点
            i++;
        }
        if(heap[i] <= heap[0]) {
            break;              // 调整时,子树认为已经是堆,若0位置比子树的根大,则已经是最大,无需筛选
        }
        else {                  // 若查到更大的元素,则交换,并继续向下筛选(0位置的交换可以等到筛选完成后填入)
            heap[k] = heap[i];
            k = i;
        }
    }
    heap[k] = heap[0];      // k记录最后一个交换的位置,其值为被交换下来的原k位置,暂存0位置的值
 }

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

四、归并排序

1. 二路归并排序

已知两个有序序列,可以在线性时间O(n)下完成这两个序列的合并。
利用分治思想,将序列划分为两个子序列,子序列先有序化后再进行原序列的有序化,子序列可再划分,由此递归可以完成排序。

// copyNums为辅助数组空间
 void BiMergeSort(int nums[], int n)
 {
     int copyNums[n];
     BiMergeSortRec(nums, 0, n - 1, copyNums);
 }

 void BiMergeSortRec(int nums[], int low, int high, int copyNums[])     // 归并递归函数
 {
     if(low < high) {
        int mid = (low + high) / 2;
        BiMergeSortRec(nums, low, mid, copyNums);
        BiMergeSortRec(nums, mid + 1, high, copyNums);
        BiMerge(nums, low, mid, high, copyNums);
     }
 }

 void BiMerge(int nums[], int low, int mid, int high, int copyNums[])	// 单次归并
 {
     int i, j, k;
     for(k = low;k <= high;k++) {
        copyNums[k] = nums[k];      // 将需要归并的整个区间拷贝到辅助数组
     }
     for(i = low, j = mid + 1, k = low;i <= mid && j <= high;k++) {
        // 设有三个指针,i遍历左侧区间序列,j遍历右侧,k遍历原数组(按序填入)
        if(copyNums[i] <= copyNums[j]) {    // =条件保证稳定性
            nums[k] = copyNums[i++];
        }
        else {
            nums[k] = copyNums[j++];
        }
     }
     // 以下将未遍历完的序列填入,两个while只会执行一个
     while(i <= mid) {
        nums[k++] = copyNums[i++];
     }
     while(j <= high) {
        nums[k++] = copyNums[j++];
     }
 }

时间复杂度:O(n·log n)
空间复杂度:O(n)
稳定性:稳定

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值