Java数据结构和算法(三)——简单排序

单单有数据还不够,对于数据的展现,经常要按照一定的顺序进行排列,越高要求的排序越复杂,这篇只介绍三种大小的简单排序。

1)冒泡排序

模拟:有数组,1,4,2,5,7,3。

(1)首先从1开始冒泡,1比4小,不冒,4大于2,冒上去,与2交换位置,4比5小,不冒,7比3大,冒,结果:1,2,4,5,3,7

(2)接下来从2开始,因为第一个元素冒过了,重复(1),结果:1,2,4,3,5,7

(3)从第三位4开始,结果1,2,3,4,5,7

(4)虽然看起来已经排好序,但是还是要继续冒,接下来就是从第4位开始,直到第五位元素冒完。

代码:

public class BubbleSort{
    public static void main(String[] args) {
        int a[] = {1,2,3,4,5,6,3,1,2,3 };
        for (int i = 0; i < a.length-1; i++) {
            for (int j = i; j < a.length; j++) {
                if(a[i]>a[j]){
                    int temp = a[j];
                    a[j] = a[i];
                    a[i]=temp;
                }
            }
        }
    }
}

复杂度:

第一次9次比较,第二次8次,最后1次,比较总数9+8+......+1,9!。

如果N个数据,那比较就是N!=N*(N-1)/2,交换次数大概为比较的一半,N*(N-1)/4,最坏的时候和比较次数一样。

去掉常数,时间复杂度O(N^2)。

2)选择排序

选择排序改进了冒泡排序,交换的次数,看清楚是交换的次数O(N^2)减少到了O(N)。为什么?

模拟:有数组,1,4,2,5,7,3。

(1)从第一个元素开始,假设1为数组的最小值,min=1(min存放的是最小元素的位置),用1开始和后面元素比较,如果还有比1小的值x,将x元素所在的位置赋值给min,比较之后发现1最小。这个时候,最小的值已经放在最左边了,进入(2)

(2)从第二个值4开始,假设min=2,与2对比,2更小,min=3,2再与5对比,还是2小,与7对比,2小,与3比,2小,最后min=3,同时将2和4替换位置。数组为:1,2,4,5,7,3。

(3)从第三个值4开始(上一步交换了),这次min=3,与4换位。数组为:1,2,3,4,5,7。

(4)从第四个值4开始,这次min是4了,以此类推。最后:1,2,3,4,5,7。

代码:

第一次敲的:

public class SelectSort{
    public static void main(String[] args) {
        int a[] = {1,2,3,4,5,6,3,1,2,3 };
        for (int i = 0; i < a.length-1; i++) {
            int min = i;
            int j =0;
            for ( j= i+1; j < a.length-1; j++) {
                if(a[i]>a[j]&&a[i]!=a[j]){
                    min = j;
                }
            }
            int temp = a[i];
            a[i] = a[min];
            a[j] = temp;
        }
        System.out.println(Arrays.toString(a));
    }
}

错漏百出。

第二次正确:

public class SelectSort{
    public static void main(String[] args) {
        int a[] = {1,2,3,4,5,6,3,1,2,3 };
        for (int i = 0; i < a.length-1; i++) {
            int min = i;
            for ( int j= i+1; j < a.length; j++) {
                if(a[j]<a[min]&&a[j]!=a[min]){
                    min = j;
                }
            }
            int temp = a[i];
            a[i] = a[min];
            a[min] = temp;
        }
        System.out.println(Arrays.toString(a));
    }
}

交换值那里和比较那里我写错了,本身比较的时候我们已经把min设定成i开始,那么是将min与j比,如果j更小,则min=j。然后,交换值的时候,和j也是没关系的,既然锁定了最小值的位置,只要和i交换即可。

算法有个可以优化地方,就是两者相等的时候是不用互换位置的。减少了一次赋值。

复杂度:

其实比较次数也是和冒泡一样——阶乘——N!=N*(N-1)/2。(N个元素)

但是交换的次数是少于N的,所以选择排序比冒泡快,当然元素达到一定数量级的时候,速度就体现出来了。

3)插入排序

在简单的排序这三种中最快,时间复杂度仍然为O(N)

这个写完代码再解释模拟过程:

代码:

public class InsertSort {
    public static void main(String[] args) {
        int[] a = {1,3,2,1,4,2,5,7,3};
        int mark,compare;
        for(mark = 1;mark < a.length;mark++ ){
            int temp = a[mark];
            compare = mark;
            while(a[compare-1]>temp&&compare-1>0){
                a[compare] = a[compare-1];
                compare--;
            }
            a[compare] =temp;
        }
        System.out.println(Arrays.toString(a));
    }
}

模拟:有数组,1,3,2,1,4,2,5,7,3。这个复杂一些,其实也不复杂。

(1)首先mark指向插入的位置,从数组第二个位置开始,temp值等于mark位置的元素值,往左比较,3大于1,while循环,a[mark]=temp,即3没有变化,下个for循环。

(2)mark=2,指向2,temp=2,往左,3大于temp,所以2的值替换3,compare

的值减一,即将1和temp比较,1小,跳出while循环,a[mark]=temp,即3的值变成2。

(3)数组现在为,1,2,3,1,4,2,5,7,3,mark=3,指向1,temp=1,往左,3大于1,a[compare],即a[3]=a[2]=3,再往左,compare减一,compare=2,2大于1,所以a[compare],a[2]=a[1]=2,此时为1,2,2,3,4,2,5,7,3。在往左,compare减一,a[0]=1,不移动,最后a[1]=temp=1,变成1,1,2,3,4,2,5,7,3。

(4)mark+1,继续循环,每次就是以mark为标志,向左比较大小,不停移动。直至最后。

复杂度:

比较次数看起来也是阶乘——N!=N*(N-1)/2,但其实每次插入点之后,插入点前面的数据就是有序的,所以,真正比较的只有一半左右——N!=N*(N-1)/4,

复制和比较的次数大致相等。虽然复杂度也是O(N^2)。

但是如果数组是1,2,3,4,5,6,7,8,7的话,也就是前面基本有序,那只有当mark等于9的时候才会比较,而且,就只和8交换而已。那这样的话时间复杂度只有O(N)。

所以说插入比冒泡快一倍,比选择排序快一些。

4)题外——计数排序

上面引用了另外一个博客链接,简单的三种排序复杂度都到了O(N^2),即使后面高级一些的排序也是要O(NlogN)。前段时间看题目发现竟然有O(N)复杂度的排序:现有n个小于100000的整数,写一个算法将这些数从小到大排序,要求时间复杂度O(n),空间复杂度O(1)。

原来就是用的计数排序。发现原来是算法导论里有的,果断翻书。

public class SelectSort{
    public static void main(String[] args) {
        int a[] = {1,2,3,4,4,4,3,1,2,3 };
        int c[] = new int[5];//c是用来存放每个数字出现次数的数组
        for (int i = 0; i < c.length; i++) {
            for (int j = 0; j < a.length; j++) {
                if(i == a[j])
                    c[i]++;
            }
        }
        System.out.println(Arrays.toString(c));
        //[0, 2, 2, 3, 2]
        for (int i = 1; i < c.length; i++) {
            c[i] = c[i] +c[i-1];
        }
        System.out.println(Arrays.toString(c));
        //[0, 2, 4, 7, 9]
        int[] b = new int[a.length];
        for (int i = 0; i < a.length; i++) {
            b[c[a[i]]-1] = a[i];
            c[a[i]]--;
        }
        System.out.println(Arrays.toString(b));
    }
}

算法设计得太巧妙了。

c数组为存储a数组中数字出现的个数,即c[0]表示a中0出现的个数,也正因为这样,所以c数组的长度要为a数组中最大元素+1。

然后:

for (int i = 1; i < c.length; i++) {
     c[i] = c[i] +c[i-1];
}

其实就是次数的累加,比如c[1]=c[1]+c[0],那么c[1]存的就是a数组出现0和1的个数,以此类推,c[2]存的就是小于等于2的个数。

for (int i = 0; i < a.length; i++) {
      b[c[a[i]]-1] = a[i];
      c[a[i]]--;
}

这里才是算法最美妙的地方。

i=0,a[0]=1,c[1]就是1以及0出现次数的地方,1以及0出现两次,既然没有0,那么1就是占据了第一位和第二位的位置(请仔细读这句话,这句话读懂了,整个算法就理解了)。然后我们就把其中的一个1放在1的第二位b[1]的位置,同时c[1]-1,因为我们已经排好了一个1了。

接下来,i=1,a[1]=2,c[2]是小于等于2的数字出现的个数,c[2]=4,那么要把它排在第四位,即b[3]的位置,同时c[2]-1=3,因为一个4已经排好序了,那么下次读到4的时候,他就是老三的位置了。

接下来就是不停的循环,刚开始看不懂算法设计者的用意。

其实次序与大小出现的次数之间的关系竟然是如此美妙。

如果要深入学习算法——《算法导论》是一本很好的书籍。

时间: 2024-10-06 02:18:20

Java数据结构和算法(三)——简单排序的相关文章

Java数据结构和算法(二)——数组

数组的用处是什么呢?--当你需要将30个数进行大小排列的时候,用数组这样的数据结构存储是个很好的选择,当你是一个班的班主任的时候,每次要记录那些学生的缺勤次数的时候,数组也是很有用.数组可以进行插入,删除,查找等. 1)创建和内存分配 Java中有两种数据类型,基本类型和对象类型,也有人称为引用类型,Java中把数组当成对象,创建数组时使用new操作符. int array[] = new int[10]; 既然是对象,那么array便是数组的一个引用,根据Java编程思想(一) -- 一切都是

Java数据结构和算法之数组与简单排序

一.数组于简单排序 数组 数组(array)是相同类型变量的集合,可以使用共同的名字引用它.数组可被定义为任何类型,可以是一维或多维.数组中的一个特别要素是通过下标来访问它.数组提供了一种将有联系的信息分组的便利方法. 一维数组 一维数组(one‐dimensional array )实质上是相同类型变量列表.要创建一个数组,你必须首先定义数组变量所需的类型.通用的一维数组的声明格式是: type var‐name[ ]; 获得一个数组需要2步: 第一步,你必须定义变量所需的类型. 第二步,你必

数据结构与算法分析之简单排序算法

在排序算法中,简单排序主要有三种,分别为冒泡排序.选择排序.插入排序,学习理解好这三种排序算法有助于进一步研究数据结构与算法分析.下面,简单地谈一谈冒泡排序.选择排序.插入排序的原理及区别. 冒泡排序原理: 1.比较相邻的元素.如果前一个比后一个大,它们就交换. 2.每对元素都要进行同样的动作,从后往前比较. 3.每趟都会确定一个位置的元素,因此n个元素,需要n-1趟才能确定各个元素的位置. 例如:对23,4,56,11四个数进行冒泡排序. 第一趟 4,23,11,56 第二趟 4,11,23,

Java数据结构和算法(九)——高级排序

春晚好看吗?不存在的!!! 在Java数据结构和算法(三)——冒泡.选择.插入排序算法中我们介绍了三种简单的排序算法,它们的时间复杂度大O表示法都是O(N2),如果数据量少,我们还能忍受,但是数据量大,那么这三种简单的排序所需要的时间则是我们所不能接受的.接着我们在讲解递归 的时候,介绍了归并排序,归并排序需要O(NlogN),这比简单排序要快了很多,但是归并排序有个缺点,它需要的空间是原始数组空间的两倍,当我们需要排序的数据占据了整个内存的一半以上的空间,那么是不能使用归并排序的. 本篇博客将

Java数据结构与算法之排序

排序从大体上来讲,做了两件事情: 1.比较两个数据项: 2.交换两个数据项,或复制其中一项 一.冒泡排序 大O表示法:交换次数和比较次数都为O(N*N). 算法原理: 1.比较相邻的元素.如果第一个比第二个大,就交换他们两个. 2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对.在这一点,最后的元素应该会是最大的数. 3.针对所有的元素重复以上的步骤,除了最后一个. 4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较. /** * 冒泡排序 demo * */

Java数据结构与算法之集合

线性表.链表.哈希表是常用的数据结构,在进行Java开发时,SDK已经为我们提供了一系列相应的类来实现基本的数据结构.这些类均在java.util包中. 一.Collection接口 Collection是最基本的集合接口,一个Collection代表一组Object.一些Collection允许相同元素而另一些不行.一些能排序而另一些不行.Java  SDK不提供直接继承自Collection的类,Java  SDK提供的类都是继承自Collection的"子接口"如List和Set

Java数据结构和算法之链表

三.链表 链结点 在链表中,每个数据项都被包含在'点"中,一个点是某个类的对象,这个类可认叫做LINK.因为一个链表中有许多类似的链结点,所以有必要用一个不同于链表的类来表达链结点.每个LINK对象中都包含一个对下一个点引用的字段(通常叫做next)但是本身的对象中有一个字段指向对第一个链结点的引用. 单链表 用一组地址任意的存储单元存放线性表中的数据元素. 以元素(数据元素的映象)  + 指针(指示后继元素存储位置)  = 结点(表示数据元素 或 数据元素的映象) 以"结点的序列&q

java数据结构与算法之双链表设计与实现

转载请注明出处(万分感谢!): http://blog.csdn.net/javazejian/article/details/53047590 出自[zejian的博客] 关联文章: 关联文章: java数据结构与算法之顺序表与链表设计与实现分析 java数据结构与算法之双链表设计与实现 java数据结构与算法之改良顺序表与双链表类似ArrayList和LinkedList(带Iterator迭代器与fast-fail机制) ??上一篇文章分析顺序表和单链表,本篇就接着上篇继续聊链表,在单链表

java数据结构与算法之树基本概念及二叉树(BinaryTree)的设计与实现

[版权申明]未经博主同意,不允许转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/53727333 出自[zejian的博客] 关联文章: java数据结构与算法之顺序表与链表设计与实现分析 java数据结构与算法之双链表设计与实现 java数据结构与算法之改良顺序表与双链表类似ArrayList和LinkedList(带Iterator迭代器与fast-fail机制) java数据结构与算法之栈(Stack)设