首页 > 其他分享 >排序(快排/归并/堆排/冒泡)

排序(快排/归并/堆排/冒泡)

时间:2023-05-30 21:22:20浏览次数:40  
标签:堆排 end start int nums len 快排 冒泡 节点

912. 排序数组

  • 稳定排序:如果 a 原本在 b 前面,且 a == b,排序之后 a 仍然在 b 前面。
  • 非稳定排序:如果 a 原本在 b 前面,且 a == b,排序之后 a 不一定在 b 前面。
  • 原地排序 / 非原地排序:区别在于是否 使用额外的数组 辅助排序

快排

快排不稳定
平均时间复杂度\(O(n \log n)\)

简单快排

最差情况时间复杂度\(n^2\)

void QuickSortSimple(vector<int>& nums, int start, int end){
    if(start >= end) return;
    swap(nums[start], nums[rand() % (end - start + 1) + start]); // 基于随机的原则,随机选取一个值作为哨兵值
    int tmp = nums[start];
    int l = start, r = end;
    while(l<r){
        // 往左走
        while (l<r && nums[r] >= tmp) --r;          // 结束时,右边遇到第一个小于tmp的数字就交换
        // 往右走
        while (l<r && nums[l] <= tmp) ++l;          // 结束时,左边遇到第一个大于tmp的数字;加等号的意义:如果在前进的过程中出现了相等的元素,加上等号,就代表跳过,继续向右移动,不进行填坑
        if(l<r) swap(nums[r], nums[l]);                  // 右边填坑
    }
    // 两边相等了
    swap(nums[start], nums[l]);
    QuickSortSimple(nums, start, l-1);     // 左边区间
    QuickSortSimple(nums, l+1, end);      // 右边区间
}

三点中值(Median-Of-Three)快排

最差情况下时间复杂度为\(O(n \log n)\)

int choosePivot(vector<int>& nums, int start, int end){
        int mid = start+(end-start)/2;
        if(nums[start] > nums[mid]) swap(nums[start], nums[mid]);
        if(nums[start] > nums[end]) swap(nums[start], nums[end]);
        if(nums[mid] > nums[end]) swap(nums[mid], nums[end]);
        return nums[mid];
    }
void QuickSortMedianOfThree(vector<int>& nums, int start, int end){
    if(start >= end) return;
    // 三数取中
    int pivot = choosePivot(nums, start, end);
    int l = start, r = end;
    while(l <= r) {
        while(nums[l] < pivot) ++l;    // 从左边找到第一个大于哨兵的值 
        while(nums[r] > pivot) --r;    // 从右边找到第一个小于哨兵的值
        if(l <= r) {                                              // 如果小于,则交换位置,并双方向各自的前方移动一个位置
            swap(nums[l++], nums[r--]);
        }
    }
    // 此时r<l,哨兵左边都是小于该哨兵值,右边都是大于该哨兵值
    QuickSortMedianOfThree(nums, start, r);
    QuickSortMedianOfThree(nums, l, end);
}

归并排序

稳定
时间复杂度\(O(n \log n)\)

自顶向下

// 自顶向下
void mergeSort(vector<int>& nums, int l, int r){
    if(l>=r) return ;
    int mid = l + (r-l) /2;
    mergeSort(nums, l, mid);                    // 递归到最左端
    mergeSort(nums, mid+1, r);                  // 递归到最右端,子区间只有一个元素
    int i=l, j = mid+1;
    int cnt =0;
    while(i<=mid && j<= r)temp[cnt++] = nums[i] < nums[j] ? nums[i++] : nums[j++];                      // 将两个右序数组合并到一个temp数组
    while(i<= mid) temp[cnt++] = nums[i++];
    while(j<= r) temp[cnt++] = nums[j++];
    for(int i=0; i< r-l+1; i++){ nums[i+l] = temp[i];
}

自底向上

// 自底向上
vector<int> sortArray(vector<int>& nums) {
    if(nums.size()==1) return nums;
    int n = nums.size();

    for(int size=1; size< n; size *= 2){
        for(int start =0; start < n-size; start += 2*size){
            int mid = start + size -1;
            int end = min(start + size * 2 -1,  n-1);
            merge(nums, start, mid, end);
        }
    }
    return nums;
}
void merge(vector<int>&nums, int start, int mid, int end){
    vector<int> temp(end-start+1, 0);
    int i=start, j = mid+1;
    int cnt=0;
    while(i<= mid && j <= end) temp[cnt++] = nums[i]<nums[j] ? nums[i++] : nums[j++];
    while(i<=mid) temp[cnt++] = nums[i++];
    while(j<=end) temp[cnt++] = nums[j++];
    for(int i=0; i< cnt; ++i){
        nums[start+i] = temp[i]; 
    }
}

堆排

大顶堆

// len显示调整的区间长度
// holeIndex为开始节点,len为结束节点 
void AdjustHeap(vector<int>& nums, int holeIndex, int len){
    while(holeIndex*2+1 < len){     // 左孩子节点要小于总节点数量,只要没有超出范围就一直向下调整大小关系
        int lchild = holeIndex * 2+1;
        int rchild = lchild + 1;
        int large = holeIndex;
        if((lchild < len) && nums[lchild] > nums[holeIndex])  large = lchild;       // 左节点大于父节点
        if((rchild < len) && nums[rchild] > nums[large])  large = rchild;           // 右节点大于父节点大于左节点
        if(large != holeIndex){
            swap(nums[holeIndex], nums[large]);
            holeIndex = large; // 将替换的孩子作为新洞
        }
        else break;   // 父节点都大于子节点,退出
    }
}
void MakeHeap(vector<int>& nums){
    int len = nums.size();
    // 找出第一个需要重排的子树头部,任何叶子节点不需要执行下溯:将子节点和其较大子节点“对调”,并持续下放,直到叶子节点
    // 数组array中如果当前节点为i,则左节点为2i+1,右节点为2i+2,父节点位于i/2处:#0元素使用
    int holeIndex = (len-1) / 2;        // 最右端叶子节点的父节点下标
    for(int i = holeIndex; i>=0; i--){
        AdjustHeap(nums, i, len);
    }
}
int main(){
    vector<int> nums = {-2, 45, 0, 11, -9, 7};
    MakeHeap(nums);                     // 创建一个大顶堆
    int len = nums.size()-1;
    for(int i=len; i>=0;i--){
        swap(nums[i], nums[0]);          // 将堆顶元素放到数组最后面,此时堆已经不是大顶堆了,需要重新调整堆的结构
        --len;                              // 调整范围[0, --len)
        AdjustHeap(nums, 0, len);           // 只调整堆顶元素就可以
    }

    for(auto& c:nums){
        cout << c <<" " ;
    }
    cout <<endl;
}

冒泡排序

时间复杂度\(O(n^2)\)

// n个长度数组长度要排n-1次
void bubblesort(vector<int>& nums){
    int n = nums.size();
    for(int i=0;i<n-1;i++){ // 当前元素
        bool flag = false;
        for(int j=0; j<n-1-i;j++){      // n-1-i后面的元素表示已经排序完成
            if(nums[j]>nums[j+1]) {
                swap(nums[j], nums[j+1]);   // 将大的元素交换到最后面
                flag = true;
            }
        }
        if(flag == false) break;
    }
}

标签:堆排,end,start,int,nums,len,快排,冒泡,节点
From: https://www.cnblogs.com/xiaohuidi/p/17444503.html

相关文章

  • 算法之常见排序算法-冒泡排序、归并排序、快速排序
    对于编程中琳琅满目的算法,本人向来是不善此道也不精于此的,而说起排序算法,也只是会冒泡排序。还记得当初刚做开发工作面试第一家公司时,面试官便让手写冒泡排序(入职之后才知道,这面试官就是一个冒泡排序"病态"爱好者,逢面试必考冒泡排序-__-)。后来看吴军的一些文章,提到提高效率的关键就......
  • 冒泡排序
    冒泡排序#include<stdio.h>intmain(){ints[10]={12,65,32,69,5,8,21,36,4,15};inti=0,j=0,c=0,b=0;intlen=sizeof(s)/sizeof(int);for(i=0;i<len;i++){printf("%d",s[i]);}printf("\n");for(j=0;j<len-1......
  • 三路快排Java版(带思路分析)
    快速排序这里我们直接开始讲相对的最优解带随机数的三路快排好了,中间还有很多版本的快排,但是都有一些问题导致在某种极端情况下造成耗费时间极多。基础快排:在序列本身有序的情况下复杂度为O(n²)带随机数的快排:在序列本身有序的情况下复杂度为O(nlogn),但是在序列全部元素相同......
  • 实现堆,堆排序,解决堆的TOPK问题
    这篇博客我会尽我自己的所能讲解堆,同时详细的解释堆中重要的向下和向上调整算法,以及推排序的两种实现方法,和堆的TOPK问题。堆是什么我们之前已经介绍过了树,而堆就是一种完全二叉树。这里我放一张二叉树的图下面我来解释一下满二叉树,和完全二叉树的区别:满二叉树是指除了叶子节点外,每......
  • 冒泡排序
    【三】冒泡排序基本思想:两个数比较大小,较大的数下沉,较小的数冒起来。过程:比较相邻的两个数据,如果第二个数小,就交换位置。从后向前两两比较,一直到比较最前两个数据。最终最小数被交换到起始的位置,这样第一个最小数的位置就排好了。继续重复上述过程,依次将第2.3...n-1个......
  • python中对列表元素大小排序(冒泡排序法,选择排序法和插入排序法)—排序算法
    前言排序(Sorting)是计算机程序设计中的一种重要操作,它的功能是将一个数据元素(或记录)的任意序列,重新排列成一个关键字有序的序列。本文主要讲述python中经常用的三种排序算法,选择排序法,冒泡排序法和插入排序法及其区别。通过对列表里的元素大小排序进行阐述。一、选择排序法选择......
  • [SEO知识讲解] 揭秘大批量做“多个关键词快排技巧”
    本文转载自:[SEO知识讲解]揭秘大批量做“多个关键词快排技巧”更多内容请访问钻芒博客:https://www.zuanmang.net做SEO优化的人都知道,同一个关键词如果你排在竞争对手的前面,那么你的机会就更多。网站前期的策划也好,用户体验也好,都需要通过关键词,才能把真正的客户带到网站,变成实实......
  • 冒泡排序
    1.问题描述对N个整数进行升序排列2.问题分析冒泡法应该是最基础最简单的了。共有N个数,我们可以利用数组进行储存。冒泡排序的思想就是从表头开始往后扫面数组,过程中比较相邻两个元素的大小,若前面元素大于后面的元素,则将他们互换,称之为消去了一个逆序。在扫描过程中,不断地将两相......
  • 冒泡排序2
    #define_CRT_SECURE_NO_WARNINGS1#include<stdio.h>intmain(void){ intarr[5];//加上'\0'后由数组变成字符串 inti,j,temp; printf("请输入五个数字:\n"); for(j=0;j<5;j++) { scanf("%d",&arr[j]); } for(i=0;i&......
  • 通过冒泡排序,实现通过值拿key
    '''实现冒泡排序后,输出关联字典的key通过值得列表,排序后,拿到字典的key,在透过key,可以关联另一个列表适用场景,比如投票后,要通过数据拿到前三名的名字'''a={'a':1,'b':2,'c':3,'d':4}s=[1,3,2,1]ss=[]list_k=[]foriinrange(len(s)):print('iiiiiiiiii......