数据结构与算法 —— O(nlogn)排序算法(快排和归并)(c++)


常见的三种高级排序:快排,归并,堆排序都是Onlogn的复杂度。

快速排序

思想:

  • 分治:DFS前序遍历,把一个序列分为较小和较大的2个子序列,然后递归地排序两个子序列。
  • 挖坑法
    1.挑选基准值 2.分割:所有比基准值小的元素放在基准值左面,大的都放到基准值右面,在这个过程结束后,基准值的排序就完成了。

复杂度

时间复杂度O(nlogn) 空间复杂度S(nlogn)

最差的情况就是每一次取到的元素就是数组中最小/最大的,也就是每次排序只能排一个数,这种情况其实就是冒泡排序了,时间复杂度O(n^2)
最好的情况是每次都选到的是中间的数则T[n] = 2T[n/2] + O(n) 时间复杂度就是O(nlogn)

//相当于DFS的前序遍历,先处理,然后对左子树进行递归,也就是进行左侧排序,再对右边排序
//分治:左边分一半,右边分一半,分别执行partition
void quickSort(vector<int>& arr, int low, int high)
{
    
    
    //结束条件
    if (low >= high) return;
    
    //挖坑法处理:调用该函数,该函数会把第一个值作为基准值,并且把所有小于基准值的值都放到基准值左侧。大的放到右侧,并返回该值位置
    int index = partition(arr, low, high);//挖坑法会对整个序列扫描,也就是O(n)

    //每次基准值已经在正确的位置了,所以基准值不需要再排序
    quickSort(arr, low, index - 1);   //对基准值左侧进行排序
    quickSort(arr, index + 1, high);  // 对基准值右侧再排序
    //T[n] = 2T[n/2] + O(n)
}
// partition(挖坑法)方法就是把传入的数组中第一个数作为基准值,然后把基准值放到正确的位置,返回这个位置
//挖坑法会对整个序列扫描,也就是O(n)
int partition(vector<int>& arr, int l, int r)
{
    
    
    int x = arr[l];  // 选第一个数作为基准值,并空出该坑
    while (l < r) {
    
    
        // 从右向左找第一个小于基准数的数
        while (l < r && arr[r] >= x) {
    
    
            r--;
        }
        arr[l] = arr[r]; // 找到该数,放入空出来的坑中,
        // 从左向右找第一个大于等于基准数的数
        while (l < r && arr[l] <= x) {
    
    
            l++;
        } 
        arr[r] = arr[l];//找到该数放入之前的坑
    }
    //这时空出的坑的位置就是我们需要放置基准值的位置
    arr[l] = x;
    return l;
}

例题

  • 剑指 Offer 40. 最小的k个数(快排思想)

归并排序

建立在归并操作上的一种有效排序,是分治法的典型应用

思想

分治,解决,合并(使用DFS后序遍历)

  • 分治,使用后序遍历进行归并,也就是后序遍历会一组一组将俩俩排序数组合并
  • 解决:排序两个有序数组:merge方法:把两个有序数组,组合成一个有序数组,类似于把两个有序链表组合成一个有序链表
  • 合并:由于是后序遍历,所以他会自底向上先合并最底层,然后一层一层向上合并排序数组,这样到最顶层的时候整个数组就排序完成

复杂度

归并排序的最好,最坏,平均时间复杂度均为O(nlogn)。

// merge方法:把两个有序数组,组合成一个有序数组,类似于把两个有序链表组合成一个有序链表
// 思路:两个指针分别指向两个链表头节点,哪个小就连接哪个,然后后移该指针,继续比较当前两个指针指向的值
// 将一个数组中left到mid,与mid到right这两个数组合并
vector<int> res;
void merge(vector<int>& nums,int left, int mid, int right) {
    
    
    //两个数组大小分别为mid-left+1   right-mid ,合并这两个长度的数组
    int pa = left;//左vector头
    int pb = mid+1;//右vector头
    int curIndex = left;
    while (pa <= mid && pb <= right) {
    
    
        if(nums[pa] <= nums[pb]) {
    
    
            res[curIndex++] = nums[pa++];
        } else {
    
    
            res[curIndex++] = nums[pb++];
        }
    }
    //把剩下的数组中没放完的放进去
    while (pa <= mid) res[curIndex++] = nums[pa++];
    while (pb <= right) res[curIndex++] = nums[pb++];
    //把排序好的数组放回nums
    for (int i = left; i <= right; ++i) {
    
    
        nums[i] = res[i];
    }
}
//分治,使用后序遍历进行归并,也就是后序遍历会一组一组将俩俩排序数组合并,由于是后序遍历,所以他会自底向上
//先合并最底层,然后一层一层向上合并排序数组,这样到最顶层的时候整个数组就排序完成
void dfs(vector<int>& nums,int left,int right)
{
    
    
    if (left >= right) return;
    
    int mid = (left+right)/2;//首先进行分区,然后递归操作
    dfs(nums,left,mid);
    dfs(nums,mid+1,right);//第一次将其分为两个区间,进行合并操作

    merge(nums,left,mid,right);
}
vector<int> mergeSort(vector<int>& nums) {
    
    
    dfs(nums,0,nums.size()-1);
    return nums;
}

测试结果


int main()
{
    
    
    //随机产生N个数
    int N = 200000;
    srand(time(NULL));  //生成随机数种子
    //放到vector中
    vector<int> nums(N);
    for (int i = 0; i < N; ++i) {
    
    
        nums[i] = 1 + (rand() % N);
        //cout << nums[i] << ",";
    }
    cout << endl;
    //开始计时
    double start, finish; /* 定义开始的时间和结束的时间 */
    start = (double)clock();

    //20000
    //BubbleSort(nums); //2288ms
    //SelectionSort(nums); //915ms
    //InsertionSort(nums); //528ms

    //200000
    //mergeSort(nums); //93ms
    //quickSort(nums, 0, nums.size()-1);//41ms 
    //HeapSort(nums);//53ms

    finish = (double)clock();
    //打印
    // for (auto item : nums)
    // {
    
    
    //     cout << item << ",";
    // }
    
    cout << (finish) - start << " ms" << endl;
}

外部排序

外部排序
https://blog.csdn.net/ailunlee/article/details/84548950
比如有100个数据排序,而我内存只能容纳10个数
1.那么我们先将100个数据分成10组,每组可以进行内部排序,比如使用归并或者快排
2.然后进行二路归并,也就是挑出两组对两个有序数组进行排序
3.两个有序数组进行排序,俩俩数据读取将数据放入内存,然后比较哪个小就将哪个输出到硬盘
4.当然这里可以使用多路归并,也就是,10路归并,每组数取第一个,这样取10个数放入内存,然后选出最小的输出到硬盘,输出的那组数再读入一个,保证内存中始终有10个数然后依次输出到硬盘。

例题

    1. 有序矩阵中第 K 小的元素(多路归并)

猜你喜欢

转载自blog.csdn.net/chongbin007/article/details/105779578