排序算法(插入、选择、冒泡;希尔、归并、快排),带精心整理的代码

Table of Contents

简单排序

插入排序

选择排序(不稳定)

冒泡排序

复杂排序

希尔排序(不稳定)

归并排序

快速排序(不稳定)


简单排序

插入排序

  • 序列分为已排序和未排序两部分。
  • 取一个未排序元素x,将已排序的元素中比x大的元素后移,然后将 x 插入空位。
  • 其外层循环遍历的是未排序部分,其内层循环遍历的是已排序部分。
  • 数据越有序,效率越高。
void InsertionSort(int *seq, int N) {
    for (int i = 1; i < N; i++) {
        int v = seq[i];
        int j = i-1;
        //右移已排序序列寻找插入位置
        while (seq[j] > v && j >= 0) {
            seq[j+1] = seq[j];
            j--;
        }
        seq[j+1] = v;
    }
}

 

选择排序(不稳定)

  • 序列分为已排序(且是最终的排序)和未排序两部分。
  • 选择(导致其效率与数据无关)出未排序部分的最小值,与未排序部分的第一个元素交换(导致其是不稳定的,如5,6,5,2)。
  • 其内层循环遍历的是未排序部分
  • 元素交换次数少,一轮比较只需要换一次位置。这也是人工排序大多使用这种方法的原因。
void SelectionSort(int *seq, int n){//选择排序
    for (int i = 0; i < n; i++) {
        //寻找未排序部分的最小值
        int minj = i;
        for (int j = i + 1; j < n; j++) {
            if (seq[j] < seq[minj]) {
                minj = j;
            }
        }
        //交换
        swap(seq[i], seq[minj]);
    }
}

冒泡排序

冒泡排序

  • 序列分为已排序(且是最终排序)和未排序两部分。
  • 每次从序列末尾开始更小的元素的一路到未排序部分的第一个元素
  • 其内层循环遍历的是未排序部分。
  • 这可以说是改进的选择排序,边比较边交换,而不是光看。到最后才进行一次破坏稳定性的远距离交换。
  • 一个序列的逆序数是固定的,逆序数可以体现序列的错乱程度。每一次交换临近的元素,就消除该序列的一个逆序数对。
  • 如果一路冒下去,一个交换的都没有,那么说明数组逆序数低到0,即已排序。此时可以中断。这种中断会带来很大的效率提升。
void BubbleSort(int *seq, int N) {
    bool flag = false;//已排序标志
    for (int i = 0; !flag; i++) {
        flag = true;
        for (int j = N - 1; j >= i + 1; j--) {
            if (seq[j] < seq[j - 1]) {
                swap(seq[j], seq[j - 1]);
                flag = false;
            }
        }
    }
}

 

 

复杂排序

希尔排序(不稳定)

  • 就是按照不同步长g对元素进行插入排序
  • 最后g = 1,也就是简单插入排序,可以绝对保证序列顺序正确。
  • g的生成规则是 g = 3*g + 1 
  • 当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小,因为插入排序对于有序的序列效率很高,速度也很快。
  • 其实不稳定的。因此,在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱。

 

void InsertionSort(int *seq, int N, int G) {
    for (int i = G; i < N; i++) { //i++保证了排序的完整性
        int v = seq[i];
        int j = i - G;
        //右移已排序序列寻找插入位置
        while (j >= 0 && seq[j] > v) {
            seq[j + G] = seq[j];
            j -= G;
        }
        seq[j + G] = v;
    }
}

void ShellSort(int *seq, int N) {
    //生成序列G
    vector<int> G;
    int g = 1;
    while (g <= N) {
        G.push_back(g);
        g = 3 * g + 1;
    }
    //循环排序
    for (int i = G.size() - 1; i >= 0; i--) {
        InsertionSort(seq, N, G[i]);
    }
}

归并排序

  • 将大序列分割成两个小序列,分别对这两个小序列执行归并排序(递归)。然后merge两个小序列成一个已排序的大序列(合并)。
  • 在合并两个序列时,只要保证前者优先于后者,那么相同元素的顺序就不会颠倒。使用归并排序是稳定的。
  • merge的思想就是双队列循环取出更小的队首
  • 排序是在递归的回溯阶段完成的
void Merge(int *seq, int start, int mid, int end, int *temp) {
    int i = start;
    int j = mid + 1;
    int k = 0;
    //双队列合并
    while (i <= mid && j <= end) {
        if (seq[i] <= seq[j]) {  //体现了第一个子序列优先合并,保证了稳定性
            temp[k++] = seq[i++];
        } else {
            temp[k++] = seq[j++];
        }
    }
    while (i <= mid) {
        temp[k++] = seq[i++];
    }
    while (j <= end) {
        temp[k++] = seq[j++];
    }
    // 同步回原序列
    for (i = 0; i < k; i++) {
        seq[start + i] = temp[i];
    }
}

void MergeSort(int *seq, int start, int end, int *temp) {
    //二分递归
    if (start < end) {
        int mid = (start + end) / 2;
        MergeSort(seq, start, mid, temp);
        MergeSort(seq, mid + 1, end, temp);
        Merge(seq, start, mid, end, temp);
    }
}

void MergeSort(int *seq, int N) {
    int *temp = new int[N]; //缓存副本
    MergeSort(seq, 0, N - 1, temp);
    delete[] temp;
}

 

快速排序(不稳定)

  • 前一部分小于轴,后一部分大于轴。分割递归下去就排好了。
  • Partition函数是核心,其选则一个数做为轴,最终使得轴左边的元素都小于轴,右边的元素都大于轴。
  • Partiiton函数实现:选择最后一个元素作为轴,从头遍历序列,如果是大于轴的不用管,如果是小于等于轴的那么与大于轴的部分的第一个值做交换。最后,轴再和大于轴部分的第一个值做交换。
  • 排序是在递归的回溯阶段完成的。
  • 因为会盲目的交换非相邻元素,因此是不稳定的。
  • 轴选择的不好的话,比如选中了最大值或最小值,那么一次分割只能分出1个,那么算法时间复杂度会上升到O(n^2),并且递归深度过大导致栈溢出 。但我们希望,一次分割尽可能能够一半一半分。

 

int Partition(int *seq, int start, int end) {//划分
    int i = start - 1;
    for (int j = start; j <= end - 1; j++) {
        if (seq[j] <= seq[end]) {
            i++;
            swap(seq[j], seq[i]);
        }
    }
    swap(seq[i + 1], seq[end]);
    return i + 1;
}

void QuickSort(int *seq, int left, int right) 
{
    if (left < right) {
        int p = Partition(seq, left, right);
        QuickSort(seq, left, p - 1);
        QuickSort(seq, p + 1, right);
    }
}

void QuickSort(int *seq, int N) {
    QuickSort(seq, 0, N - 1);
}

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值