计算机经典排序算法——C++实现

计算机笔试经典排序方法总结如下:


计算机经典排序算法

按照上图我们依次介绍不同排序算法及其具体代码实现(升序)。

1.直接插入排序

实现原理:将一个数据插入到已排好序的有序数据中,从而得到一个新的,个数加一的有序数据,在数据量较小时比冒泡排序和简单选择排序性能好一些。


插入排序

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

空间复杂度:O(1)

稳定性:稳定(相同大小的数据,在原无序记录和排好序的记录中前后顺序不被交换)

c++代码:

vector<int> InsertSort::sort(vector<int> ivec) {
    int j = 0;
    for (int i = 1; i < ivec.size(); i++) {
        if (ivec[i] < ivec[i - 1]) {
            j = i;
            while (j > 0 && ivec[j] < ivec[j - 1]) {
                swap(ivec[j], ivec[j - 1]);
                j --;
            }
        }
    }
    return ivec;
}

2.冒泡排序

实现原理:比较相邻两数的大小关系,在一趟冒泡排序过程中可以从原数据中选取最大或最小值,在循环过程中从剩余数据中依次选取最值。


冒泡排序

时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:稳定

算法

  • 比较相邻的元素,如果前大于后,交换;
  • 对每一对相邻元素进行该操作,直到结尾则此时最后元素最大;
  • 重复以上步骤,每次除去上一轮最后一个元素;
  • 重复步骤1-3直到排序完成。

C++代码:

vector<int> BubboSort::sort(vector<int> ivec) {
     for (int i = 0; i < ivec.size(); i ++) {
         for (int j = 1; j < ivec.size() - i; j++) {
             if (ivec[j - 1] > ivec[j]){
                 swap(ivec[j], ivec[j - 1]);
             }
         }
     }
     return ivec;
 }

3.快速排序

实现原理:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。


快速排序

时间复杂度:O(nlogn)
空间复杂度:O(logn)

算法

  • 从数据中挑出一个元素,称为“基准”(pivot);
  • 重新排序数列,所有元素比基准值小放在基准值前,大放在后,相同均可。则这次分区后该基准处于数列中间位置,称为“分区”(partition)操作;
  • 递归地(recursive)把小于基准值的元素的数列和大于基准值的数列进行排序。

C++代码:

void quickSort(vector<int> &vec, int left, int right) {
    if (left >= right) {
        return;
    }
    int pivot = vec[left], l = left, r = right;
    while (l < r) {
        while (l < r && vec[r] >= pivot) {
            r --;
        }
        while (l < r && vec[l] <= pivot) {
            l ++;
        }
        if (l < r) {
            swap(vec[l], vec[r]);
        }
    }
    swap(vec[left], vec[l]);
    quickSort(vec, left, l - 1);
    quickSort(vec, l + 1, right);
}

vector<int> QuickSort::sort(vector<int> ivec) {
    int left = 0;
    int right = ivec.size()-1;
    quickSort(ivec, left, right);
    return ivec;
}

4.选择排序

实现原理:首先在未排序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续找最小(大)元素,放到已排序列末尾。


选择排序

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

算法

  • 初始状态:无序区为R[1,2,...,n],无序区为空;
  • 第i趟排序(i=1,2,...,n-1)开始前,当前有序区和无序区分别为R[1,2,...,i-1]和R[i,i+1,...,n]。该趟从无序区中选出关键字最小的记录R(k),将他与无序区第1个记录R(i)交换,使R[1,2,...,i]和R[i+1,...,n]分别变为个数增加1的新有序区和记录个数减少1的新无序区;
  • n-1趟结束,数列变为有序。

C++代码:

vector<int> InsertSort::sort(vector<int> ivec) {
    for (int i = 0; i < ivec.size(); i ++) {
        int minIndex = i;
        for (int j = i + 1; j < ivec.size(); j++) {
            if (ivec[j] < ivec[minIndex]) {
                minIndex = j;
            }
        }
        swap(ivec[i], ivec[minIndex]);
    }
    return ivec;
}

5.堆排序

实现原理:利用堆这种数据结构设计的一种算法,堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或大于)其父结点。


堆排序

堆的定义:n个元素的序列\{ k_1,k_2,...,k_n\}当且仅当满足以下条件时称为堆。
小顶堆\left\{\begin{aligned} k_i \leq k_{2i} \\ k_i \leq k_{2i+1}\end{aligned}\right.(i = 1,2,...)
大顶堆\left\{\begin{aligned} k_i \geq k_{2i} \\ k_i \geq k_{2i+1}\end{aligned}\right.(i = 1,2,...)
时间复杂度:O(nlogn)
空间复杂度:O(1)
稳定性:不稳定

算法

  • 将初始待排序关键字序列R[1,2,...,n]构成大顶堆,此堆为初始无序区
  • 将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区R[1,2,...,n-1]和新的有序区R[n],且满足R[1,2,...,n-1] \leq R[n]
  • 交换后对当前无序区R[1,2,...,n-1]调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区R[1,2,...,n-2]和新的有序区R[n-1,n],不断重复此过程直到有序区元素个数为n-1,则整个排序过程完成

C++代码

//递归方法进行堆调整
void heapAjust(vector<int> &ivec, int p, int lenl) {
    //p待调整的节点下标
    //len待调整的数组长度
    int parent = p;
    int leftchild = 2 * parent + 1;
    int rightchild = 2 * parent + 2;
    if (leftchild < lenl && ivec[leftchild] > ivec[parent]) {
        parent = leftchild;
    }
    if (rightchild < lenl && ivec[rightchild] > ivec[parent]) {
        parent = rightchild;
    }
    if (parent != p) {
        swap(ivec[parent], ivec[p]);
        heapAjust(ivec, parent, lenl);
    }
}
//非递归方法进行堆调整
void heapAjust(vector<int> &ivec, int p, int len){
//每次判断根节点和左右孩子节点的大小关系,比最大的孩子节点小则交换
        for(int i = 2*p +1; i < len; i = 2*i + 1){
              if(i + 1 < len && ivec[i] < ivec[i + 1]) i++;
              if(ivec[i] > ivec[p]){
                  swap(ivec[i], ivec[p]);
                  p = i;
              }
              else break;
        }
}
vector<int> HeapSort::sort(vector<int> ivec){
    int len = ivec.size();
    //初始化堆
    for (int i = len / 2; i >= 0; i--) {
        heapAjust(ivec, i, len);
    }
    //排序
    for (int t = len - 1; t >= 0; t--) {
        swap(ivec[0], ivec[t]);
        heapAjust(ivec, 0, t);
    }
    return ivec;
}

6.归并排序

实现原理:归并排序(MERGE-SORT)是利用完全二叉树数据结构与归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略
时间复杂度:O(nlogn)
空间复杂度:O(n)
稳定性:稳定

归并排序

//合并函数
void merge(vector<int> &ivec, vector<int> &tmp, int left, int mid, int right) {
    int k = 0, i = left, j = mid + 1;
    while (i <= mid && j <= right){
        if (ivec[i] <= ivec[j]) tmp[k++] = ivec[i++];
        if (ivec[i] > ivec[j]) tmp[k++] = ivec[j++];
    }
    while (i <= mid) tmp[k++] = ivec[i++];
    while (j <= right) tmp[k++] = ivec[j++];
    for (int n = 0; n < k; n++) ivec[left + n] = tmp[n];
}
vector<int> MergeSort::sort(vector<int> ivec) {
    vector<int> tmp = ivec;
    int left = 0;
    int right = ivec.size()-1;
    mergeSort(ivec, tmp, left, right);
    return ivec;
}
//非递归
//以间隔为1开始进行归并,也就是(1,2),(3,4)依次分别进行归并
//以间隔2开始进行归并,也就是(1,2,3,4),(5,6,7,8)依次进行归并
//再以间隔2*2开始进行归并,直到全部排完
 void  mergeSort(vector<int> &ivec, vector<int> &tmp){
        int i = 1, length = ivec.size();
        while(i < length){
            int l = 0;
            while(l < length){
                mid = l + i - 1;
                r = min(l+ 2*i -1, length-1);
                if mid < r and mid < length:
                    merge(ivec, tmp, l, mid, r);
                l += 2*i;
            }
            i *= 2;
      }
}
//递归结构
void mergeSort(vector<int> &ivec, vector<int> &tmp, int left, int right) {
    if (left >= right) { return; }
    int mid = (left + right) / 2;
    mergeSort(ivec, tmp, left, mid);
    mergeSort(ivec, tmp, mid + 1, right);
    merge(ivec, tmp, left, mid, right);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,646评论 4 366
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,979评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,391评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,356评论 0 215
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,740评论 3 293
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,836评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,022评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,764评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,487评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,728评论 2 252
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,200评论 1 263
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,548评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,217评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,134评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,921评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,919评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,766评论 2 274

推荐阅读更多精彩内容

  • 上周谈到“选择”的方法论,这周就讲了“创业的选择”,每一个“概念”是那么地紧紧相扣,上下呼应,还不时插入之前的内容...
    快乐坚强阅读 655评论 0 0
  • 周瑜半晌方苏,众将再三劝解。瑜发誓攻打南郡,定要夺还东吴。肃曰:“公瑾且耐。容某亲见玄德,将理来说他,若...
    饼干贝贝阅读 1,881评论 0 1
  • 尼尔·菲奥说:“我们真正的痛苦,来自于因耽误而产生的持续的焦虑,来自于因最后时刻所完成项目质量之低劣而产生的负罪感...
    Anitaanina阅读 817评论 2 20
  • 忽然的一阵狗叫声吵醒了苏舒,她打开床头灯,看了看手机,时间刚过三点。 开始睡不着了,来凤凰只是想来完成自己心中的诺...
    麦麦_ok阅读 119评论 1 1