八 排序

8.1 插入排序

8.1.1 直接插入排序

void InsertSort() {
    for(int i = 2; i <= n; i++) {
        if(A[i] < A[i - 1]) {
            A[0] = A[i];
            int j = i - 1;
            for(; A[0] < A[j]; j--)
                A[j + 1] = A[j];
            A[j + 1] = A[0];
        }
    }
}

8.1.2 二分插入排序

因为前面是有序的所以可以用二分查找找到要插入的位置

void InsertSort2() { 
    for(int i = 2; i <= n; i++) {
        A[0] = A[i];
        int low = 1;
        int high = i - 1;
        while(low <= high) {
            int mid = (low + high) / 2;
            if(A[mid] > A[0])
                high = mid - 1;
            else
                low = mid + 1;
        }
        for(int j = i - 1; j >= high + 1; j--)
            A[j + 1] = A[j];
        A[high + 1] = A[0];
    }
}

8.1.3 希尔排序(缩小增量排序)

先追求部分有序,再逐渐逼近全局有序
利用插入排序“越有序效率越高”的特点。
空间复杂度 O(1),平均时间复杂度O(n^1.3),最坏O(n ^2);不稳定

void ShellSort() {
    for(int d = n / 2; d >= 1; d /= 2) { //增量变化
        for(int i = d + 1; i <= n; i++) { //对所以组采用直接插入排序
            
            if(A[i] < A[i - d]) { //要注意每组中相邻元素距离为d,其余和直接插入排序一样
                A[0] = A[i];
                int j = i - d;
                for(; j > 0 && A[0] < A[j]; j -= d)
                    A[j + d] = A[j];
                A[j + d] = A[0];
            }
            
        }
    }
}

8.2 交换排序

8.2.1冒泡排序

void BubbleSort() {
    for(int i = 1; i <= n; i++) {
        bool flag = false;
        for(int j = n; j > i; j--) {
            if(A[j - 1] > A[j]) {
                swap(A[j - 1], A[j]);
                flag = true;
            }
        }
        if(!flag) //剪枝
            return;
    }
}

8.2.2 快速排序

分治思想,每次选一个尽量为中值的元素为枢轴,将比枢轴大的放它右边,小的放左边(方法是用2个指针分别从两端交替扫描),这样就分解成了2个性质相同且独立的子问题,分而治之。
所以可以看出来每趟排序之后会将枢轴元素放到最终的位置,且不稳定
最好时间复杂度 O(nlogn),最坏时间复杂度O(n^2),平均时间复杂度O(nlogn)
最坏空间复杂度O(n)【栈】,空间复杂度O(logn)。

int Partition(int low, int high) {
    int pivot = A[low]; //选择一个元素为枢轴,一般就是第一个元素
    while(low < high) { //从两端交替向中间扫描,low==high终止
        while(low < high && A[high] >= pivot)
            --high; //从右往左找到第一个小于pivot
        A[low] = A[high];//pivot放到左边,保证可以分为2个独立的子问题
        while(low < high && A[low] <= pivot)
            ++low;
        A[high] = A[low];
    }
    A[low] = pivot;
    return low;
}
void QuickSort(int low, int high) {
    if(low < high) {
        int pivotpos = Partition(low, high); //分治,将问题分成2个相同的且独立的子问题;
        QuickSort(low, pivotpos - 1);
        QuickSort(pivotpos + 1, high);
    }
}

8.3 选择排序

8.3.1 简单选择排序

void SelectSort() {
    for(int i = 1; i < n; i++) {
        int Min = i;
        for(int j = i + 1; j <= n; j++) {
            if(A[j] < A[Min])
                Min = j;
        }
        swap(A[i], A[Min]);
    }
}

8.3.2 堆排序

将数组看成一颗完全二叉树,利用堆的性质不断建堆,每趟排序建立一个堆,然后将根节点(也就是最大值)和堆的最后一个节点交换(之后那个最大值就永远固定了,看成离开了堆),这个时候堆就被破坏,继续调整成为新堆(下坠),如此循环。
其实就是利用堆来“选择排序”,每次选择最大值放到最后。
时间复杂度 O(nlogn),其中初始建堆O(n),然后n-1趟建堆排序,每趟O(logn)
空间复杂度为O(1),不稳定。


//调整以元素k为根的子树为堆
void HeadAdjust(int k, int len) {
    A[0] = A[k]; //暂存根节点
    for(int i = 2 * k; i <= len; i *= 2) { //沿着较大的儿子下坠
        if(i < len && A[i] < A[i + 1])
            i++; //A[i]为较大的儿子
        if(A[0] >= A[i]) //这个时候满足堆的性质了
            break;
        else { //否则
            A[k] = A[i]; //下坠,将A[i]这个大的儿子往上移
            k = i; //修改k值,继续
        }
    }
    A[k] = A[0]; //放入最终位置
}
//建初始堆
void BuildMaxHeap(int len) {
    for(int i = len / 2; i > 0; i--) //将所有的非叶子节点按照顺序调整
        HeadAdjust(i, len);
}

void HeapSort(int len) {
    BuildMaxHeap(len); //初始建大根堆 O(n)的时间复杂度
    for(int i = len; i > 1; i--) { //n-1趟交换和下坠,每次O(logn),所以一共O(nlogn)
        swap(A[i], A[1]); //这个时候的A[1]就是堆的最大元素,就是选择排序里面“选择”的那个数字,将他放到最后固定
        HeadAdjust(1, i - 1); //大根堆被破坏,将根节点下坠调整成新的大根堆
    }
}

8.4 归并排序

分治思想
空间复杂度:O(n)
时间复杂度:每趟都是O(n),一共是logn趟,所以为O(nlogn),且最好最坏都是O(nlogn)
二路归并算法是稳定的。

int B[maxn];
void Merge(int low, int mid, int high) {
    for(int k = low; k <= high; k++)
        B[k] = A[k];
    int i = low, j = mid + 1, k = low;
    while(i <= mid && j <= high) {
        if(B[i] <= B[j])
            A[k++] = B[i++];
        else
            A[k++] = B[j++];
    }
    while(i <= mid)
        A[k++] = B[i++];
    while(j <= high)
        A[k++] = B[j++];
}

void MergeSort(int low, int high) {
    if(low < high) {
        int mid = (low + high) / 2;
        MergeSort(low, mid);
        MergeSort(mid + 1, high);
        Merge(low, mid, high);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值