首页 > 编程语言 >经典排序算法

经典排序算法

时间:2022-12-10 21:45:30浏览次数:62  
标签:arr temp int elems 算法 经典 排序 size

经典排序算法

预定义

#define Ty int // 以整型为例

交换数据的函数

void swap(Ty* elems,int i,int j) {
    Ty temp=elems[i];
    elems[i]=elems[j];
    elems[j]=temp;
}

// 如果确认只用整型数据使用下面的代码会更快,因为减少了函数压栈,也使用了位运算
#define SWAP_INT(a, b) { a = (a)^(b); b = (a)^(b); a = (a)^(b); }

冒泡排序

原理:将一段序列的最大值(最小值)拿到最左边或者最右边的操作,使用循环重复操作,(每轮排序都会少一个最大值或最小值),当最后只剩下一个数据的时候整个序列就已经排好序了。
冒泡排序的原理很简单,使用代码实现也很简单。也是所有排序算法里面最简单的一个。
具体代码实现:

void bubSort(Ty* elems,int size) {
    for (int i = 0;i < size;size--) {    //轮
        for (int j = i + 1;j < size;j++) {    //找剩余元素中的最值
            if (elems[j] < elems[j-1]) {
                swap(elems,j,j-1);
            }
        }
    }
}

void bubSort(Ty* elems,int size) {
    for(int i=0;i<size-1;i++)
        for(int j=i+1;j<size;j++)
            if(elems[i]<elems[j])
                swap(elems,i,j);
}

直接使用两层循环去实现,外层循环主要作用是存放最大值或最小值的,内存循环的主要作用是找到发生冲突的元素,如果发生冲突就交换两个数据。当两层循环的结束的时候整个序列就自然排好序了。时间复杂度为O(n^2).

选择排序

原理:基本思想和冒泡排序是一样的,选择排序相对于冒泡排序的优点就是减少交换次数。算法思想都是在序列中找到最大值(最小值),然后存放好下次进入循环就访问不到这个最大值(最小值)。当两层循环都结束的时候序列就自然排好了。
具体代码实现:

void selectSort(Ty* elems,int size) {
    for(int i=0;i<size-1;i++) {
        int minPos=i;
        for(int j=i+1;j<size;j++)
              if(elems[minPos]>elems[j])
                minpos=j;
        swap(elems,i,minPos);
    }
}

插入排序

原理:基本思想还是冒泡排序,不过插入排序是两边相靠的冒泡,所以在序列部分有序的情况下,插入排序的效率要比冒泡排序效率高。从序列的尾部开始往前比较,如果当前的数据小于(大于)前一个的数据就进行交换,否则进入下一次循环,直到外层循环遍历完整个序列就自然排好序了。
具体代码实现:

//使用交换函数
void insertSort(Ty* elems,int size) {
    for(int i = 1;i < size;i++)
        for(int j = i;j > 0 && elems[j-1] > elems[j];j--)
            swap(elems,j-1,j);
}
//不使用交换函数
void insertSort(Ty* elems,int size) {
    for (int i = 1;i < size;i++) {
        int j = i;
        int temp = arr[j];
        for (;j >= 1 && temp < arr[j-1];j--)
            arr[j] = arr[j-1];
        arr[j] = temp;
    }
}

希尔排序

原理:希尔排序是建立在插入排序的基础上进行优化的排序算法,所以希尔排序又叫做优化版的插入排序。
代码实现:

  1. 将间隔定为4
void shellSort(Ty* elems,int size) {
    for(int h = 4;h >= 1;h = h >> 1)
        for(int i = h;i < size;i++)
            for(int j = i;j >= h && elems[j] > elems[j-h];j -= h)
                swap(elems,j,j-h);
}
  1. 使用常用的序列号
//使用交换函数
void shellSort(Ty* elems,int size) {
    int h = 1;
    while(h < size/3) h = 3*h + 1;
    while(h >= 1) {
        for(int i = h;i < size;i++)
            for(int j = i;j >= h && elems[j] > elems[j-h];j -= h)
                swap(elems,j,j-h);
        h /= 3;
    }
}
//不使用交换函数
void shellSort(Ty* elems,int size) {
    int h = 1;
    int t = length/3;
    while (h < t) h = 3*h + 1;
    while (h >= 1) {
        for (int i = h;i < length;i++) {
            int j = h;
            Ty temp = arr[j];
            for (;j >= h && temp < arr[j-h];j-=h)
                arr[j] = arr[j-h];
            arr[j] = temp;
        }
        h /= 3;
    }
}
# 实例解释
size=13,h=4 # h=13/3
        h
        |
4 6 8 7 9 5 3 1 2 10 0 11 12
        ||
        ij
# 4<9 so i++;j=i
        h
        |
4 6 8 7 9 5 3 1 2 10 0 11 12
          ||
          ij
# 6>5 交换 j-=h
        h
        |
4 5 8 7 9 6 3 1 2 10 0 11 12
  |          |
  j          i
# j<h i++;j=i
# 依此类推

快速排序

原理:快速排序的核心思想是设立一个轴,然后其他数据都和这个轴作比较,最后把轴放在序列的中间,执行完一遍快速排序后左边的数据都比轴小,右边的数据都比轴大。然后递归下去,当递归结束的时候就拍好序了。快速排序的排序很快,但是当数据形成一边倒的情况的时候就发挥不出快速排序的优势。
具体代码实现:

//稍微修改一下适用于单链表的快速排序
void quickSort(Ty *elems, int first, int last) {
    if (first >= last || first < 0 || last < 0)
        return;
    Ty privot = elems[first];
    int i = first + 1;
    int j = first + 1;
    while (j <= last) {
        if (elems[j] < privot) {
            swap(&elems[i], &elems[j]);
            i++;
        }
        j++;
    }
    swap(&elems[first], &elems[i - 1]);
    quickSort(elems, first, i - 1);
    quickSort(elems, i, last);
}
//仅仅适用于数组的快速排序
void quickSort(Ty* elems,int left,int right) {
    if (left >= right) return;
    int i = left;
    int j = right;
    Ty privot = elems[i];
    while (i < j) {
        while (i < j && arr[j] >= privot) --j;
        arr[i] = arr[j];
        while (i < j && arr[i] <= privot) ++i;
        arr[j] = arr[i];
    }
    arr[i] = privot;
    quickSort(elems,left,i - 1);
    quickSort(elems,i + 1,right);
}

归并排序

原理:把要排序的序列拆分成多个含有一个数据的序列,然后按照从小到大(从大到小)进行合并,这样就自然的将无序的序列排好序。
具体代码实现:

void merge(int arr[],int left,int mid,int right,int* temp) {
    int i = left;
    int j = mid + 1;
    int k = left;
    while (i <= mid && j <= right) temp[k++] = arr[i] < arr[j] ? arr[i++] : arr[j++];
    while (i <= mid) temp[k++] = arr[i++];
    while (j <= right) temp[k++] = arr[j++];
    for (i = left;i <= right;i++)
        arr[i] = temp[i];
}

void merge_sort(int arr[],int left,int right,int* temp) {
    if (left >= right) return;
    int mid = left + ((right - left) >> 1);
    merge_sort(arr,left,mid,temp);
    merge_sort(arr,mid + 1,right,temp);
    merge(arr,left,mid,right,temp);
}

void mergeSort(int arr[],int length) {
    int* temp = (int*)malloc(sizeof(int)*length);
    assert(temp);
    merge_sort(arr,0,length - 1,temp);
    free(temp);
}

堆排序

堆分有小顶堆和大顶堆,而堆排序又分有外堆和内堆。

  • 外堆
    意思是,而外申请一段和原来数组一样大的内存大小,并将数组的元素构造成小顶堆或大顶堆。根据排序的顺序和逆序确定要构成的堆结构是大顶堆还是小顶堆。现以最终序列是顺序排序(从小到大)为例,则需要构成的堆结构是小顶堆。因为小顶堆可以快速的找到序列的最小值,如果将小顶堆的数据依次弹出,则每次弹的都是剩余序列的最小值,并且每次弹出就放到原来的数组中,当小顶堆里的数据都弹出来完了,原来的数组也自然有序了。如果要求最终序列是逆序的则构造的堆结构是大顶堆,然后操作是一样的。时间复杂度是\(O(nlogn)\). 空间复杂度是\(O(n)\).

  • 内堆
    意思是,不需要额外申请空间,直接在原来的数组上进行操作。现以最终序列是顺序排序(从小到大)为例,则需要构成的堆结构是大顶堆。在原来的数组上构造堆结构称之为 “堆化” ,heapify.形成大顶堆后,将堆顶依次弹出并立即放数组的尾部,反复操作此步骤直到最后一个数据,最终自然形成从小到大依次排好序。

//外堆
//堆的结构体描述
typedef struct Heap {
    Ty* root;
    int size;
}Heap;

//创建堆内存
Heap* creatHeap(int capacity) {
    Heap* heap = (Heap*)malloc(sizeof(Heap));
    assert(heap);
    heap->root = (Ty*)malloc(sizeof(Ty) * (capacity+1) );
    heap->size = 1;
    return heap;
}
//入堆
//先将要插入的数据插入到堆的尾部,然后向上渗透,爬到对应的位置,就把数据放进去即可
void pushHeap(Heap* heap,Ty data) {
    int current = heap->size++;
    int parent  = current >> 1; 
    heap->root[current] = data;
    while (parent) {
        if (heap->root[current] < heap->root[parent]) {
            swap(heap->root,current,parent);    
            current = parent;
            parent >>= 1;    
        }
        else break;
    }
}
//出堆
//先将堆顶元素保存下来,然后使用堆的尾部覆盖堆顶,然后往下渗透,走到对应的位置,就把数据放进去,然后返回保存的元素
Ty popHeap(Heap* heap) {
    int current = 1;
    int rchild  = 3;
    int n = --heap->size;
    Ty ret = heap->root[1];
    heap->root[1] = heap->root[n];
    while (rchild < n) {
        int small = heap->root[rchild - 1] < heap->root[rchild] ? rchild - 1 : rchild;
        if (heap->root[small] < heap->root[current]) {
            swap(heap->root,current,small);
            current = small;
            rchild  = (current << 1) + 1;
        }
        else break;
    }
    return ret;
}
//内堆
//在原来数组的基础上直接操作,其实就是入堆和出队直接结合,不需要额外申请空间
void heapify(int arr[],int current,int length) {
    int rchild = (current << 1) + 2;
    int large;
    while (rchild <= length && (arr[large = rchild == length ? rchild-1 : (arr[rchild-1] > arr[rchild] ? rchild-1 : rchild)] > arr[current])) {
        swap(arr,large,current);
        current = large;
        rchild  = (current << 1) + 2;
    }
}

void heapSort(int arr[],int length) {
    int current = length >> 1;
    while (current >= 0) heapify(arr,current--,length);
    while (length) {
        swap(arr,0,--length);
        heapify(arr,0,length);
    }
}

计数排序

前面的算法都是基于比较的排序,计数排序是利用了数组的下标天然有序原理进行排序,所以计数排序是基于统计而排序的排序算法。算法的核心思想是遍历一个无序数组,将遍历到的数据按它的数值找到统计数组的对应下标将其放入统计数组中,依次类推,直到无序的数组的每一个数据都被遍历完之后统计数组也已经初始化完毕,接下来就是遍历统计数组如果遍历到的空间是大于零的就将其下标存入原来的数组中,直到统计数组被遍历完,最终可以排好序。

void countSort(int arr[],int length,int max) {
    int* count = (int*)calloc(max,sizeof(int));
    for (int i = 0;i < length;i++) {
        count[arr[i]]++;
    }
    for (int i = 0,j = 0;i < max;i++) {
        while (count[i]--)
            arr[j++] = i;
    }
    free(count);
}

基数排序

桶排序的思想,按照数字的位数进行排序,准备0-9的链式队列,从低位开始到高位进行排序,当最高位被排好序后原来的序列自然排好序了。
例如:对以下序列进行基数排序
578,234,86,432,512,618,384
排序过程:
第一轮(\(在第零轮的基础上按10^0位排\)):432,512,234,384,86,578,618
第二轮(\(在第一轮的基础上按10^1位排\)):512,618,432,234,578,384,86
第三轮(\(在第二轮的基础上按10^2位排\)):86,234,384,432,512,578,618
第三轮结束序列自然排好序。86不够3位数,就往前面补零,即86 = 086.

void redixSort(int arr[], int length) {
    int i;
    int j;
    int ii;
    int jj;
    int temp[10][10];
    for (ii = 0; ii < 10; ii++)
        for (jj = 0; jj < 10; jj++)
            temp[ii][jj] = -1;
    for (int k = 10; k < 10000; k *= 10) {
        for (i = 0; i < length; i++) {
            int index = (arr[i] % k) / (k / 10);
            j = 0;
            while (temp[index][j] != -1)
                j++;
            temp[index][j] = arr[i];
        }
        i = 0;
        for (ii = 0; ii < 10; ii++) {
            for (jj = 0; jj < length && temp[ii][jj] != -1; jj++) {
                arr[i++] = temp[ii][jj];
                temp[ii][jj] = -1;
            }
        }
    }
}

我在去年已经先将视频讲解发布到B站上了,但是当时没有将文档发布,直接放到博客里让大家访问和学习吧。下面是本次内容的视频链接:
B站视频链接:Cukor丘克

标签:arr,temp,int,elems,算法,经典,排序,size
From: https://www.cnblogs.com/cukor-zhong-520/p/16950326.html

相关文章

  • 一种用于全局数值优化的适应度-距离平衡的新型随机分形搜索优化算法(Matlab代码实现)
    OverridetheentrypointofanimageIntroducedinGitLabandGitLabRunner9.4.Readmoreaboutthe extendedconfigurationoptions.Beforeexplainingtheav......
  • 快速排序
    数据结构实验六的函数题本题要求实现快速排序的一趟划分函数,待排序列的长度1<=n<=1000。函数接口定义:intPartition(SqListL,intlow,inthigh);其中L是待排序表......
  • snowland-smx密码算法库
    snowland-smx密码算法库一、snowland-smx密码算法库的介绍snowland-smx是python实现的国密套件,对标python实现的gmssl,包含国密SM2,SM3,SM4,SM9,ZUC等。其代码实现效率上......
  • function~排序多个班级的成绩
    题目描述把m个班级的学生成绩由高到底进行排序。输入第1行是一个整数m(0<m<100),表示需要排序的班级数。 后面有m组数,每组数分两行:第一行是一个整数n(0<n<50),表示一个班级......
  • KM算法模板
    P3967[TJOI2014]匹配时间复杂度是n3#include<bits/stdc++.h>usingnamespacestd;usingpii=pair<int,int>;#definefifirst#definesesecondconstintM=105;......
  • Dijkstra 算法说明与实现
    Dijkstra算法说明与实现作者:Grey原文地址:博客园:Dijkstra算法说明与实现CSDN:Dijkstra算法说明与实现问题描述问题:给定出发点,出发点到所有点的距离之和最小是多少?......
  • [2022-12-06]神经网络与深度学习hw11 - 各种优化算法比较
    OverridetheentrypointofanimageIntroducedinGitLabandGitLabRunner9.4.Readmoreaboutthe extendedconfigurationoptions.Beforeexplainingtheav......
  • oracle 12.2+支持mysql与postgresql中的collate(排序规则)特性
    sqlserver,mysql,postgresql都支持针对字符串类型定义排序规则的概念(collate),一般来说,排序规则分为三种:基于二进制,是否区分大小写,是否区分重音。例如sqlserver中:SELE......
  • 地下城地图图块生成算法
    一.概述生成地下城,包含房间和迷宫通路。类似:示例效果一示例效果二二.思路1.生成迷宫通路从房间的边缘坐标XY为奇数的格子生成迷宫,确保房间和迷宫通路之间有......
  • openssl国密算法库
    openssl国密算法库一、开发背景openssl是一个功能丰富且自包含的开源安全工具箱。它提供的主要功能有:SSL协议实现(包括SSLv2、SSLv3和TLSv1)、大量软算法(对称/非对称/摘......