zoukankan      html  css  js  c++  java
  • 排序算法总结

    1.冒泡与选择

    冒泡:冒泡排序只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个数据进行比较,如果前者比后者大,就互相交换,最后就会找到一个最大的落在数组最后.重复以上工作n次即可完成排序.

    void BubbleSort(vector<int> a)
    {
        int len = a.size();
        if (len <= 1)
            return;
        for (int i = 0; i < len; i++)
        {
            for (int j = 0; j < len - i - 1; j++)
            {
                if (a[j + 1] < a[j])
                    std::swap(a[j], a[j + 1]);
            }
        }
    }
    
    • 时间复杂度:平均时间复杂度O(n^2).
    • 空间复杂度O(1).原地排序
    • 是否稳定:稳定

    选择:每次找到最小的一个元素放到已经排好序的数组的尾部即可.

    void SelectSort(vector<int> a)
    {
        int len = a.size();
        if (len <= 1)
            return;
        for (int i = 0; i < len; i++)
        {
            for (int j = i + 1; j < len; j++)
            {
                if (a[j] < a[i])
                    std::swap(a[j], a[i]);
            }
        }
    }
    

    2.插入

    就是将后面的数据与前面的数据进行相比,如果小的话就将前面的数据向后移动,然后将其插入即可.

    void InsertSort(vector<int> &a)
    {
        int len = a.size();
        if (len <= 1)
            return;
        for (int i = 1; i < len; i++)
        {
            int key = a[i];
            int j = i - 1;
            /*查找插入的位置*/
            for (; j >= 0; j--)
            {
                if (a[j] > key)
                    a[j + 1] = a[j]; /*数据移动*/
                else
                    break;
            }
            a[j + 1] = key; //数据插入
        }
    }
    
    • 时间复杂度:平均时间复杂度O(n^2).因为我们在一个数组中插入数据是O(n),最坏也是n的平方
    • 空间复杂度O(1).原地排序
    • 是否稳定:稳定

    3.快排

    快排的思想就是"分而治之".找到一个分区点,然后将小的放在前面,大的放在后面,分区点放在中间.最后发现待排序的区间变为1时停止.

    /*在这个函数中会改变原数组,见下图:*/
    /*从头到尾迭代,和支点比较,大的不管,小的换。换了才往后看。最后支点戳中间,算是分界线*/
    int partition(vector<int> &a, int l, int r)
    {
        int k = l, pivot = a[r];
        int temp = 0;
        for (int i = l; i < r; i++)
        {
            if (a[i] <= pivot) /*相等的元素要么放右边,要么放左边,这里也可以放在右边*/
            {
                std::swap(a[i], a[k++]);
            }
        }
        std::swap(a[k], a[r]);
        return k;
    }
    /*快速排序递归函数*/
    void quick_sort(vector<int> &a, int l, int r)
    {
        if (l >= r)
            return;
        int q = partition(a, l, r); /*获取分区点*/
        quick_sort(a, l, q - 1);
        quick_sort(a, q + 1, r);
    }
    
    void QuickSortWithRecursive(vector<int> &a)
    {
        quick_sort(a, 0, a.size() - 1);
    }
    

    其中经过partition函数分区之后,数组就会变成这样.partition 在一般情况下会选择以数组的尾巴为分界线

    在这里插入图片描述

    • 时间复杂度:大多数情况下时间复杂度O(nlog(n)).极少数情况下,效率是O(n^2).(所谓的优化其实就是将这个概率不断降低)
    • 空间复杂度O(1).原地排序
    • 是否稳定:不稳定

    4. 拓扑排序(与图有关,图用(逆)邻接表存储)

    我们先来看一个生活中的拓扑排序的例子。
       我们在穿衣服的时候都有一定的顺序,我们可以把这种顺序想成,衣服与衣服之间有一定的依赖关系。比如说,你必须先穿袜子才能穿鞋,先穿内裤才能穿秋裤。假设我们现在有八件衣服要穿,它们之间的两两依赖关系我们已经很清楚了,那如何安排一个穿衣序列,能够满足所有的两两之间的依赖关系?
       这就是个拓扑排序问题。从这个例子中,你应该能想到,在很多时候,拓扑排序的序列并不是唯一的。你可以看我画的这幅图,我找到了好几种满足这些局部先后关系的穿衣序列。
    在这里插入图片描述
    拓扑排序的一个经典问题就是"如何确定代码源文件的编译依赖关系?"什么是依赖关系,这个就不属于本文讨论范畴了.那么该如何解决呐?

       我们可以把源文件与源文件之间的依赖关系,抽象成一个有向图。每个源文件对应图中的一个顶点,源文件之间的依赖关系就是顶点之间的边。 如果 a 先于 b 执行,也就是说 b 依赖于 a,那么就在顶点 a 和顶点 b 之间,构建一条从 a 指向 b 的边。而且,这个图不仅要是有向图,还要是一个有向无环图,也就是不能存在像 a->b->c->a 这样的循环依赖关系。因为图中一旦出现环,拓扑排序就无法工作了。实际上,拓扑排序本身就是基于有向无环图的一个算法。


    OK ,现在我们有思路了,但是我们如何实现拓扑排序呐?这里有两种方法:

    (1)Kahn 算法

    Kahn 算法实际上用的是贪心算法思想,思路非常简单、好懂。
       定义数据结构的时候,如果 s 需要先于 t 执行,那就添加一条 s 指向 t 的边。所以,如果某个顶点入度为 0, 也就表示,没有任何顶点必须先于这个顶点执行,那么这个顶点就可以执行了。
       我们先从图中,找出一个入度为 0 的顶点,将其输出到拓扑排序的结果序列中(对应代码中就是把它打印出来),并且把这个顶点从图中删除(也就是把这个顶点<指向的顶点的入度>都减 1)。我们循环执行上面的过程,直到所有的顶点都被输出。最后输出的序列,就是满足局部依赖关系的拓扑排序。

    (2)DFS 算法

    首先,我们去思考DFS算法的思想,递归,从开始到结束,再从结束到上一步.那么我们邻接表是否适合?答案不适合,应该用逆邻接表.具体看代码解释:

    #include <iostream>
    #include <vector>
    #include <list>
    #include <algorithm>
    #include <queue>
    
    // 如果点的表示还需要插入等操作的话,就将vector 改为 std::list
    using namespace std;
    
    class Graph
    {
      public:
        Graph(int v)
        {
            this->v = v;
            for (int i = 0; i < v; i++)
                adj.push_back(std::list<int>());
        }
        void AddEdge(int s, int t)//s->t
        {
            adj[s].push_back(t);
        }
        void TopSortByKahn()
        {
            vector<int> inDegree(v, 0); //统计每个顶点的入度
            for (int i = 0; i < v; i++)
            {
                for (auto t : adj[i])
                {
                    inDegree[t]++;
                }
            }
            queue<int> que;
            for (int i = 0; i < v; i++)
            {
                if (inDegree[i] == 0)
                    que.push(i);
            }
            while (!que.empty())
            {
                int head = que.front();
                que.pop();
                cout << "->" << head;
                for (auto t : adj[head])
                {
                    inDegree[t]--;
                    if (inDegree[t] == 0)
                        que.push(t);
                }
            }
        }
        void TopSortByDFS()
        {
            // 先构建逆邻接表
            std::vector<std::list<int>> inverseadj(v, std::list<int>());
            for (int i = 0; i < v; i++) // 通过邻接表生成逆邻接表
            {
                for (auto t : adj[i])
                {
                    inverseadj[t].push_back(i);
                }
            }
            bool visted[v] = {false};
            for (int i = 0; i < v; i++) //dfs 
            {
                if (visted[i] == false)
                {
                    visted[i] = true;
                    dfs(i, inverseadj, visted);
                }
            }
        }
    
      private:
        void dfs(int vertex, const std::vector<std::list<int>> &inverseadj, 
        bool visted[])
        {
            // cout << vertex << endl;
            for (auto t : inverseadj[vertex])
            {
                if (visted[t] == true)
                    continue;
                else
                {
                    visted[t] = true;
                    dfs(t, inverseadj, visted);
                }
            }// 先把 vertex "这个顶点依赖的所有顶点"都打印出来之后,再打印它自己(核心)
            cout << "->" << vertex;
        }
    
      private:
        int v;                           //顶点个数
        std::vector<std::list<int>> adj; //邻接表 
    };
    int main(void)
    {
        Graph g(6);
        g.AddEdge(5, 2);
        g.AddEdge(5, 0);
        g.AddEdge(4, 0);
        g.AddEdge(4, 1);
        g.AddEdge(2, 3);
        g.AddEdge(3, 1);
    
        g.TopSortByKahn();
        cout << endl;
        g.TopSortByDFS();
        cout << endl;
        return 0;
    }
    

    所使用的图是:
    在这里插入图片描述

    5.

    6.

    7.持续更新...

  • 相关阅读:
    Hadoop集群VSFTP和SecureCRT安装配置
    Hadoop集群完全分布式坏境搭建
    Hadoop集群坏境搭建配置
    Hadoop集群坏境CentOS安装
    Pr视频剪辑基础技巧学习
    SAN和NAS之间的基本区别
    原始容量、可用容量和有效容量的区别
    解释一下什么是网盘与云盘
    纠错技术之FEC(向前纠错)
    分布式存储的冗余类型(N+2:1)
  • 原文地址:https://www.cnblogs.com/Tattoo-Welkin/p/10335253.html
Copyright © 2011-2022 走看看