【算法】排序算法总结(附演示图+代码)

数据结构课程设计----综合排序 《数据结构课程设计》报告 日期: 项目名称: 综合排序 一、项目简介和需求分析 排序(sorting是计箅机程序设计的一种重要操作,它的功能是将一组任意顺序数据元素(记录),根据某一个(或几个)关键字按一定的顺序里新排列成为有序的序列。由于待排序的记录数量不同,使得排序过程中涉及的存储器的不同,可将排序方法分为两大类:一类是内部排序,指的是待排序的记录存放在计算机随机存储器中进行的排序过程;另一类是外部排序,指的是待排序记录的数量很大,以致内存一次不能容纳全部记 阅读详情

排序问题总结

十种常见排序算法可以分为两大类:

  • 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。
  • 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。

排序分类
分别的特性如下:

  1. 稳定性:排序后,相同大小的项不改变前后位置,称为稳定,反之为不稳定。
  2. 时间复杂度:算法基础知识,表示一个方法的运行时间量级
  3. 空间复杂度:表示方法所需的内存数量级

时间复杂度

比较类排序:

一、冒泡排序(Bubble Sort)

1.1 算法描述:

这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。

1.2 算法演示:

冒泡排序算法的原理如下:

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
    bubble

1.3 代码:

#include <iostream>
using namespace std;
template<typename T>
//整数或浮点数皆可使用
void bubble_sort(T arr[], int len)
{
    int i, j;  T temp;
    for (i = 0; i < len - 1; i++)
        for (j = 0; j < len - 1 - i; j++)
        if (arr[j] > arr[j + 1])
        {
            temp = arr[j];
            arr[j] = arr[j + 1];
            arr[j + 1] = temp;
        }
}
int main()
{
    int arr[] = { 61, 17, 29, 22, 34, 60, 72, 21, 50, 1, 62 };
    int len = (int) sizeof(arr) / sizeof(*arr);
    bubble_sort(arr, len);
    for (int i = 0; i < len; i++)
        cout << arr[i] << ' ';
 
    cout << endl;
 
    float arrf[] = { 17.5, 19.1, 0.6, 1.9, 10.5, 12.4, 3.8, 19.7, 1.5, 25.4, 28.6, 4.4, 23.8, 5.4 };
    len = (int) sizeof(arrf) / sizeof(*arrf);
    bubble_sort(arrf, len);
    for (int i = 0; i < len; i++)
        cout << arrf[i] << ' ';
 
    return 0;
}

二、快速排序(Quick Sort)

2.1 算法描述:

快速排序(Quicksort)是对冒泡排序算法的一种改进。
冒泡排序是将具有“相对大/小”的这种性质的数字进行逐个位置确认并交换位置,而快速排序在这种位置确认操作模式的基础上,引入了“分治”的思想。
快速排序使用分而治之的思想,通过一趟排序将待排序列分割成两部分,其中一部分均比另一部分小。之后分别对这两部分记录继续进行排序,以达到整个序列有序的目的。

2.2 算法演示:

快速排序算法通过多次比较和交换来实现排序,其排序流程如下:

  1. 首先设定一个分界值,通过该分界值将数组分成左右两部分。
  2. 将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
  3. 然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
  4. 重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

对于分治算法,当每次划分时,算法若都能分成两个等长的子序列时,那么分治算法效率会达到最大。通过上述流程我们可以发现,分界值的选择是很重要的。选择分界值的方式决定了两个分割后两个子序列的长度,进而对整个算法的效率产生决定性影响。

最理想的方法是,选择的分界值恰好能把待排序序列分成两个等长的子序列
一般的选择方法有三种:
方法(1):固定位置
方法(2):随机选取基准
方法(3):三数取中(median-of-three)
快排的确定是面对已经排好的序列与全部相等的序列进行排序时效率很差,因此我们要对它进行优化,可以参考这篇博客快速排序优化

这个过程看起来并不直观,还是需要理解分治的思想:
在这里插入图片描述

2.3 代码:

朴素版:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>

using namespace std;

const int N = 1000010;
int num[N];

void QUICK_sort(int num[], int l, int r) {
    if (l >= r) return;

    int i = l - 1, j = r + 1, x = num[l + r >> 1];
    while (i < j)
    {
        do i++; while (num[i] < x);
        do j--; while (num[j] > x);
        if (i < j) swap(num[i], num[j]);
    }
    QUICK_sort(num, l, j);
    QUICK_sort(num, j + 1, r);
}

最终优化版本:


/*函数作用:取待排序序列中low、mid、high三个位置上数据,选取他们中间的那个数据作为枢轴*/  
int SelectPivotMedianOfThree(int arr[],int low,int high)  
{  
    int mid = low + ((high - low) >> 1);//计算数组中间的元素的下标  

    //使用三数取中法选择枢轴  
    if (arr[mid] > arr[high])//目标: arr[mid] <= arr[high]  
    {  
        swap(arr[mid],arr[high]);  
    }  
    if (arr[low] > arr[high])//目标: arr[low] <= arr[high]  
    {  
        swap(arr[low],arr[high]);  
    }  
    if (arr[mid] > arr[low]) //目标: arr[low] >= arr[mid]  
    {  
        swap(arr[mid],arr[low]);  
    }  
    //此时,arr[mid] <= arr[low] <= arr[high]  
    return arr[low];  
    //low的位置上保存这三个位置中间的值  
    //分割时可以直接使用low位置的元素作为枢轴,而不用改变分割函数了  
} 


void QSort(int arr[],int low,int high)  
{  
    int first = low;  
    int last = high;  

    int left = low;  
    int right = high;  

    int leftLen = 0;  
    int rightLen = 0;  

    if (high - low + 1 < 10)  
    {  
        InsertSort(arr,low,high);  
        return;  
    }  

    //一次分割  
    int key = SelectPivotMedianOfThree(arr,low,high);//使用三数取中法选择枢轴  

    while(low < high)  
    {  
        while(high > low && arr[high] >= key)  
        {  
            if (arr[high] == key)//处理相等元素  
            {  
                swap(arr[right],arr[high]);  
                right--;  
                rightLen++;  
            }  
            high--;  
        }  
        arr[low] = arr[high];  
        while(high > low && arr[low] <= key)  
        {  
            if (arr[low] == key)  
            {  
                swap(arr[left],arr[low]);  
                left++;  
                leftLen++;  
            }  
            low++;  
        }  
        arr[high] = arr[low];  
    }  
    arr[low] = key;  

    //一次快排结束  
    //把与枢轴key相同的元素移到枢轴最终位置周围  
    int i = low - 1;  
    int j = first;  
    while(j < left && arr[i] != key)  
    {  
        swap(arr[i],arr[j]);  
        i--;  
        j++;  
    }  
    i = low + 1;  
    j = last;  
    while(j > right && arr[i] != key)  
    {  
        swap(arr[i],arr[j]);  
        i++;  
        j--;  
    }  
    QSort(arr,first,low - 1 - leftLen);  
    QSort(arr,low + 1 + rightLen,last);  
}  

三、插入排序(Insertion Sort)

3.1 算法描述:

插入排序,一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法。
插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。

3.2 算法演示:

算法过程:

  1. 从第一个元素开始,该元素可以认为已经被排序;
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描;
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置;
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
  5. 将新元素插入到该位置后;
  6. 重复步骤2~5,直到最后一个元素结束。

这种算法关键词相同的数据元素将保持原有位置不变,所以该算法是稳定的。
插入排序适用于已经有部分数据已经排好,并且排好的部分越大越好。一般在输入规模大于1000的场合下不建议使用插入排序。
insert

3.3 代码:

    void sort(int a[],int len)
    {
        //将a[]按升序排列
        for (int i=1;i<len;i++)
        {
        //将a[i]插入到a[i-1],a[i-2],a[i-3]……之中
            for(int j=i;j>0&&(a[j]<a[j-1);j--)
            {
                int temp=a[j];
                a[j]=a[j-1];
                a[j-1]=temp;
            }
        }
    }

四、选择排序(Selection Sort)

4.1 算法描述:

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。

4.2 算法演示:

运行过程:

  1. 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
  2. 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
  3. 重复第二步,直到所有元素均排序完毕。

选择排序是一种简单直观的排序算法,无论什么数据进去都是 O(n²) 的时间复杂度。所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。
select

4.3 代码:

void swap(int *a,int *b) //交換兩個變數
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

void selection_sort(int arr[], int len)
{
    int i,j;

        for (i = 0 ; i < len - 1 ; i++)
    {
                int min = i;
                for (j = i + 1; j < len; j++)     //走訪未排序的元素
                        if (arr[j] < arr[min])    //找到目前最小值
                                min = j;    //紀錄最小值
                swap(&arr[min], &arr[i]);    //做交換
        }
}

五、希尔排序(Shell Sort)

5.1 算法描述:

希尔排序(Shell’s Sort)是插入排序的一种,又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本,该算法是冲破O(n2)的第一批算法之一,十分经典的体现了算法的进步。希尔排序是非稳定排序算法。该方法因 D.L.Shell 于 1959 年提出而得名。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

5.2 算法演示:

算法步骤:

  1. 选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
  2. 按增量序列个数 k,对序列进行 k 趟排序;
  3. 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

请添加图片描述

5.3 代码:

void shell_sort(int arr[], int len) {
        int gap, i, j;
        int temp;
        for (gap = len >> 1; gap > 0; gap >>= 1)
                for (i = gap; i < len; i++) {
                        temp = arr[i];
                        for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap)
                                arr[j + gap] = arr[j];
                        arr[j + gap] = temp;
                }
}

六、归并排序(Merge Sort)

6.1 算法描述:

在《算法导论》这本书中,讲到分治思想时举得例子就是归并排序(Merge Sort),归并排序是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

6.2 算法演示:

算法步骤:

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;

  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;

  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;

  4. 重复步骤 3 直到某一指针达到序列尾;

  5. 将另一序列剩下的所有元素直接复制到合并序列尾。

迭代版本示意图:
在这里插入图片描述

和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是 O(nlogn) 的时间复杂度。代价是需要额外的内存空间。

6.3 代码:

迭代版本:

int min(int x, int y) {
    return x < y ? x : y;
}
void merge_sort(int arr[], int len) {
    int *a = arr;
    int *b = (int *) malloc(len * sizeof(int));
    int seg, start;
    for (seg = 1; seg < len; seg += seg) {
        for (start = 0; start < len; start += seg * 2) {
            int low = start, mid = min(start + seg, len), high = min(start + seg * 2, len);
            int k = low;
            int start1 = low, end1 = mid;
            int start2 = mid, end2 = high;
            while (start1 < end1 && start2 < end2)
                b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
            while (start1 < end1)
                b[k++] = a[start1++];
            while (start2 < end2)
                b[k++] = a[start2++];
        }
        int *temp = a;
        a = b;
        b = temp;
    }
    if (a != arr) {
        int i;
        for (i = 0; i < len; i++)
            b[i] = a[i];
        b = a;
    }
    free(b);
}

递归版本:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>

using namespace std;

const int N = 1000010;
int num[N],tmp[N];


void GB_sort(int num[], int l, int r) {
    if (l >= r)return;
    int mid = l + r >> 1;
    GB_sort(num, l, mid);
    GB_sort(num, mid + 1, r);
    int k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r)
        if (num[i] <= num[j]) tmp[k++] = num[i++];
        else tmp[k++] = num[j++];
    while (i <= mid) tmp[k++] = num[i++];
    while (j <= r)tmp[k++] = num[j++];
    for (i = l, j = 0; i <= r; i++, j++)
        num[i] = tmp[j];

}


int main() {

    int x;
    scanf("%d", &x);
    for (int i = 0; i < x; i++)
        scanf("%d", &num[i]);

    GB_sort(num, 0, x - 1);

    for (int i = 0; i < x; i++)
        printf("%d ", num[i]);

    return 0;

}

七、堆排序(Heap Sort)

7.1 算法描述:

堆排序(Heap Sort)是指利用堆这种数据结构所设计的一种排序算法。
堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

7.2 算法演示:

算法过程:

  1. 创建一个堆并不断维护
  2. 输出根节点后,将根节点与最后的叶子交换,并减小尺寸1,维护堆
  3. 重复第二步直到堆的尺寸为 1。

请添加图片描述

7.3 代码:

#include <iostream>
#include<algorithm>

using namespace std;

const int N =1e5+10;

int n,m,Size=0;
int d[N];

void down(int x){
    int t = x;
    if(2*x<=Size&&d[2*x]<d[t])t=2*x;
    if(2*x+1<=Size&&d[2*x+1]<d[t])t=2*x+1;
    if(t != x){
        swap(d[t],d[x]);
        down(t);
    }
    return ;
}

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>d[i];
    Size = n;

    for(int i = Size/2 ; i>=1 ; i--)down(i);

    for(int i=1;i<=m;i++)
    {
        printf("%d ",d[1]);
        d[1]=d[Size];
        Size--;
        down(1);
    }
    return 0;

}

非比较类排序:

八、计数排序(Counting Sort)

8.1 算法描述:

计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。

当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(n* log(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(n* log(n)), 如归并排序,堆排序)

8.2 算法演示:

算法过程:

  1. 找出待排序的数组中最大和最小的元素
  2. 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
  3. 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
  4. 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1

count

8.3 代码:

#include <iostream>
using namespace std;
const int MAXN = 100000;
const int k = 1000; // range(范围)
int a[MAXN], c[MAXN], ranked[MAXN];
 
int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; ++i) {
        cin >> a[i]; 
        ++c[a[i]];
    }
    for (int i = 1; i < k; ++i)
        c[i] += c[i-1];
    for (int i = n-1; i >= 0; --i)
        ranked[--c[a[i]]] = a[i];//如果是i表达的是原数标号,a[i]就是排序后的正确序列
    for (int i = 0; i < n; ++i)
        cout << ranked[i] << endl;
    return 0;
}

九、桶排序(Bucket Sort)

9.1 算法描述:

桶排序 (Bucket sort)或所谓的箱排序是计数排序的改进,工作的原理是将数组分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。
桶排序是鸽巢排序的一种归纳结果。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间(O(n))。但桶排序并不是比较排序,他不受到 O(n * log n) 下限的影响。

9.2 算法演示:

桶排序算法要求,数据的长度必须完全一样,程序过程要产生长度相同的数据

数据结构设计:链表可以采用很多种方式实现,通常的方法是动态申请内存建立结点,但是针对这个算法,桶里面的链表结果每次扫描后都不同,就有很多链表的分离和重建。如果使用动态分配内存,则由于指针的使用,安全性低。
所以,笔者设计时使用了数组来模拟链表(当然牺牲了部分的空间,但是操作却是简单了很多,稳定性也大大提高了)。

平均情况下桶排序以线性时间运行。像基数排序一样,桶排序也对输入作了某种假设, 因而运行得很快。具体来说,基数排序假设输入是由一个小范围内的整数构成,而桶排序则假设输入由一个随机过程产生,该过程将元素一致地分布在区间[0,1)上。 桶排序的思想就是把区间[0,1)划分成n个相同大小的子区间,或称桶,然后将n个输入数分布到各个桶中去。因为输入数均匀分布在[0,1)上,所以一般不会有很多数落在一个桶中的情况。
为得到结果,先对各个桶中的数进行排序,然后按次序把各桶中的元素列出来即可。

9.3 代码:

#include<iterator>
#include<iostream>
#include<vector>
using namespace std;
const int BUCKET_NUM = 10;

struct ListNode{
        explicit ListNode(int i=0):mData(i),mNext(NULL){}
        ListNode* mNext;
        int mData;
};

ListNode* insert(ListNode* head,int val){
        ListNode dummyNode;
        ListNode *newNode = new ListNode(val);
        ListNode *pre,*curr;
        dummyNode.mNext = head;
        pre = &dummyNode;
        curr = head;
        while(NULL!=curr && curr->mData<=val){
                pre = curr;
                curr = curr->mNext;
        }
        newNode->mNext = curr;
        pre->mNext = newNode;
        return dummyNode.mNext;
}


ListNode* Merge(ListNode *head1,ListNode *head2){
        ListNode dummyNode;
        ListNode *dummy = &dummyNode;
        while(NULL!=head1 && NULL!=head2){
                if(head1->mData <= head2->mData){
                        dummy->mNext = head1;
                        head1 = head1->mNext;
                }else{
                        dummy->mNext = head2;
                        head2 = head2->mNext;
                }
                dummy = dummy->mNext;
        }
        if(NULL!=head1) dummy->mNext = head1;
        if(NULL!=head2) dummy->mNext = head2;
       
        return dummyNode.mNext;
}

void BucketSort(int n,int arr[]){
        vector<ListNode*> buckets(BUCKET_NUM,(ListNode*)(0));
        for(int i=0;i<n;++i){
                int index = arr[i]/BUCKET_NUM;
                ListNode *head = buckets.at(index);
                buckets.at(index) = insert(head,arr[i]);
        }
        ListNode *head = buckets.at(0);
        for(int i=1;i<BUCKET_NUM;++i){
                head = Merge(head,buckets.at(i));
        }
        for(int i=0;i<n;++i){
                arr[i] = head->mData;
                head = head->mNext;
        }
}

十、基数排序(Radix Sort)

10.1 算法描述:

基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或(bin sort),顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog®m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。

10.2 算法演示:

实现原理:
基数排序的发明可以追溯到1887年赫尔曼·何乐礼在打孔卡片制表机(Tabulation Machine)上的贡献。它是这样实现的:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
基数排序的方式可以采用LSD(Least significant digital)或最高位优先MSD(Most Significant Digit first)法,LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。

MSD法:先按k1排序分组,同一组中记录,关键码k1相等,再对各组按k2排序分成子组,之后,对后面的关键码继续这样的排序分组,直到按最次位关键码kd对各子组排序后。再将各组连接起来,便得到一个有序序列。

LSD法:先从kd开始排序,再对kd-1进行排序,依次重复,直到对k1排序后便得到一个有序序列

在这里插入图片描述

10.3 代码:

#include<math.h>
testBS()
{
    inta[] = {2, 343, 342, 1, 123, 43, 4343, 433, 687, 654, 3};
    int *a_p = a;
    //计算数组长度
    intsize = sizeof(a) / sizeof(int);
    //基数排序
    bucketSort3(a_p, size);
    //打印排序后结果
    inti;
    for(i = 0; i < size; i++)
    {
        printf("%d\n", a[i]);
    }
    intt;
    scanf("%d", t);
}
//基数排序
voidbucketSort3(int *p, intn)
{
    //获取数组中的最大数
    intmaxNum = findMaxNum(p, n);
    //获取最大数的位数,次数也是再分配的次数。
    intloopTimes = getLoopTimes(maxNum);
    inti;
    //对每一位进行桶分配
    for(i = 1; i <= loopTimes; i++)
    {
        sort2(p, n, i);
    }
}
//获取数字的位数
intgetLoopTimes(intnum)
{
    intcount = 1;
    inttemp = num / 10;
    while(temp != 0)
    {
        count++;
        temp = temp / 10;
    }
    returncount;
}
//查询数组中的最大数
intfindMaxNum(int *p, intn)
{
    inti;
    intmax = 0;
    for(i = 0; i < n; i++)
    {
        if(*(p + i) > max)
        {
            max = *(p + i);
        }
    }
    returnmax;
}
//将数字分配到各自的桶中,然后按照桶的顺序输出排序结果
voidsort2(int *p, intn, intloop)
{
    //建立一组桶此处的20是预设的根据实际数情况修改
    intbuckets[10][20] = {};
    //求桶的index的除数
    //如798个位桶index=(798/1)%10=8
    //十位桶index=(798/10)%10=9
    //百位桶index=(798/100)%10=7
    //tempNum为上式中的1、10、100
    inttempNum = (int)pow(10, loop - 1);
    inti, j;
    for(i = 0; i < n; i++)
    {
        introw_index = (*(p + i) / tempNum) % 10;
        for(j = 0; j < 20; j++)
        {
            if(buckets[row_index][j] == NULL)
            {
                buckets[row_index][j] = *(p + i);
                break;
            }
        }
    }
    //将桶中的数,倒回到原有数组中
    intk = 0;
    for(i = 0; i < 10; i++)
    {
        for(j = 0; j < 20; j++)
        {
            if(buckets[i][j] != NULL)
            {
                *(p + k) = buckets[i][j];
                buckets[i][j] = NULL;
                k++;
            }
        }
    }
}

参考地址:

https://www.runoob.com/

https://www.baike.baidu.com/

十大经典排序算法(动图演示)(***) 阅读详情

相关推荐

排序算法演示程序

排序算法的演示程序,在windows平台下运行,可以演示多种排序算法的过程。

七大经典排序算法(动图演示+代码展示)

常见排序算法可以分为两大类:非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序。线性时间非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此称为线性时间非比较类排序。 算法复杂度 1. 冒泡排序(BubbleSort) 冒泡排序是一种简单的排序算法,是...

weixin_43224539的博客 3839

数据结构8种排序算法动态演示

该程序使用VC6.0工具开发,使用MFC单文档实现数据结构8种排序算法的图形界面动态演示,更加形象的展示排序过程,八种排序算法包括插入排序(直接插入排序、折半插入排序、希尔排序)、选择排序(直接选择排序、堆排序)、交换排序(冒泡排序、快速排序)、归并排序

八大排序算法详解(动图演示 思路分析 实例代码java 复杂度分析 适用场景)

一、分类 1.内部排序和外部排序  内部排序:待排序记录存放在计算机随机存储器中(说简单点,就是内存)进行的排序过程。 外部排序:待排序记录的数量很大,以致于内存不能一次容纳全部记录,所以在排序过程中需要对外存进行访问的排序过程。 2.比较类排序和非比较排序 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性...

bytes9的专栏 1137

数据结构排序算法演示

一款模拟数据结构排序过程的课件,以前在数据结构课上老师给演示过国外学生做的 java 版的,感觉不错,自己也写了一个用了大约 1 个月的时间!——外国学生的创意,中国学生的手艺!

SAP ERP 物料主数据同步外围系统

w_input-beskz = i_marc_new-beskz."采购类型 " BESKZ--- ADD BY HX_FHJ 20210106。w_input-beskz = lw_marc-beskz."采购类型 " BESKZ--- ADD BY HX_FHJ 20210106。w_input-sobsl = i_marc_new-sobsl."特殊采购类型-虚拟物料标识。

ChampaignWolf的专栏 1747

C语言十大经典排序算法(动态演示+代码,值得收藏)!

§ 时间、空间复杂度比较 排序算法平均时间复杂度最差时间复杂度空间复杂度数据对象稳定性 1、冒泡排序 算法思想: 比较相邻的元素。如果第一个比第二个大,就交换他们两个。 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。 针对所有的元素重复以上的步骤,除了最后一个。 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。 冒泡排序动图演示: 代码: 2、选择排序 算法思想: Ⅰ. 在未排序序列中找...

HUYA69的博客 3762

maxwell与steady state thermal 通过workbench耦合仿真

文章目录maxwellsteady state thermal maxwell 1加入maxwell并打开 2导入模型 3选择求解器类型:涡流求解器eddy current 4修改模型名字不能带_等特殊符号 5为模型赋材料 6设立3D边界,各边电流进出方向为0其余方向300 7设置激励条件,可设置电流大小,相位,方向 8设置集肤深度,选取中间铜导体的体然后设置,如何设置看帮助文档 9求解设置 迭代步数 收敛精度 交流电频率 10验证条件满足计算 11求解 发现无法求解,删除ai

roymustang的博客 9202

【数据结构与算法】十二大经典排序算法之--冒泡排序(动图演示+附Python代码)详细分析-看了还不懂算我输

冒泡排序 1.冒泡排序中心思想: ①冒泡排序(Bubble Sort)是一种简单的排序算法。 ②它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。 ③遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。 ④这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。 2.运算流程: &amp;amp;amp;lt;1&amp;amp;amp;gt;.比较相邻的元素。如果第一个比第二个大...

weixin_41635305的博客 705

深入浅出DNS系列(一)- DNS杂谈

黄丝玛:班达山,你好,我记性不好,你告诉我搜狐、新浪的网站IP地址好吗? 班达山:你问问DNS先生吧,他专门干这买卖的

jiangsd198的博客 4130

BQ25713寄存器配置全解析:从基础原理到笔记本充电实战

在电源管理系统中,寄存器配置是实现精细化控制的核心技术。其原理是通过I2C等总线向芯片内部特定地址写入二进制代码,直接设定其工作参数与行为逻辑,从而实现对电压、电流、功率及保护阈值的底层控制。这项技术的价值在于,它能将通用电源管理芯片灵活适配于千差万别的具体应用,是提升系统性能、可靠性与能效的关键。在便携设备、嵌入式系统等场景中,精准的寄存器配置决定了充电效率、动态响应和故障保护能力。本文聚焦于**BQ25713**这款经典充电管理芯片,深入剖析其**ChargeOption**、**ProchotOpti

weixin_42713608的博客 286

Java十大经典排序算法详解:手撕代码+性能对比,面试必备

排序算法是程序员的“内功心法”,无论是面试还是实际开发,都绕不开这一基础话题。本文将用,详解五大经典排序算法(冒泡、归并、快速、基数、堆排序),!文末附,帮你快速选择最优方案!冒泡排序(Bubble Sort)归并排序(Merge Sort)快速排序(Quick Sort)基数排序(Radix Sort)堆排序(Heap Sort)算法对比与总结从第一个元素开始,依次比较相邻元素。若当前元素 > 下一个元素,则交换位置。每轮结束后,最大的元素“冒泡”到末尾。重复上述步骤,直到所有元素有序。

weixin_38078308的博客 842

Cass三角网法土方计算优化技巧:如何根据地形特点调整边界采样间距

本文深入探讨了Cass软件中三角网法(DTM法)土方计算的优化技巧,核心在于如何根据地形的复杂程度动态调整边界采样间距。针对平坦、复杂及人工构造物等不同地形特征,提供了具体的间距调整策略与精度验证方法,旨在帮助工程师在计算精度与效率间找到最佳平衡,实现精细化土方计算。

weixin_29294975的博客 525

【java】十大经典排序算法(动图演示)

十大经典排序算法(动图演示) 0,算法概述 0.1算法分类 十种常见排序算法可以分为两大类: 非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序。 线性时间非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此称为线性时间非比较类排序。  0.2算法复杂度 ...

csdn_baotai的博客 1万+

【教程4>第1章>第8节】ILA在线调试工具与MATLAB联合应用

Vivado的ILA(Integrated Logic Analyzer)核是一种可定制的集成逻辑分析仪内核,专门用于监控设计的内部信号。ILA内核包含了现代逻辑分析仪的许多高级功能,例如布尔触发方程和边沿转换触发等。此外,由于ILA内核与被监控的设计保持同步,因此应用于设计的所有设计时钟约束也会应用于ILA内核内部的组件。ILA核的主要应用是在硬件设计和验证阶段进行调试。通过监控关键信号,设计师可以了解硬件的实际行为,从而找出并修复设计中的错误。ILA核还可以用于分析设计的性能。

FPGA/MATLAB学习教程/源码/项目合作开发 801

大模型驱动电费核算智能升级:从人工调参到智能进化的规则优化新范式

大模型技术为电费核算规则优化提供了全新的解决方案,通过构建数据驱动的规则智能优化闭环,实现了从人工调参到智能进化的规则优化新范式。这一解决方案不仅有效提高了电费核算的效率和准确性,还降低了人工成本,提升了客户满意度。随着大模型技术的不断发展和应用,电费核算业务将迎来更加智能化、高效化的未来。

getusushu的博客 36

十大经典排序算法(动图演示C++实现)

参考:https://www.cnblogs.com/onepixel/articles/7674659.html 十大经典排序算法(动图演示C++实现) 0、算法概述 0.1 算法分类 十种常见排序算法可以分为两大类: (选泡插,快归堆希统计基) 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。 非比较类排...

zhangsy_csdn的博客 1万+

华为OD机试真题 新系统【进制转换后自定义排序】

华为OD机试 新系统真题【进制转换后自定义排序】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题【进制转换后自定义排序】多语言题解。

qq_45776114的博客 598

十大经典排序算法(动图演示)

0.1 算法分类比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。0.2 算法复杂度0.3 相关概念稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。时间复杂度:对排序数据的总的操作次数。

2255

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

十大经典排序算法详解(动图演示+代码实现)

排序算法总览 十大排序算法可以分为比较类排序以及非比较类排序。 **比较类排序:**通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn)O(nlogn)O(nlogn),因此也称为非线性时间比较类排序。 **非比较类排序:**不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。 排序算法可以分为内部排序和外部...

Hello World 2078

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

算法 - 十大经典排序算法(动图演示)

[TOC] 算法 - 十大经典排序算法(动图演示) ​ 在计算机科学与数学中,一个排序算法(英语:Sorting algorithm)是一种能将一串资料依照特定排序方式进行排列的一种算法。最常用到的排序方式是数值顺序以及字典顺序。排序算法也用在处理文字资料以及产生人类可读的输出结果。 基本上,排序算法的输出必须遵守下列两个原则: 输出结果为递增序列(递增是针对所需的排序顺序而言) 输出结果是原输入的一种排列或是重组 算法基本介绍 十种排序算法一般分为两大类: 比较类排序:通过比较来决定元素间的相对次

u012179233的博客 3053

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

十大经典排序算法Java版(动图演示)

文章目录0 排序算法说明0.1 内部排序和外部排序0.2 比较类排序和非比较类排序0.3 关于时间复杂度0.4 关于稳定性0.5 名词解释:1 交换排序——冒泡排序(Bubble Sort)1.1 什么时候最快1.2 什么时候最慢1.3 算法步骤1.4 动图演示1.5 Java实现2 选择排序——直接选择排序(Selection Sort)2.1 算法步骤2.2 动图演示2.3 Java实现3 插...

Kahcc的博客 2613

排序算法—交换排序(冒泡、快速)(动图演示)

本篇分享十大排序算法中的 需要进行交换操作的与, 其余算法也有介绍噢(努力赶进度中,后续会添加上)

2401_83177222的博客 1399
上一篇: 【数论知识】2:欧拉函数
下一篇: 【机器学习基础】(一) 爬山算法 ( Hill Climbing )与模拟退火(SA,Simulated Annealing)
Andy in boots
博客等级 码龄7年 73粉丝 69原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值