初级排序算法

本文深入解析了冒泡排序、选择排序、插入排序和希尔排序四种初级排序算法的实现原理及时间复杂度分析。通过对比不同排序算法的特点,帮助读者理解算法的优缺点,包括稳定性、最坏情况和最好情况下的时间复杂度。

初级排序算法

模板:定义了基本的辅助函数

传入了实现了Comparable接口的数据类型的数组

class Sort{
    public void sort(Comparable [] a){}
    public  static boolean less(Comparable v,Comparable w){
        return v.compareTo(w)<0;
    }
    public  static void exch(Comparable[] a,int i ,int j){
        Comparable t = a[i];
        a[i] = a[j];
        a[j] = t;
    }
    public static void show(Comparable []a ){
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i]+" ");
        }
        System.out.println();
    }
}

冒泡排序

实现

重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。

时间复杂度分析

若数组的初始状态是正序的,一趟扫描既可以完成排序,所需的比较次数C和记录移动次数M均达到最小值,Cmin = n-1,Mmin=0,因此冒泡排序的时间复杂度为O(n)

若数组的初始状态为反序的,需要进行n-1趟排序,每趟排序要进行n-i次关键字的比较,且每次都需要三次对记录的交换来交换位置,这种情况下,比较和移动的次数均达到最大值

Cmax = n(n−1)2\frac{n(n-1)}{2}2n(n1) = O(n2)

Mmax = 3n(n−1)2\frac{3n(n-1)}{2}23n(n1) = O(n2)

冒泡排序的最坏时间复杂度为O(n2)

稳定性:由于不换交换相等的数据,因此冒泡排序是稳定的排序

class BubbleSort extends Sort{
    public void sort (Comparable [] a) {
        int N = a.length;
        boolean flag = false;//判断本次冒泡是否有交换数据
        for(int i = 0;i<N;i++){
            flag = false;//重置为没有交换
            for(int j = 0;j<N-1-i;j++){//每次循环最后一个数据都为最大的数字
                if(a[j].compareTo(a[j+1])>0){
                    exch(a,j,j+1);
                    flag = true;
                }
            }
            if(flag == false){//如果这次排序没有调换数据,证明序列已经有序
                return;
            }
        }
    }
}

选择排序

实现:

第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。

时间复杂度分析:

选择的比较次数O(n2),总的比较次数为N=(n-1)+(n-2)+…+1 = n∗(n−1)2\frac{n*(n-1)}{2}2n(n1)

交换次数是O(n)

最好情况:已经有序,交换0次;最坏情况交换n-1次,逆序交换n2\frac{n}{2}2n次,交换次数比冒泡排序少

**稳定性:**不稳定排序,在交换的时候,可能将相等的值往后面交换。例如58529,这种排序,选择排序会破坏算法的稳定性。

class Selection extends Sort{
//    public Selection(){}
    public void sort(Comparable [] a){
        int N = a.length;
        for (int i = 0; i < N; i++) {
            int min = i;
            for (int j = i+1; j < N; j++) {
                //寻找最小的数
                if(less(a[j],a[min]))min = j;
            }
            //将最小的数与遍历的下标交换
            exch(a,i,min);
        }
    }
}

插入排序

思路:

每步将一个待排序的记录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。

时间复杂度分析:

如果数组最开始是有序的,那么比较次数为Cmin=n-1,移动次数Mmin=0

如果数组最开始是无序的,那么复制度为O(n2)

Cmax=(n-1)+(n-2)+…+1=n∗(n−1)2\frac{n*(n-1)}{2}2n(n1) = O(n2)

Mmax = 3n(n−1)2\frac{3n(n-1)}{2}23n(n1) = O(n2)

稳定性:

稳定算法,每次的交换都发生在一个数字和一个已经有序的数组中。

class Insertion extends Sort{
    public void sort(Comparable [] a) {
        int N = a.length;
        for (int i = 1; i < N; i++) {//将a[i]插入到前面的排好序的序列中,这个循环只是提供前面排好序的下标,真正进行交换的是下面一个循环
            //比较已经排序的前面的数组
            for (int j = i; j > 0; j--) {
                //从边界开始与已经有序的数组比较,如果比前面的小,一直交换,直到到达指定位置
                if(a[j].compareTo(a[j-1])<0)
                    exch(a,j,j-1);
            }
        }
    }

希尔排序

思路:

对于大规模的乱序数组的插入排序很慢,因为他们只会交换相邻的元素,因此元素只能一点一点的从数组的一端移动到另外一个端。希尔排序为了加快速度简单的改进了插入排序,交换不相邻的元素以对数组的局部进行排序,并最终插入排序将局部有序的数组排序。希尔排序的思想就是使数组中任意间隔为h的元素都是有序的。算法实现冲N/3开始递减至1

稳定性

不稳定,由于h有序数组,排序后相同的数字的顺序可能会被打乱

class Shell extends Sort{
    public void sort(Comparable[] a) {
        int N = a.length;
        int  h = 1;
        while(h<N/3) h = h * 3 + 1;//让h从N/3开始递减到1
        while (h >= 1) {
            //形成h有序数组,做法与插入排序相似,只是以h为间隔
            for (int i = h; i < N; i++) {
                for (int j = i; j >= h; j -= h) {
                    if(less(a[j],a[j-1]))
                        exch(a,j,j-1);
                }
            }
            h = h/3;//循环直到h为1,就是进行最后的插入排序了
        }
    }
}
### 初级 CSP 信奥赛排序算法题目 #### 题目描述: 在一个初级的信息学奥林匹克竞赛 (CSP) 中,有一道常见的题型就是编写简单的排序程序。例如: **【题目】** 给定一组整数序列,你需要将这组数据按照从小到大的顺序排列,并输出排好序的结果。 输入示例:`5\n4 8 2 9 6` 输出示例:`2 4 6 8 9` 这里给出了一种基础的解决思路——冒泡排序法(Bubble Sort),它是一种简单直观但是效率较低的经典排序算法之一。 --- ### 冒泡排序原理简介 1. **基本思想** 比较相邻元素大小并交换位置;一轮遍历结束后最大值会“浮”到最后一位; 2. **步骤说明** - 设数组长度为 n,则需要进行 n−1 轮比较操作。 - 第 i 趟从前往后依次两两比较第 i 和第 i+1 的数值, 如果前者大于后者则互换它们的位置。 3. **代码实现(C语言)** ```c #include <stdio.h> void bubbleSort(int arr[], int length){ for (int step = 0; step < length-1; ++step){ // 控制轮次 bool flag=false; for (int i=0;i<length-step-1;++i){ // 相邻两个数字逐一比较 if(arr[i]>arr[i+1]){ int temp=arr[i]; arr[i]=arr[i+1]; // 交换位置 arr[i+1]=temp; flag=true; } } if(!flag) break; // 若某趟未发生任何变化直接跳出循环提高性能 } } int main(){ int N; scanf("%d", &N); int nums[N]; for(int i = 0 ; i<N ;++i) scanf("%d",&nums[i]); bubbleSort(nums,N); printf("Sorted array: "); for (size_t i = 0; i < N; i++) printf("%d ", nums[i]); return 0; } ``` 这个例子演示了如何通过 C 程序读取用户输入的一串数字作为待处理的数据集,然后应用上面提到的基本冒泡排序逻辑对其进行升序整理后再打印出来。 --- 为了帮助初学者更好地理解和掌握这类问题,建议练习以下几个方面的知识点和技术点: - 学习其他常见排序方法如选择排序、插入排序等及其应用场景的区别分析。 - 掌握时间复杂度的概念以及计算公式用于评估各种排序方案之间的优劣对比。 - 尝试优化现有的解决方案,在保证正确性的前提下尽可能减少不必要的运算量达到更高效的执行效果。 - 练习更多的样例测试用例验证自己的程序是否能够稳定可靠地工作于所有合理的边界条件之下。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值