• 常用排序算法实现


    时间复杂度

    O ( 1 ) O(1) O(1)

    void func1(int n){
        int count = 100;
        count++;
    }
    void func2(int n){
        int count = 100;
        for(int i = 0; i < count;i++){
    
        }
    }
    int func3(int n){
        return n;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    O ( n ) O(n) O(n)

    void func1(int n){
        int count = 100;
        for(int i = 0; i < n;i++){
            count++;
        }    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    O ( n 2 ) O(n^2) O(n2)

    void func1(int n){
        int count = 100;
        for(int i = 0; i < n;i++){
            for(int j = 0;j < n;j++){
                count++;
            }
        }    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    O ( n m ) O(nm) O(nm)

    void func1(int n,int m){
        int count = 100;
        for(int i = 0; i < n;i++){
            for(int j = 0;j < m;j++){
                count++;
            }
        }    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    O ( log ⁡ n ) O(\log n) O(logn)

    void func1(int n){
        int i = 1;
        while(i < n){
            i *= 2;
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    O ( n log ⁡ n ) O(n \log n) O(nlogn)

    void func1(int n){
        int i = 1;
        for(j = 0; j < n; j++){
            while(i < n){
                i *= 2;
            }  
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    空间复杂度

    O ( 1 ) O(1) O(1)

    void func1(int n){
        int a = 1;
        a++;
        int b = 2;
        b++;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    O ( n ) O(n) O(n)

    void func1(int n){
        int[] array = new int[n];
        for(int i = 0;i < n; i++){
            array[i] = i;
        }
    }
    
    int sum(int n){
        int a;
        if(n == 0) return 0;
        return n + sum(n - 1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    O ( n 2 ) O(n^2) O(n2)

    void func1(int n){
        int[,] array = new int[n,n];
    }
    
    int sum(int n){
        int[] array = new int[n];
        if(n == 0) return 0;
        return n + sum(n - 1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    稳定性和冒泡排序

    稳定性:当需要排序的序列中存在相同关键字的元素,排序后的相对次序保持不变,则算法稳定,稳定的排序算法会保留数值项顺序的表达意义。
    冒泡排序:

    int [] array = new int[]{2,5,6,7,1,3}
    void bubbleSort(int[] arr, int n){
        int temp;
        int i,j;
        for(i = 0;i< n - 1; i++){
            for(j = 0; j < n-1-i; j++){
                if(arr[j] > arr[j + 1]){
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    时间复杂度为 O ( n 2 ) O(n^2) O(n2),空间复杂度 O ( 1 ) O(1) O(1)

    改进版:

    int [] array = new int[]{2,5,6,7,1,3}
    void bubbleSort(int[] arr, int n){
        int temp;
        int i,j;
        bool flag = false;   
        for(i = 0;i< n - 1; i++){
            for(j = 0; j < n-1-i; j++){
                flag = false;
                if(arr[j] > arr[j + 1]){
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    flag = true;
                }
            }
            if(!flag){
                break;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    选择排序

    
    void arr_sort(int *p,int n)
    {
        int i,j;
        int min = 0;
        for(i = 0;i < n - 1;i++)//排序次数
        {
            min = i;
            for(j = i + 1;j < n;j++)
            {
                if(p[j] < p[min])
                {
                    min = j;//记录交换的元素下标值
                }
            }
            if(i != min)
            {
                int temp = p[i];
                p[i] = p[min];
                p[min] = temp;
            }  
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    插入排序

    int [] array = new int[]{2,5,6,7,1,3}
    void printArray(int[] arr){
        ls = "";
        foreach(var r in arr){
            ls += r + ",";
        }
        Debug.Log("轮次" + index++ + "数组" + ls);
    }
    void insertSort(int[] arr, int n){
        for(i = 0;i< n; i++){
            for(j = i; j > 0 && arr[j-1] > arr[j]; j--){
                int temp = arr[j];
                arr[j] = arr[j - 1];
                arr[j - 1] = temp;
            }
            printArray(arr);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    时间复杂度为 O ( n 2 ) O(n^2) O(n2),空间复杂度 O ( 1 ) O(1) O(1)

    希尔排序

    希尔排序是对插入排序的优化。

    int [] array = new int[]{2,5,6,7,1,3}
    void printArray(int[] arr){
        ls = "";
        foreach(var r in arr){
            ls += r + ",";
        }
        Debug.Log("轮次" + index++ + "数组" + ls);
    }
    void shellSort(int[] arr, int n){
        int i = 0;
        int j = 0;
        int temp;
        for(int step = n / 2; step > 0; step = step / 2){
            for(i = step; i < n; i++){
                temp = arr[i];
                for(j = i; j >= step && temp < arr[j - step]; j -= step){
                    arr[j] = arr[j - step];
                }           
                arr[j] = temp;
            }
            printArray(arr);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度 O ( 1 ) O(1) O(1),稳定性是不稳定的

    堆排序

    堆是一颗完全的二叉树,堆中某个节点的值总是不大于(大顶堆)或不小于(小顶堆)其父节点的值

    void heapSort(int[] list){
        // 构建初始堆
        for(int i = list.Length / 2 - 1; i >= 0; i--){
            AdjustHeap(list,i,list.Length);                // O(n)
        }
        // 进行n-1次循环,完成排序
        for(int i = list.Length - 1; i > 0; i--){         // O(nlogn)
            // 交换堆顶(最大数)到最后,并把这个节点从二叉树去掉
            int temp = list[i];
            list[i] = list[0];
            list[0] = temp;
            // 只有堆顶不确定是不是最大,只调整堆顶即可
            AdjustHeap(list, 0, i);
        }
    }
    public void AdjustHeap(int[] array,int parent ,int Length){
        int temp = array[parent];    // 父节点
        int child = 2 * parent + 1;  // 左子节点
        while(child < Length){
            // 找最大的子节点
            if(child + 1 < Length && array[child] < array[child + 1]){
                child++;
            }
            if(temp >= array[child])
                break;
            // 如果子节点较大,交换父子节点
            array[parent] = array[child];
            // 子节点作为父节点,进行下一轮比较
            parent = child;
            child = 2 * child + 1;
        }
        array[parent] = temp;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn),空间复杂度 O ( 1 ) O(1) O(1),稳定性是不稳定的

    二叉树排序

    二叉排序树:空树或满足以下条件

    1. 如果左子树不空,左子树上的所有节点值均小于根节点的值。如果右子树不空,右子树上的所有节点均大于根节点的值。
    2. 左、右子树分别为二叉排序树。
      遍历顺序:
      中序遍历(左根右)、先序遍历(根左右)、后序遍历(左右根)
      我们采用中序遍历,也就是说,二叉排序树从小到大的遍历顺序是从最后最左的子节点开始,往上遍历,遍历到父节点的时候,再从下往上,从左往右遍历右子节点,如此往复,遍历完左右节点。
    public class BinarySortTreeNode{
        public int Key{get;set;}
        public  BinarySortTreeNode Left{get;set;}
        public BinarySortTreeNode Right{get;set;}
        public BinarySortTreeNode(int key){
            Key = key;
        }
        public void Insert(int key){
            var tree = new BinarySortTreeNode(key);
            if(tree.Key <= Key){
                if(Left == null){
                    Left = tree;
                }
                else{
                    Left.Insert(key);
                }
                else{
                    if(Right == null){
                        Right = tree;
                    }
                    else{
                        Right.Insert(key);
                    }
                }
            }
        }
        // 遍历方法(妙啊)
        public void InorderTraversal(){
            Left?.InorderTraversal();
            Debug.Log(Key);
            Right?.InorderTraversal();
        }
    }
    
    public static void BinaryTreeSort(int[] array){
        var binarySortTreeNode = new BinarySortTreeNode(array[0]);
        for(int i = 1; i < array.Length; i++){
            binarySortTreeNode.Insert(array[i]);        
        }
        binarySortTreeNode.InorderTraversal();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn),空间复杂度 O ( n ) O(n) O(n),稳定性是不稳定的

    快速排序

    参考:https://blog.csdn.net/qq_40941722/article/details/94396010

    void Quick_Sort(int *arr, int begin, int end){
        if(begin > end)
            return;
        int tmp = arr[begin];
        int i = begin;
        int j = end;
        while(i != j){
            while(arr[j] >= tmp && j > i)
                j--;
            while(arr[i] <= tmp && j > i)
                i++;
            if(j > i){
                int t = arr[i];
                arr[i] = arr[j];
                arr[j] = t;
            }
        }
        arr[begin] = arr[i];
        arr[i] = tmp;
        Quick_Sort(arr, begin, i-1);
        Quick_Sort(arr, i+1, end);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    function Sort(list, leftIndex, rightIndex)
      if(leftInfex >= rightIndex) then
        return
      end
      local i = leftIndex
      local j = rightIndex
      local baseValue = list[leftIndex]
    
      while i < j do
        while i < j and list[j] >= baseValue do
          j = j - 1
        end
        while i < j and list[i] <= baseValue do
          i = i + 1
        end
        if j > i then
          t = list[i];
          list[i] = list[j];
          list[j] = t;
        end
      end
      list[leftIndex] = list[i];
      list[i] = baseValue;
      Quick_Sort(list, leftIndex, i-1);
      Quick_Sort(list, i+1, rightIndex);
    end
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    快速排序最优时间复杂度为O(nlogn),不稳定

    归并排序

    参考:https://www.bilibili.com/video/BV1Pt4y197VZ/

    function sort(arr, startIndex = 0, endIndex = arr.length - 1) {
        // 递归结束条件:startIndex大于等于endIndex的时候
        if (startIndex >= endIndex) {
            return;
        }
    
        // 折半递归
        let midIndex = parseInt((startIndex + endIndex) / 2);
        sort(arr, startIndex, midIndex);
        sort(arr, midIndex + 1, endIndex);
        // 将两个有序的小数组,合并成一个大数组
        merge(arr, startIndex, midIndex, endIndex);
    }
    
    function merge(arr, startIndex, midIndex, endIndex) {
        // 新建一个大数组
        let tempArr = [];
        let p1 = startIndex;
        let p2 = midIndex + 1;
        let p = 0;
    
        // 比较两个有序小数组的元素,依次放入大数组中
        while (p1 <= midIndex && p2 <= endIndex) {
            if (arr[p1] <= arr[p2]) {
                tempArr[p++] = arr[p1++];
            } else {
                tempArr[p++] = arr[p2++];
            }
        }
    
        // 右侧小数组已排序完毕,左侧小数组还有剩余,将左侧小数组元素依次放入大数组尾部
        while (p1 <= midIndex) {
            tempArr[p++] = arr[p1++];
        }
        // 左侧小数组已排序完毕,右侧小数组还有剩余,将右侧小数组元素依次放入大数组尾部
        while (p2 <= endIndex) {
            tempArr[p++] = arr[p2++];
        }
    
        for (let i = 0; i < tempArr.length; i++) {
            arr[i + startIndex] = tempArr[i];
        }
    }
    
    let arr = [4, 5, 8, 1, 7, 2, 6, 3];
    sort(arr);
    console.log(arr);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    时间复杂度是O(nlogn),稳定

    排序算法的使用场景

    1. 游戏中得分排行榜
    2. 英雄战力榜列表等

    拓扑排序

    拓扑排序(Topological Sorting)是一种对有向无环图(Directed Acyclic Graph, DAG)进行排序的算法。在拓扑排序中,节点表示图中的任务或事件,有向边表示任务间的依赖关系。
    拓扑排序的目标是将图中的节点按照一定的顺序线性排列,使得所有的依赖关系都得到满足。也就是说,在排序结果中,若存在一条从节点 A 到节点 B 的有向边,那么节点 A 在排序中必须出现在节点 B 之前。
    通俗讲解:当同时存在很多件事情需要完成,但是有些事情的完成依赖于另外若干件事情的完成,拓扑排序需要我们排列出事情完成的顺序,这个顺序往往不止一种。

    拓扑排序的算法通常使用深度优先搜索(DFS)或广度优先搜索(BFS)来实现。在实际应用中,如果图中存在环路(即有向图中存在回路),则无法进行拓扑排序,因为无法满足所有的依赖关系。因此,拓扑排序算法通常要求应用于有向无环图。

    步骤
    1. 构建图:根据给定的有向图的边关系,构建表示图的数据结构。通常使用邻接表或邻接矩阵表示图。
    2. 统计节点入度:对于每个节点,统计它的入度(即指向该节点的边的数量)。可以通过遍历图的边关系,记录每个节点的入度。
    3. 初始化队列:创建一个队列(可以使用队列数据结构),用于存储入度为0的节点。
    4. 入度为0的节点入队:遍历所有节点,将入度为0的节点加入队列。
    5. 拓扑排序:从队列中取出一个节点,输出该节点(或保存到结果序列中),并将其相邻节点的入度减1。若某个节点的入度减为0,则将其加入队列。
    6. 重复步骤5,直到队列为空。如果输出的节点数量与图中的节点数量相同,则拓扑排序成功;否则,图中存在环路,无法进行拓扑排序。
    7. 输出结果:按照拓扑排序的顺序输出节点,即为拓扑排序的结果。
    int topological_sort(LGraph G)
    {
        int i,j;
        int index = 0;
        int head = 0;           // 辅助队列的头
        int rear = 0;           // 辅助队列的尾
        int *queue;             // 辅组队列
        int *ins;               // 入度数组
        char *tops;             // 拓扑排序结果数组,记录每个节点的排序后的序号。
        int num = G.vexnum;
        ENode *node;
     
        ins  = (int *)malloc(num*sizeof(int));  // 入度数组
        tops = (char *)malloc(num*sizeof(char));// 拓扑排序结果数组
        queue = (int *)malloc(num*sizeof(int)); // 辅助队列
        assert(ins!=NULL && tops!=NULL && queue!=NULL);
        memset(ins, 0, num*sizeof(int));
        memset(tops, 0, num*sizeof(char));
        memset(queue, 0, num*sizeof(int));
     
        // 统计每个顶点的入度数
        for(i = 0; i < num; i++)
        {
            node = G.vexs[i].first_edge;
            while (node != NULL)
            {
                ins[node->ivex]++;
                node = node->next_edge;
            }
        }
     
        // 将所有入度为0的顶点入队列
        for(i = 0; i < num; i ++)
            if(ins[i] == 0)
                queue[rear++] = i;          // 入队列
     
        while (head != rear)                // 队列非空
        {
            j = queue[head++];              // 出队列。j是顶点的序号
            tops[index++] = G.vexs[j].data; // 将该顶点添加到tops中,tops是排序结果
            node = G.vexs[j].first_edge;    // 获取以该顶点为起点的出边队列
     
            // 将与"node"关联的节点的入度减1;
            // 若减1之后,该节点的入度为0;则将该节点添加到队列中。
            while(node != NULL)
            {
                // 将节点(序号为node->ivex)的入度减1。
                ins[node->ivex]--;
                // 若节点的入度为0,则将其"入队列"
                if( ins[node->ivex] == 0)
                    queue[rear++] = node->ivex;  // 入队列
     
                node = node->next_edge;
            }
        }
     
        if(index != G.vexnum)
        {
            printf("Graph has a cycle\n");
            free(queue);
            free(ins);
            free(tops);
            return 1;
        }
     
        // 打印拓扑排序结果
        printf("== TopSort: ");
        for(i = 0; i < num; i ++)
            printf("%c ", tops[i]);
        printf("\n");
     
        free(queue);
        free(ins);
        free(tops);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    其中G是有向图,结构可能如下:

    struct ENode
    {
        int ivex;                   // 该边所指向的顶点的位置
        struct ENode *next_edge;    // 指向下一条弧的指针
    } ;
    
    struct VNode
    {
        char data;                  // 顶点的数据
        ENode *first_edge;          // 指向第一条依附该顶点的边的指针
    } ;
    
    struct struct
    {
        VNode vexs[MAX_VERTEX_NUM]; // 顶点数组
        int vexnum;                 // 顶点数量
        int edgenum;                // 边数量
    } ;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 相关阅读:
    【leetcode】【剑指offer Ⅱ】028. 展平多级双向链表
    Mac使用本地docker,在IDEA进行本地容器化部署、启动
    java计算机毕业设计共享汽车管理系统MyBatis+系统+LW文档+源码+调试部署
    Vue3 监听属性
    实验三-----数据库
    4.03 用户中心-订单管理功能开发
    2023年亚太杯数学建模思路 - 复盘:光照强度计算的优化模型
    如何修改docker容器中的MySQL数据库的密码?
    从中间表取数更新TW销售合同登记数据(含表体)
    PhalAPI学习笔记 ——— 第三章细致讲解使用PSR-4规范自定义你的命名空间
  • 原文地址:https://blog.csdn.net/tianjuewudi/article/details/134277933