数据结构笔记--十大经典排序算法(C++)

目录

1--概述

2--冒泡排序

3--选择排序

4--插入排序

5--希尔排序

6--快速排序

7--归并排序

8--堆排序

9--计数排序

10--基数排序

11--桶排序


1--概述

        十大经典排序算法包括:冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序、堆排序、计数排序、基数排序 和 桶排序;

2--冒泡排序

主要思想:视频讲解参考

        每次将最大的元素移动到最后;

        时间复杂度为 O (n^2);

#include <iostream>
#include <vector>
#include <queue>

class Solution{
public:
    // 冒泡排序
    void bubSort(std::vector<int> &input){
        int len = input.size();
        while(--len){ // len 一开始指向最后一个元素
            for(int i = 0; i < len; i++){
                if(input[i] > input[i + 1]){
                    int tmp = input[i];
                    input[i] = input[i + 1];
                    input[i + 1] = tmp;
                }
            }
        }
    }
    // 优化的冒泡排序
    void Better_bubSort(std::vector<int> &input){
        int len = input.size();
        int flag = 1;
        while(--len && flag){ // 当前面的已有序,即上一轮没有发生交换,就不会进入循环体
            flag = 0;
            for(int i = 0; i < len; i++){
                if(input[i] > input[i + 1]){
                    flag = 1;
                    int tmp = input[i];
                    input[i] = input[i + 1];
                    input[i + 1] = tmp;
                }
            }
        }
    }
    // 打印函数
    void Print(const std::vector<int>& input){
        for(int item : input){
            std::cout << item << " ";
        }
        std::cout << std::endl;
    }
};

int main(int argc, char *argv[]){
    std::vector<int> input = {1, 3, 6, 4, 2, 5};
    Solution S1;
    std::cout << "Before Sort: " << std::endl;
    S1.Print(input);
    std::cout << "After Sort: " << std::endl;
    S1.bubSort(input);
    S1.Print(input);
    return 0;
}

3--选择排序

主要思想:视频讲解参考

        选择排序基于冒泡排序的思想,每一轮找到最小值(用 k 指向),将最小值与循环的第一个数(用 i 指向)交换,这样可以确保每一轮将最小值选择排序到前面;

        时间复杂度为 O (n^2);

#include <iostream>
#include <vector>
#include <queue>

class Solution{
public:
    // 选择排序
    void selectSort(std::vector<int>& input){
        int len = input.size();
        int k = 0; // 指向本次循环最小值的位置
        for(int i = 0; i < len; i++){
            k = i;
            for(int j = i + 1; j < len; j++){
                if(input[j] < input[k]) k = j; // 更新本次循环最小值的位置
            }
            int tmp = input[i];
            input[i] = input[k]; // 最小值的位置移动到前面
            input[k] = tmp;
        }
    }
    // 打印函数
    void Print(const std::vector<int>& input){
        for(int item : input){
            std::cout << item << " ";
        }
        std::cout << std::endl;
    }
};

int main(int argc, char *argv[]){
    std::vector<int> input = {1, 3, 6, 4, 2, 5};
    Solution S1;
    std::cout << "Before Sort: " << std::endl;
    S1.Print(input);
    std::cout << "After Sort: " << std::endl;
    S1.selectSort(input);
    S1.Print(input);
    return 0;
}

4--插入排序

主要思想:视频讲解参考

        遍历每一个元素,将其插入到前面已经排好序的序列中,即与前面的数逐一比较,直到找到比其小的数,将元素插入到其后面;

        时间复杂度为 O (n^2);

#include <iostream>
#include <vector>
#include <queue>

class Solution{
public:
    // 插入排序
    void insertSort(std::vector<int>& input){
        int len = input.size();
        int key; // key表示本次循环中要插入的值
        for(int i = 1; i < len; i++){ // i 从 1 开始,因为input[0]已经是有序的
            key = input[i];
            int j = i-1; // 从前一个数开始比较
            while(j >= 0 && key < input[j]){ // 向前寻找,直到找到第一个比 key 小的数
                input[j+1] = input[j]; // 比较过的元素向后移动,因为要空出一个位置
                j--; // 继续向前寻找
            }
            input[j+1] = key; // 将 key 插入到第一个比它小的数的后面
        }
    }
    // 打印函数
    void Print(const std::vector<int>& input){
        for(int item : input){
            std::cout << item << " ";
        }
        std::cout << std::endl;
    }
};

int main(int argc, char *argv[]){
    std::vector<int> input = {1, 3, 6, 4, 2, 5};
    Solution S1;
    std::cout << "Before Sort: " << std::endl;
    S1.Print(input);
    std::cout << "After Sort: " << std::endl;
    S1.insertSort(input);
    S1.Print(input);
    return 0;
}

5--希尔排序

主要思想:视频讲解参考

        希尔排序是插入排序的进阶版,实质上是使用多次插入排序,只是每次插入比较的步长是step,当 step 为1时,希尔排序就退化为普通的插入排序;

        希尔排序主要应用在数据量很大的排序问题中,本质上是减少交换的次数,因为当 step 退化为 1 时,序列已经大部分是有序的,所以交换的次数很少。

        希尔排序的时间复杂度为:O(n^(1.3--2)),是一种不稳定的排序;

#include <iostream>
#include <vector>
#include <queue>

class Solution{
public:
    // 希尔排序
    void shellSort(std::vector<int>& input){
        int len = input.size();
        //初始比较的 step 为 len/2,每一轮退化为 step/2;
        for(int step = len / 2; step > 0; step = step / 2){
            // 每一轮使用插入排序
            for(int i = step; i < len; i++){
                int key = input[i]; // 要插入的数
                int j = i - step; // 与前一个数进行比较
                while(j >= 0 && key < input[j]){
                    input[j + step] = input[j]; 
                    j = j - step; // 不断前移
                }
                input[j + step] = key;
            } 
        }
    }
    // 打印函数
    void Print(const std::vector<int>& input){
        for(int item : input){
            std::cout << item << " ";
        }
        std::cout << std::endl;
    }
};

int main(int argc, char *argv[]){
    std::vector<int> input = {1, 3, 6, 4, 2, 5};
    Solution S1;
    std::cout << "Before Sort: " << std::endl;
    S1.Print(input);
    std::cout << "After Sort: " << std::endl;
    S1.shellSort(input);
    S1.Print(input);
    return 0;
}

6--快速排序

主要思路:

        每次选定一个基准轴元素 (pivot),从两边开始扫描,比 pivot 小的元素放在轴的左边,比 pivot 大的元素放在轴的右边;

        经过一次遍历之后,pivot 左边的元素都比 pivot 小,pivot 右边的元素都比 pivot 大,这样对于最终的结果来说,pivot 所在的位置是排好序的,只需递归排序 pivot 左边的元素和右边的元素即可;

        快速排序的时间复杂度为:O(nlogn) ~ O(n^2)

#include <iostream>
#include <vector>
#include <queue>

class Solution{
public:
    // 快速排序
    void quickSort(std::vector<int>& input, int left, int right){
        if(left >= right) return; // 递归结束条件
        int len = input.size();
        int pivot = input[left]; // 记录基准轴元素
        int i = left, j = right;
        while(i < j){
            while(i < j && input[j] >= pivot) j--; // 从右往左扫描直到找到第一个比 pivot 小的元素停下
            input[i] = input[j]; // 将小的数放到 pivot 的左边
            while(i < j && input[i] <= pivot) i++; // 从左往右扫描直到找到第一个比 pivot 大的元素停下
            input[j] = input[i]; // 将大的数放到 pivot 的右边
        }
        // 一趟遍历之后,将基准元素 pivot 放在 i 的位置(i == j)
        input[i] = pivot;
        // 递归排序pivot左边的元素和右边的元素
        quickSort(input, left, i - 1);
        quickSort(input, i+1, right);
    }
    // 打印函数
    void Print(const std::vector<int>& input){
        for(int item : input){
            std::cout << item << " ";
        }
        std::cout << std::endl;
    }
};

int main(int argc, char *argv[]){
    std::vector<int> input = {1, 3, 6, 4, 2, 5};
    Solution S1;
    std::cout << "Before Sort: " << std::endl;
    S1.Print(input);
    std::cout << "After Sort: " << std::endl;
    S1.quickSort(input, 0, input.size()-1);
    S1.Print(input);
    return 0;
}

7--归并排序

        普通归并排序将两个有序的序列归并为一个有序的新序列视频讲解

#include <iostream>
#include <vector>   
 
class Solution{
public:
    std::vector<int> mergeSort(std::vector<int> input1, std::vector<int> input2){
        std::vector<int> Res;
        int i = 0, j = 0;
        while(i < input1.size() && j < input2.size()){
            if(input1[i] < input2[j]){
                Res.push_back(input1[i]);
                i++;
            }
            else{
                Res.push_back(input2[j]);
                j++;
            }
        }
        while(i < input1.size()){
            Res.push_back(input1[i]);
            i++;
        } 
        while(j < input2.size()){
            Res.push_back(input2[j]);
            j++;
        }
        return Res;
    }

    // 打印函数
    void Print(const std::vector<int>& input){
        for(int item : input){
            std::cout << item << " ";
        }
        std::cout << std::endl;
    }
};
 
int main(int argc, char *argv[]){
    std::vector<int> input1 = {1, 3, 4, 6};
    std::vector<int> input2 = {2, 5, 7, 8};
    Solution S1;
    std::cout << "Before Sort: " << std::endl;
    S1.Print(input1);
    S1.Print(input2);
    std::cout << "After Sort: " << std::endl;
    std::vector<int> output = S1.mergeSort(input1, input2);
    S1.Print(output);
    return 0;
}

        进阶归并排序将一个无序的序列归并排序为一个有序的系列;通过递归将无序的序列二分,从底层开始将二分的序列归并排序为有序序列;

#include <iostream>
#include <vector>   
 
class Solution{
public:
    // 归并排序
    std::vector<int> mergeSort(std::vector<int> input){
        std::vector<int> Res = split(input, 0, input.size() - 1);
        return Res;
    }

    // 二分序列
    std::vector<int> split(std::vector<int> input, int left, int right){
        if(left == right){ // 只剩下一个元素,返回由该元素构成的有序序列
            std::vector<int> Res = {input[left]};
            return Res;
        }
        int mid = left + (right - left) / 2;
        std::vector<int> input1 = split(input, left, mid);
        std::vector<int> input2 = split(input, mid+1, right);
        std::vector<int> Res = merge(input1, input2); // 二分至底层,进行归并排序再回溯
        return Res;
    }

    // 将两个有序的序列归并排序
    std::vector<int> merge(std::vector<int> input1, std::vector<int> input2){
        std::vector<int> Res;
        int i = 0, j = 0; 
        while(i < input1.size() && j < input2.size()){
            if(input1[i] < input2[j]){
                Res.push_back(input1[i]);
                i++;
            }
            else{
                Res.push_back(input2[j]);
                j++;
            }
        }
        while(i < input1.size()){
            Res.push_back(input1[i]);
            i++;
        }
        while(j < input2.size()){
            Res.push_back(input2[j]);
            j++;
        }
        return Res;
    }

    // 打印函数
    void Print(const std::vector<int>& input){
        for(int item : input){
            std::cout << item << " ";
        }
        std::cout << std::endl;
    }
};
 
int main(int argc, char *argv[]){
    std::vector<int> input = {1, 4, 6, 3, 5, 2, 8, 7, 10, 9, 11, 12};

    Solution S1;
    std::cout << "Before Sort: " << std::endl;
    S1.Print(input);
    std::cout << "After Sort: " << std::endl;
    std::vector<int> output = S1.mergeSort(input);
    S1.Print(output);
    return 0;
}

8--堆排序

关于堆的知识:从堆的定义到优先队列、堆排序(强烈推荐)

堆排序基本知识:视频讲解参考

        ① 堆排序中的堆分为大根堆小根堆,小根堆中孩子结点的值必须大于父亲结点的值(即堆顶是最小值),大根堆中孩子结点的值必须小于父亲结点的值(即堆顶是最大值);

        ② 大根堆和小根堆本质上是一个完全二叉树,满足以下性质:结点连续存储,假设结点从0开始编号,则左孩子的位置为 2*parent + 1,右孩子的位置为 2*parent + 2;

        ③ (外堆且基于小顶堆)堆排序本质上分为入堆出堆两步:

        入堆时新加入结点,结点需要自底向上与父亲结点进行比较,重构大根堆和小根堆,确保满足上面的性质;

        出堆时返回堆顶元素,并将最后一个元素放在堆顶位置,自顶向下与较小的孩子结点进行比较,重构大根堆和小根堆,确保满足上面的性质;

#include <iostream>
#include <vector>
#include <queue>

class Solution{
public:
    void heapSort(std::vector<int>& input){
        int size = input.size();
        // 入堆
        for(int data : input){
            pushHeap(data);
        }
        // 出堆
        for(int i = 0; i < size; i++){
            input[i] = popheap();
        }
    }

    // 入堆
    void pushHeap(int data){
        if(len == 0){
            heap.push_back(data); // 入堆
            len++;
            return;  
        }

        heap.push_back(data); // 入堆
        len++; // 元素个数 +1

        // 重新排序堆
        int cur = len - 1; // 当前堆中最后一个元素的位置
        int parent = (cur-1) / 2; // 父亲节点的位置
        while(parent != cur){ // 直到根节点
            if(heap[cur] < heap[parent]){
                swap(cur, parent); // 交换两个位置的值
                cur = parent;
                parent = (cur-1) / 2; // 从下往上比较,不断交换
            }
            else break;
        }
    }

    // 出堆
    int popheap(){
        int res = heap[0]; // 堆顶元素
        int cur = len - 1; // 最后一个元素的位置
        heap[0] = heap[cur]; // 最后一个元素的位置放到堆顶中
        len--; // 堆中元素 -1
        // 重新排序堆
        int start = 0;
        while(start <= len){ // 从根节点开始排序
            int l_child = 2*start+1;
            int r_child = 2*start+2;
            if(l_child > len || r_child > len) break; // 防止溢出
            int small = heap[l_child] < heap[r_child] ? l_child : r_child; // 较小的孩子
    
            if (heap[small] < heap[start]){ // 从上到下与较小的孩子交换位置
                swap(small, start);
                start = small;
            }
            else break;
        }
        return res;
    }

    void swap(int index1, int index2){
        int temp = heap[index1];
        heap[index1] = heap[index2];
        heap[index2] = temp;
    }

    // 打印函数
    void Print(const std::vector<int>& input){
        for(int item : input){
            std::cout << item << " ";
        }
        std::cout << std::endl;
    }

private:
    std::vector<int> heap; // 小顶堆
    int len = 0; // 堆中元素的个数
};
 
int main(int argc, char *argv[]){
    std::vector<int> input = {1, 3, 6, 4, 2, 5, 8, 7, 10, 9};
    Solution S1;
    std::cout << "Before Sort: " << std::endl;
    S1.Print(input);
    std::cout << "After Sort: " << std::endl;
    S1.heapSort(input);
    S1.Print(input);
    return 0;
}

9--计数排序

        在计数排序中,用一个数组的下标表示原数组的真值,数组下标对应的值表示真值出现的次数;遍历记录的数组,当下标对应的值不为0时,输入其下标(值多大就输出多少次);

        在 C++ 中可以通过哈希表的形式来实现计数排序,key 表示原数组的真值,value 表示真值出现的次数;

        经典利用数组实现计数排序可以参考视频讲解

#include <iostream>
#include <vector>
#include <map>

class Solution{
public:
    std::vector<int> countSort(std::vector<int> input){
        for(int item : input){ // 遍历每一个数
            M.insert(std::pair<int, int>(item, M.count(item)+1));
        }
        for(auto item : M){ // map自动根据key排好序了,因此直接遍历即可
            int num = item.second; // 出现的次数
            while(num != 0){
                Res.push_back(item.first);
                num--;
            } 
        }
        return Res;
    }

    // 打印函数
    void Print(const std::vector<int>& input){
        for(int item : input){
            std::cout << item << " ";
        }
        std::cout << std::endl;
    }

private:
    std::map<int, int> M; // <真值,出现的次数>
    std::vector<int> Res;
};
 
int main(int argc, char *argv[]){
    std::vector<int> input = {1, 3, 6, 4, 2, 5, 8, 7, 10, 9};
    Solution S1;
    std::cout << "Before Sort: " << std::endl;
    S1.Print(input);
    std::cout << "After Sort: " << std::endl;
    std::vector<int> output = S1.countSort(input);
    S1.Print(output);
    return 0;
}

10--基数排序

        在基数排序中,对每一个数依次按照个位、十位、百位 ... 来排序,对最高位排序后即为有序结果,返回即可;

11--桶排序

猜你喜欢

转载自blog.csdn.net/weixin_43863869/article/details/131611361