前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >排序之简单排序

排序之简单排序

作者头像
Rochester
发布于 2020-09-01 03:12:46
发布于 2020-09-01 03:12:46
40500
代码可运行
举报
文章被收录于专栏:牛人NR牛人NR
运行总次数:0
代码可运行

简单排序

1. Comparable接口介绍

在元素之间进行比较,而Java提供了一个接口Comparable就是用来定义排序规则的。

需求:

1.定义一个学生类Student,具有年龄age和姓名username两个属性,并通过Comparable接口提供比较规则;

2.定义测试类Test,在测试类Test中定义测试方法Comparable getMax(Comparable c1,Comparable c2)完成测试。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package cn.silentcow.comparable;

/**
 * 学生类
 * @author silentCow
 * @Date 2020/8/9 8:44
 */
public class Student implements Comparable<Student>{

    private int age;
    private String name;

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public Student() {
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

    /**
     * 定义比较规则
     * @param o
     * @return
     */
    @Override
    public int compareTo(Student o) {
        return this.getAge() - o.getAge();
    }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package cn.silentcow.comparable;

/**
 * 测试类
 * @author silentCow
 * @Date 2020/8/9 8:45
 */
public class StudentTest {

    public static void main(String[] args) {
        // 创建Student对象,并调用getMax方法,进行测试
        Student stu1 = new Student(15, "张三");
        Student stu2 = new Student(16, "李四");

        Comparable max = getMax(stu1, stu2);
        System.out.println(max);
    }

    public static Comparable getMax(Comparable c1,Comparable c2) {
        int result = c1.compareTo(c2);
        // 如果 result < 0,则 c1 比 c2 小;
        // 如果 result = 0,则 c1 和 c2 相等;
        // 如果 result > 0,则 c1 比 c2 大;
        if (result >= 0) {
            return c1;
        } else {
            return c2;
        }
    }

}

2. 冒泡排序

冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。

需求:

排序前:{4,5,6,3,2,1}

排序后:{1,2,3,4,5,6}

排序原理:

  1. 比较相邻的元素。如果前一个元素比后一个元素大,就交换这两个元素的位置。
  2. 对每一对相邻元素做同样的工作,从开始第一对元素到结尾的最后一对元素。最终最后位置的元素就是最大值

冒泡排序API设计:

代码实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package cn.silentcow.comparable;

/**
 * @author silentCow
 * @Date 2020/8/9 9:11
 */
public class Bubble {

    //对数组a中的元素进行排序
    public static void sort(Comparable[] a) {
        for (int i = a.length - 1; i > 0; i--) {
            for (int j = 0; j < i; j++) {
                if (greater(a[j], a[j + 1])) {
                    exch(a, j, j + 1);
                }
            }
        }
    }


    //比较v元素是否大于w元素
    private static boolean greater(Comparable v, Comparable w) {
        return v.compareTo(w) > 0;
    }

    // 数组元素i和j交换位置
    private static void exch(Comparable[] a, int i, int j) {
        Comparable t = a[i];
        a[i] = a[j];
        a[j] = t;
    }

}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package cn.silentcow.test;

import cn.silentcow.comparable.Bubble;

import java.util.Arrays;

/**
 * @author silentCow
 * @Date 2020/8/9 9:14
 */
public class SortTest {
    public static void main(String[] args) {
        Integer[] a = {4, 5, 6, 3, 2, 1};
        Bubble.sort(a);
        System.out.println(Arrays.toString(a));
    }
}

冒泡排序的时间复杂度分析 冒泡排序使用了双层for循环,其中内层循环的循环体是真正完成排序的代码,所以,我们分析冒泡排序的时间复杂度,主要分析一下内层循环体的执行次数即可。

在最坏情况下,也就是假如要排序的元素为{6,5,4,3,2,1}逆序,那么:

元素比较的次数为:

(N-1)+(N-2)+(N-3)+…+2+1=((N-1)+1)*(N-1)/2=N^2/2-N/2;

元素交换的次数为:

(N-1)+(N-2)+(N-3)+…+2+1=((N-1)+1)*(N-1)/2=N^2/2-N/2;

总执行次数为:

(N^2/2-N/2)+(N^2/2-N/2)=N^2-N;

按照大O推导法则,保留函数中的最高阶项那么最终冒泡排序的时间复杂度为O(N^2).

3. 选择排序

选择排序是一种更加简单直观的排序方法。

需求:

排序前:{4,6,8,7,9,2,10,1}

排序后:{1,2,4,5,7,8,9,10}

排序原理:

1.每一次遍历的过程中,都假定第一个索引处的元素是最小值,和其他索引处的值依次进行比较,如果当前索引处的值大于其他某个索引处的值,则假定其他某个索引出的值为最小值,最后可以找到最小值所在的索引

2.交换第一个索引处和最小值所在的索引处的值

选择排序API设计:

类名

Selection

构造方法

Selection():创建Selection对象

成员方法

1.public static void sort(Comparable[] a):对数组内的元素进行排序 2.private static boolean greater(Comparable v,Comparable w):判断v是否大于w 3.private static void exch(Comparable[] a,int i,int j):交换a数组中,索引i和索引j处的值

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package cn.silentcow.comparable;

/**
 * @author silentCow
 * @Date 2020/8/9 12:34
 */
public class Selection {

    //对数组a中的元素进行排序
    public static void sort(Comparable[] a) {
        for (int i = 0; i <= a.length - 2; i++) {
            //假定本次遍历,最小值所在的索引是i
            int minIndex = i;
            for (int j = i + 1; j < a.length; j++) {
                if (greater(a[minIndex], a[j])) {
                    //跟换最小值所在的索引
                    minIndex=j;
                }
            }
            //交换i索引处和minIndex索引处的值
            exch(a, i, minIndex);
        }
    }

    //比较v元素是否大于w元素
    private static boolean greater(Comparable v, Comparable w) {
        return v.compareTo(w) > 0;
    }

    //数组元素i和j交换位置
    private static void exch(Comparable[] a, int i, int j) {
        Comparable t = a[i];
        a[i] = a[j];
        a[j] = t;
    }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package cn.silentcow.test;

import cn.silentcow.comparable.Selection;

import java.util.Arrays;

/**
 * @author silentCow
 * @Date 2020/8/9 12:35
 */
public class SelectionTest {

    public static void main(String[] args) {
        Integer[] a = {4, 6, 8, 7, 9, 2, 10, 1};
        Selection.sort(a);
        System.out.println(Arrays.toString(a));
    }

}

选择排序的时间复杂度分析:

选择排序使用了双层for循环,其中外层循环完成了数据交换,内层循环完成了数据比较,所以我们分别统计数据交换次数和数据比较次数:

数据比较次数:

(N-1)+(N-2)+(N-3)+…+2+1=((N-1)+1)*(N-1)/2=N^2/2-N/2;

数据交换次数:

N-1

时间复杂度:N^2/2-N/2+(N-1)=N^2/2+N/2-1;

根据大O推导法则,保留最高阶项,去除常数因子,时间复杂度为O(N^2);

4. 插入排序

插入排序(Insertion sort)是一种简单直观且稳定的排序算法

需求:

排序前:{4,3,2,10,12,1,5,6}

排序后:{1,2,3,4,5,6,10,12}

排序原理:

1.把所有的元素分为两组,已经排序的和未排序的;

2.找到未排序的组中的第一个元素,向已经排序的组中进行插入;

3.倒叙遍历已经排序的元素,依次和待插入的元素进行比较,直到找到一个元素小于等于待插入元素,那么就把待插入元素放到这个位置,其他的元素向后移动一位;

插入排序API设计:

插入排序代码实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package cn.silentcow.comparable;

/**
 * @author silentCow
 * @Date 2020/8/9 15:08
 */
public class Insertion {

    // 对数组a中的元素进行排序
    public static void sort(Comparable[] a) {

        for (int i = 0; i < a.length; i++) {
            //当前元素为a[i],依次和i前面的元素比较,找到一个小于等于a[i]的元素
            for (int j = i; j > 0; j--) {
                if (greater(a[j - 1], a[j])) {
                    // 交换元素
                    exch(a, j - 1, j);
                } else {
                    // 找到了该元素,结束
                    break;
                }
            }
        }

    }

    //比较v元素是否大于w元素
    private static boolean greater(Comparable v, Comparable w) {
        return v.compareTo(w) > 0;
    }

    // 数组元素i和j交换位置
    private static void exch(Comparable[] a, int i, int j) {
        Comparable t = a[i];
        a[i] = a[j];
        a[j] = t;
    }

}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package cn.silentcow.test;

import cn.silentcow.comparable.Insertion;

import java.util.Arrays;

/**
 * @author silentCow
 * @Date 2020/8/9 15:08
 */
public class InsertionTest {
    public static void main(String[] args) {
        Integer[] arr = {4, 3, 2, 10, 12, 1, 5, 6};
        Insertion.sort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

插入排序的时间复杂度分析

插入排序使用了双层for循环,其中内层循环的循环体是真正完成排序的代码,所以,我们分析插入排序的时间复杂度,主要分析一下内层循环体的执行次数即可。

最坏情况,也就是待排序的数组元素为{12,10,6,5,4,3,2,1},那么:

比较的次数为:

(N-1)+(N-2)+(N-3)+…+2+1=((N-1)+1)*(N-1)/2=N^2/2-N/2;

交换的次数为:

(N-1)+(N-2)+(N-3)+…+2+1=((N-1)+1)*(N-1)/2=N^2/2-N/2;

总执行次数为:

(N^2/2-N/2)+(N^2/2-N/2)=N^2-N;

按照大O推导法则,保留函数中的最高阶项那么最终插入排序的时间复杂度为O(N^2).

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-08-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
进程组、会话、控制终端概念,如何创建守护进程?
守护进程,也就是通常所说的Daemon进程,是Linux中的后台服务进程。周期性的执行某种任务或等待处理某些发生的事件。
睡魔的谎言
2020/11/25
1.5K0
什么是守护进程?
在了解守护进程之前,需要先知道什么是什么是终端?什么是作业?什么是进程组?什么是会话?
全栈程序员站长
2022/09/07
1.1K0
Linux内核编程--进程组和守护进程
进程组:进程组是多个进程的集合, 接收同一个终端的各类信号信息。进程调用setpgid(pid, pgid)可以加入一个现有的进程组或者创建一个新的进程组。
Coder-ZZ
2022/05/09
3K0
Linux内核编程--进程组和守护进程
Linux 守护进程|应急响应
通常我们都是通过以上两种方式来获得一个shell,之后运行程序的,此时我需要纠正一个概念,我们通常都说获得一个shell,本质上来说,我们获取了一个session(会话,以下session都是会话)
意大利的猫
2021/03/18
3.9K0
Linux 守护进程|应急响应
linux系统编程之进程(五):终端、作业控制与守护进程
该文介绍了如何在Linux系统中通过fork函数创建守护进程,并给出了具体的示例代码。同时,文章还介绍了守护进程的一些常见用途,如保证程序在后台运行、处理控制台输入输出等。
s1mba
2018/01/03
2.7K0
linux系统编程之进程(五):终端、作业控制与守护进程
【计算机网络】日志与守护进程
一般使用cout进行打印,但是cout打印是不规范的 实际上 是采用日志进行打印的
lovevivi
2023/11/17
1820
【计算机网络】日志与守护进程
【在Linux世界中追寻伟大的One Piece】进程间关系与守护进程
其实每一个进程除了有一个进程ID(PID)之外,还属于一个进程组。进程组是一个或者多个进程的集合, 一个进程组可以包含多个进程。 每一个进程组也有一个唯一的进程组ID(PGID), 并且这个 PGID类似于进程ID, 同样是一个正整数, 可以存放在pid_t数据类型中。
枫叶丹
2024/09/24
610
【在Linux世界中追寻伟大的One Piece】进程间关系与守护进程
Linux守护进程
进程组,也叫做作业。BSD于1980年前后向Unix中增加的一个新特性,代表一个或者多个进程的集合,每个进程都属于一个进程组。操作系统设计进程组的概念主要就是为了简化对多个进程的管理。
mindtechnist
2024/08/08
1930
Linux守护进程
守护进程「建议收藏」
在UNIX系统中, 用户通过终端登录系统后得到一个Shell进程, 这个终端成为Shell进程的控制终端(Controlling Terminal), 进程中, 控制终端是保存在PCB中的信息, 而fork会复制PCB中的信息, 因此由Shell进程启动的其它进程的控制终端也是这个终端. 默认情况下(没有重定向), 每个进程的标准输入, 标准输出和标准错误输出都指向控制终端, 进程从标准输入读也就是读用户的键盘输入, 进程往标准输出或标准错误输出写也就是输出到显示器上. 信号中还讲过, 在控制终端输入一些特殊的控制键可以给前台进程发信号, 例如Ctrl-C表示SIGINT,Ctrl-\表示SIGQUIT。
全栈程序员站长
2022/09/16
5980
13(守护进程)
守护进程是一种纯粹的后台进程,与运行前环境完全隔离,包括未关闭的文件描述符、控制终端、会话、进程组、工作目录以及文件创建掩码等 很多守护进程是父进程 fork 产生,所以会继承所有的父进程地址空间中的环境,所以必须在守护进程诞生之初,断绝这些相关环境,当然,守护进程也可以在 linux 系统启动时从启动脚本 /etc/rc.d 中启动,也可以由 crontab 启动 事实上,守护进程与普通进程的编写并没有特别大的区别
提莫队长
2019/02/21
8030
Python守护进程daemon实现
守护进程是系统中生存期较长的一种进程,常常在系统引导装入时启动,在系统关闭时终止,没有控制终端,在后台运行。守护进程脱离于终端是为了避免进程在执行过程中的信息在任何终端上显示并且进程也不会被任何终端所产生的终端信息所打断。 在这里,我们在Linux2.6内核的centos中,ps -ef |awk '{print $1"\t "$2"\t "$3"\t  "$8}'看到:PPID=0的进程有两个,分别是PID=1的/sbin/init进程和PID=2的[kthreadd]进程。
py3study
2020/01/07
7.7K0
Linux守护进程
守护进程在 Linux 系统中极为重要,它们是许多服务器的核心组成部分,例如 Internet 服务器 inetd 和 Web 服务器 httpd。这些进程不仅负责提供网络服务,还执行各种系统任务,例如作业调度进程 crond。
不脱发的程序猿
2024/11/26
1770
Linux守护进程
Linux - 请允许我静静地后台运行
枕边书
2018/01/04
1.7K0
Linux - 请允许我静静地后台运行
守护进程
在Linux中,session(会话)通常指的是与用户交互的一个环境,它是系统中与某个用户交互的一系列活动的集合。会话在Linux系统中有多种用途,下面是几种常见的会话类型及其相关概念:
ljw695
2025/01/03
780
守护进程
python 守护进程(daemon)
通常,我们执行服务端程序的时候都会通过终端连接到服务器,成功连接后会加载shell环境,终端盒shell都是进程,shell进程是终端进程的子进程,通过ps命令可以很容易的查看到,在这个shell环境下一开始执行的程序都是shell进程的子进程,自然会受到shell进程的影响,在程序里fork子进程后,父进程退出,对于shell进程来说,这个父进程就算执行完毕,而产生的子进程会被init进程接管,从而也就脱离了终端控制。
py3study
2020/01/10
1.1K0
守护进程Xinted和日志记录Syslogd
调用fork函数创建子进程后,使父进程立即退出。这样,产生的子进程将变成孤儿进程,并被init进程接管,同时,所产生的新进程将变为在后台运行。
星哥玩云
2022/07/04
8920
Python实现守护进程
專 欄 ❈汤英康,Python程序员,负责设计和开发大数据监控平台的相关产品。 PyCon China2016 深圳 讲师。 博客:http://blog.tangyingkang.com/ ❈— Daemon场景 考虑如下场景:你编写了一个python服务程序,并且在命令行下启动,而你的命令行会话又被终端所控制,python服务成了终端程序的一个子进程。因此如果你关闭了终端,这个命令行程序也会随之关闭。 要使你的python服务不受终端影响而常驻系统,就需要将它变成守护进程。 守护
Python中文社区
2018/01/31
2K0
PHP中的会话
2、当执行php xxx.php 时,默认系统会把当前的进程设置为会话首进程(使用strace查看),所以当前会话首进程不能使用posix_setsid 创建为会话首进程,只能使用子进程调用此函数
北溟有鱼QAQ
2021/06/08
1.2K0
Linux之守护进程理解(2)
1、屏蔽一些有关控制终端操作的信号 防止在守护进程没有正常运转起来时,控制终端受到干扰退出或挂起。 2、脱离控制终端,登录会话和进程组 登录会话可以包含多个进程组,这些进程组共享一个控制终端,这个控制终端通常是创建进程的登录终端。控制终端,登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们,使之不受它们的影响。 其方法是在fork()的基础上,调用setsid()使进程成为会话组长。调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离,由于会话过程对控制终端的独占性,进程同时与控制终端脱离。 setsid()实现了以下效果: (a) 成为新对话期的首进程 (b) 成为一个新进程组的首进程 (c) 没有控制终端。 3、禁止进程重新打开控制终端 现在,进程已经成为无终端的会话组长,但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端,再fork()一次。 4、关闭打开的文件描述符 进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在地文件系统无法卸下以及无法预料的错误。一般来说, 必要的是关闭0、1、2三个文件描述符,即标准输入、标准输出、标准错误。因为我们一般希望守护进程自己有一套信息输出、输入的体系,而不是把所有的东西 都发送到终端屏幕上。 5、改变当前工作目录 将当前工作目录更改为根目录。从父进程继承过来的当前工作目录可能在一个装配的文件系统中。因为守护进程通常在系统重启之前是一直存在的,所以如果守护进程的当前工作目录在一个装配文件系统中,那么该文件系统就不能被拆卸。 另外,某些守护进程可能会把当前工作目录更改到某个指定位置,在此位置做它们的工作。例如,行式打印机假脱机守护进程常常将其工作目录更改到它们的spool目录上。 6、重设文件创建掩码 将文件方式创建屏蔽字设置为0:umask(0)。 由继承得来的文件方式创建的屏蔽字可能会拒绝设置某些许可权。例如,若守护进程要创建一个组可读、写的文件,而继承的文件方式创建屏蔽字,屏蔽了这两种许可权,则所要求的组可读、写就不能起作用。 7、处理SIGCHLD信号 处理SIGCHLD信号并不是必须的。但对于某些进程, 特别是服务器进程往往在请求到来时fork子进程出来处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)而仍占用系统资源。如 果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在系统V下可以简单地将SIGCHLD信号的操作设为SIG_IGN,即忽略掉。这样,内核在子进程结束时不会产生僵尸进程,这一点与BSD4不同,在BSD4下必须显示等待子进程结束才能释放僵尸进程。 8、记录信息 在Linux/Unix下有个syslogd的守护进程,向用户提供了syslog()系统调用。任何程序都可以通过syslog记录事件。  源码实现及分析:
chain
2018/08/02
2.6K0
守护进程
守护进程(daemon)是一类在后台运行的特殊进程,用于执行特定的系统任务。很多守护进程在系统引导的时候启动,并且一直运行直到系统关闭。另一些只在需要的时候才启动,完成任务后就自动结束。
zy010101
2020/09/02
1.9K0
相关推荐
进程组、会话、控制终端概念,如何创建守护进程?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文