数据结构和排序---9种常规排序

本文详细介绍了9种常见的排序算法,包括冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序、堆排序以及基数排序。针对每种排序算法,文章不仅阐述了其基本思想,还提供了实现细节和复杂度分析,帮助读者深入理解各种排序方法的工作原理和适用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

冒泡排序BubbleSort【O(N2)】:依次比较相邻两元素,若前一元素大于后一元素则交换之,直至最后一个元素即为最大
				一共重复n-1轮,第i轮做到最后倒数第i个成员为最大/小值
插入排序【O(N2)】:最初x个数字作为数组 数组后面的第一个数字进行插入→得到x+1的有序数组    迭代→整体变成有序数组
选择排序【O(N2)】:每次找到余下全体最小值的索引值,放到(替换)到第i个位置  每次确定更新一个位置

快排Quicksort【O(NlogN)--分治法】:从两侧分别找一个较大、较小的数字并对调 直至左右搜索指针位于同一个位置 +【迭代】
		1.先从数列中取出一个数作为基准数。
		2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
		3.再对左右区间重复第二步,直到各区间只有一个数。  

堆排序HeapSort【O(NlogN) 】:想构建最大堆→根节点和最后一个数字交换【沉底==确认】→不符合→调整至符合最大二叉堆  这样就确定一个数据了
归并排序Merge Sort【O(NlogN)】:将数组迭代拆分成2个有序数组→合并成一个有序数组【内部迭代直至以2个数据进行归并】

桶排【O(N+NlogN)】:	1、简单:几种情况就几个桶 
					2、复杂:每个桶负责一个区域 将所有的数据放入个个桶中→每个桶内部进行排序
基数排序【O(M+N)】: 桶排的升级版 10个桶0~9 
					先按照个位进行分桶→序列【个位有序】→然后按照十位分桶→合并成序列【十位有序】→以此处理
希尔排序【O(N3/2) 】:按照【间隔进行分组】进行【插入排序】 
					间隔逐渐变小【最初为数组元素数量的一半,之后为上一次的一半 求商】→当为1时进行一次全局的插入排序

在这里插入图片描述

在这里插入图片描述

1、冒泡排序BubbleSort

//java代码实现:
public static void BubbleSort(int[] arr){
     int temp;//临时变量
     for(int i=0; i<arr.length-1; i++){   //表示趟数,一共arr.length-1次。
         for(int j=arr.length-1; j>i; j--){

             if(arr[j] < arr[j-1]){
                 temp = arr[j];
                 arr[j] = arr[j-1];
                 arr[j-1] = temp;
             }
         }
     }
 }

优化:数据的顺序排好之后,冒泡算法仍然会继续进行下一轮的比较,直到arr.length-1次,后面的比较没有意义的。
方案:
设置标志位flag,如果发生了交换flag设置为true;如果没有交换就设置为false。
这样当一轮比较结束后如果flag仍为false,即:这一轮没有发生交换,说明数据的顺序已经排好,没有必要继续进行下去。

2、选择排序(SelctionSort)

3、插入排序(Insertion Sort)

4、希尔排序(Shell Sort)

数据序列1: 13-17-20-42-28 利用插入排序,13-17-20-28-42. Number of swap:1;
数据序列2: 13-17-20-42-14 利用插入排序,13-14-17-20-42. Number of swap:3;
如果数据序列基本有序,使用插入排序会更加高效。

基本思想:
在要排序的一组数中,根据某一增量分为若干子序列,并对子序列分别进行插入排序。
然后逐渐将增量减小,并重复上述过程。直至增量为1,此时数据序列基本有序,最后进行插入排序。
在这里插入图片描述

public static void shell_sort(int array[],int lenth){
   int temp = 0;
   int incre = lenth;
   while(true){
       incre = incre/2;//最初以2个数一组进行插入排序,然后以4个数一组进行插入排序,然后8 16 32.....
       					//这里的incre表示一组数中相邻的数据的序号的差值
       for(int k = 0;k<incre;k++){    //根据增量分为若干子序列
           for(int i=k+incre;i<lenth;i+=incre){
               for(int j=i;j>k;j-=incre){
                   if(array[j]<array[j-incre]){
                       temp = array[j-incre];
                       array[j-incre] = array[j];
                       array[j] = temp;
                   }else{
                       break;
                   }
               }
           }
       }
       if(incre == 1){
           break;
       }
   }
}

5、快速排序(Quicksort)

基本思想:(分治)
1、先从数列中取出一个数作为key值;
2、将比这个数小的数全部放在它的左边,大于或等于它的数全部放在它的右边;
3、对左右两个小数列重复第二步,直至各区间只有1个数。

//分治法的代码
//快速排序
void quick_sort(int s[], int l, int r){
    if (l < r){
        //Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 参见注1
        int i = l, j = r, x = s[l];    先将第一个坑空出来,用于存放右起找到的大于x的数(第一个左侧的坑)
        while (i < j){
            while(i < j && s[j] >= x) // 从右向左找第一个小于x的数
                j--;  
            if(i < j) 
                s[i++] = s[j];		右起找到的<x的数就放到左侧的坑,空出一个右侧的坑
                
            while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数
                i++;  
            if(i < j) 
                s[j--] = s[i];		左起找到的>x的数就放到右侧的坑,空出一个左侧的坑
        }
        s[i] = x;
        quick_sort(s, l, i - 1); // 递归调用 
        quick_sort(s, i + 1, r);
    }
}

大于&小于基准数的数据分组的2种实现方法:
1、挖坑填数及实现代码
 1.i =L; j = R; 将基准数挖出形成第一个坑a[i]。
 2.j–由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中。
 3.i++由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中。
 4.再重复执行2,3二步,直到i==j,将基准数填入a[i]中。
2、从右往左&从左往右找大于&小于基准数的一对数据并交换

6、归并排序(Merge Sort)

//将有二个有序数列a[first...mid]和a[mid...last]合并。
void mergearray(int a[], int first, int mid, int last, int temp[])
{
    int i = first, j = mid + 1;
    int m = mid,   n = last;
    int k = 0;
    
    while (i <= m && j <= n)
    {
        if (a[i] <= a[j])
            temp[k++] = a[i++];
        else
            temp[k++] = a[j++];
    }
    
    while (i <= m)
        temp[k++] = a[i++];
    
    while (j <= n)
        temp[k++] = a[j++];
    
    for (i = 0; i < k; i++)
        a[first + i] = temp[i];
}
void mergesort(int a[], int first, int last, int temp[])
{
    if (first < last)
    {
        int mid = (first + last) / 2;		当分组的数据量≥2时,分组并归并
        mergesort(a, first, mid, temp);    //左边有序
        mergesort(a, mid + 1, last, temp); //右边有序
        mergearray(a, first, mid, last, temp); //再将二个有序数列合并
    }
}
 
bool MergeSort(int a[], int n)
{
    int *p = new int[n];		这里要准备一个同样长度的数组用于存放单次合并后的数组饭后复制给原数组
    if (p == NULL)
        return false;
    mergesort(a, 0, n - 1, p);
    delete[] p;
    return true;
}

在这里插入图片描述

7、堆排序(HeapSort):利用堆结构

实现:
1、将无序序列重排成最大堆(最小堆)
while{
 2、堆根节点放至队列结尾(已确定)
 3、其余未确定节点重新排序成最大堆(最小堆)
}
实现代码

复杂度分析:
因为堆排序无关乎初始序列是否已经排序已经排序的状态,始终有两部分过程,构建初始的大顶堆的过程时间复杂度为O(n),交换及重建大顶堆的过程中,需要交换n-1次,重建大顶堆的过程根据完全二叉树的性质,[log2(n-1),log2(n-2)…1]逐步递减,近似为nlogn。所以它最好和最坏的情况时间复杂度都是O(nlogn),空间复杂度O(1)。

8、基数排序(RadixSort)—特殊的桶排

桶排序的桶(数组)数量不确定
基数排序的桶(数组)数量确定=10

1、第一轮:按照个位的值,将所有数据放至10个桶中
2、第二轮:按照十位的值,将10个桶的数据一次放入10个新的桶(先处理个位值较小的桶)👉在一个十位桶中,各个元素按照个位从小到大排序
3、第三轮:按照百位的值:…
4、…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值