排序也就是使集合中的元素有序化,他是常见的计算机操作之一,主要介绍几种经典的排序方法:冒泡排序,插入排序,快速排序,选择排序
一,冒泡排序
冒泡排序是一种简单的排序算法,就是将一个列表种的两个元素进行比较,并将最小元素交换到顶部,从最底部元素开始比较,两个元素种较小的会冒到顶部,较大的会沉到底部,该过程会重复执行,直到所有元素都被排序。
第一次比较完,最后一个数一定是最大的值,所以第二趟比较的时候最后一个数不参与较。第二趟比较完成后,倒数第二个数也一定是数组中第二大的数,所以第三趟比较的时候最后两个数不参与比较。依次类推,每一趟比较次数-1;
N个数字要排序完成,总共进行N-1趟排序,每i趟的排序次数为(N-i)次,所以可以用双重循环语句,外层控制循环多少趟,内层控制每一趟的循环次数,即
int a[]={16,25,9,90,23}; //N个数字来排队,两两相比小靠前
for (int i=0;i<a.length-1;i++){ //外层循环N-1
for (int j=0;j<a.length-1-i;j++){ //内存循环N-1-i
if(a[j]>a[j+1]){
int tmp=a[j+1];
a[j+1]=a[j];
a[j]=tmp;
}
}
}
for (int i=0;i<a.length;i++){
System.out.println(a[i]+" ");
}
冒泡排序的优点:每进行一趟排序,就会少比较一次,因为每进行一趟排序都会找出一个较大值。如上例:第一趟比较之后,排在最后的一个数一定是最大的一个数,第二趟排序的时候,只需要比较除了最后一个数以外的其他的数,同样也能找出一个最大的数排在参与第二趟比较的数后面,第三趟比较的时候,只需要比较除了最后两个数以外的其他的数,以此类推…也就是说,没进行一趟比较,每一趟少比较一次,一定程度上减少了算法量。
二,插入排序
假设有一个已经有序的数据数列,要求在这个已经排好序的数据序列里插入一个数,但要求插入后数据仍然有序,类似于玩扑克的时候,玩家每抓到一张牌,都插入到手里已有的牌里,使之从小到大排好序。 将要排序的是一个乱的数组int [] a={3,1,2,4,3},在未知后续数值情况下
第一趟排序
我们把第一个数当作已经排好序的,然后用数组的第二个数跟第一个数想比 如果第二个数比第一个大,就不管他 如果比第一个数小,那就让大的往后排一位,小的排到第一个的位置,排完结果使{1,3}
第二趟排序
用数组的第三个数与已是有序的数据{1,3}(刚才在第一趟排的)比较 如果比第二个大,那就不管它,如果比第2个小,那就将第2个往后退一个位置,再让第三个数和第1个比较 如果第三个数比第一个大,那么将第三个数插入到第2个的位置上 如果第三个数比第一个小,那么将第1个数后退一步,将第三个数插入到第1个的位置上
代码
n个数排序,需要n-1趟排序,第一个表示已经有序
public static void main(String[] args) {
//插入排序
int temp;
int [] a={3,1,2,6,5};
//外层循环控制排序的趟数,i从1开始是因为将第0位看成有序的了
for (int i=1;i<a.length;i++){
temp=a[i];
//如果第二位比第一个小,进入第二趟循环
while (i>0 && a[i-1]>temp){
//将第一个数值赋值到第二位置上
a[i]=a[i-1];
//不断的向前比较
i--;
}
a[i]=temp;//推出循环则说明找到合适位置
}
//数组a遍历输出
for (int i=0;i<a.length;i++){
System.out.print(a[i]+" ");
}
}
快速排序
有没有既不浪费空间又可以快一点的排序算法呢?那就是“快速排序”啦!光听这个名字是不是就觉得很高端呢。假设我们现在对“6 1 2 7 9 3 4 5 10 8”这个10个数进行排序。首先在这个序列中随便找一个数作为基准数(不要被这个名词吓到了,就是一个用来参照的数,待会你就知道它用来做啥的了)。为了方便,就让第一个数6作为基准数吧。接下来,需要将这个序列中所有比基准数大的数放在6的右边,比基准数小的数放在6的左边,类似下面这种排列:
3 1 2 5 4 6 9 7 10 8
在初始状态下,数字6在序列的第1位。我们的目标是将6挪到序列中间的某个位置,假设这个位置是k。现在就需要寻找这个k,并且以第k位为分界点,左边的数都小于等于6,右边的数都大于等于6。想一想,你有办法可以做到这点吗?
方法其实很简单:分别从初始序列“6 1 2 7 9 3 4 5 10 8”两端开始“探测”。先从右往左找一个小于6的数,再从左往右找一个大于6的数,然后交换他们。这里可以用两个变量i和j,分别指向序列最左边和最右边。我们为这两个变量起个好听的名字“哨兵i”和“哨兵j”。刚开始的时候让哨兵i指向序列的最左边(即i=1),指向数字6。让哨兵j指向序列的最右边(即=10),指向数字。
首先哨兵j开始出动。因为此处设置的基准数是最左边的数,所以需要让哨兵j先出动,这一点非常重要(请自己想一想为什么)。哨兵j一步一步地向左挪动(即j–),直到找到一个小于6的数停下来。接下来哨兵i再一步一步向右挪动(即i++),直到找到一个数大于6的数停下来。最后哨兵j停在了数字5面前,哨兵i停在了数字7面前。
现在交换哨兵i和哨兵j所指向的元素的值。交换之后的序列如下:
6 1 259 3 4710 8
到此,第一次交换结束。接下来开始哨兵j继续向左挪动(再友情提醒,每次必须是哨兵j先出发)。他发现了4(比基准数6要小,满足要求)之后停了下来。哨兵i也继续向右挪动的,他发现了9(比基准数6要大,满足要求)之后停了下来。此时再次进行交换,交换之后的序列如下:
6 1 2 54397 10 8
第二次交换结束,“探测”继续。哨兵j继续向左挪动,他发现了3(比基准数6要小,满足要求)之后又停了下来。哨兵i继续向右移动,糟啦!此时哨兵i和哨兵j相遇了,哨兵i和哨兵j都走到3面前。说明此时“探测”结束。我们将基准数6和3进行交换。交换之后的序列如下:
31 2 5 469 7 10 8
到此第一轮“探测”真正结束。此时以基准数6为分界点,6左边的数都小于等于6,6右边的数都大于等于6。回顾一下刚才的过程,其实哨兵j的使命就是要找小于基准数的数,而哨兵i的使命就是要找大于基准数的数,直到i和j碰头为止。
OK,解释完毕。现在基准数6已经归位,它正好处在序列的第6位。此时我们已经将原来的序列,以6为分界点拆分成了两个序列,左边的序列是“3 1
2 5 4”,右边的序列是“9 7 10
8”。接下来还需要分别处理这两个序列。因为6左边和右边的序列目前都还是很混乱的。不过不要紧,我们已经掌握了方法,接下来只要模拟刚才的方法分别处理6左边和右边的序列即可。现在先来处理6左边的序列现吧。
左边的序列是“3 1 2 5 4”。请将这个序列以3为基准数进行调整,使得3左边的数都小于等于3,3右边的数都大于等于3。好了开始动笔吧
public static void main(String[] args) {
int[] arr = { 49, 38, 65, 97, 23, 22, 76, 1, 5, 8, 2, 0, -1, 22 };
quickSort(arr, 0, arr.length - 1);
System.out.println("排序后:");
for (int i : arr) {
System.out.println(i);
}
}
private static void quickSort(int arr[], int low, int high) {
int i,j,temp,t;
if(low>high){
return;
}
i=low;
j=high;
//temp就是基准位
temp = arr[low];
while (i<j) {
//先看右边,依次往左递减
while (temp<=arr[j]&&i<j) {
j--;
}
//再看左边,依次往右递增
while (temp>=arr[i]&&i<j) {
i++;
}
//如果满足条件则交换
if (i<j&&arr[i]>arr[j]) {
t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
//最后将基准为与i和j相等位置的数字交换
arr[low] = arr[i];
arr[i] = temp;
//递归调用左半数组
quickSort(arr, low, j-1);
//递归调用右半数组
quickSort(arr, j+1, high);
}
选择排序
a) 原理:每一趟从待排序的记录中选出最小的元素,顺序放在已排好序的序列最后,直到全部记录排序完毕。也就是:每一趟在n-i+1(i=1,2,…n-1)个记录中选取关键字最小的记录作为有序序列中第i个记录。基于此思想的算法主要有简单选择排序、树型选择排序和堆排序。(这里只介绍常用的简单选择排序)
b) 简单选择排序的基本思想:给定数组:int[] arr={里面n个数据};第1趟排序,在待排序数据arr[1]~arr[n]中选出最小的数据,将它与arrr[1]交换;第2趟,在待排序数据arr[2]~arr[n]中选出最小的数据,将它与r[2]交换;以此类推,第i趟在待排序数据arr[i]~arr[n]中选出最小的数据,将它与r[i]交换,直到全部排序完成。
c) 举例:数组 int[] arr={5,2,8,4,9,1};
-------------------------------------------------------
第一趟排序: 原始数据:5 2 8 4 9 1
最小数据1,把1放在首位,也就是1和5互换位置,
排序结果:1 2 8 4 9 5
-------------------------------------------------------
第二趟排序:
第1以外的数据{2 8 4 9 5}进行比较,2最小,
排序结果:1 2 8 4 9 5
-------------------------------------------------------
第三趟排序:
除1、2以外的数据{8 4 9 5}进行比较,4最小,8和4交换
排序结果:1 2 4 8 9 5
-------------------------------------------------------
第四趟排序:
除第1、2、4以外的其他数据{8 9 5}进行比较,5最小,8和5交换
排序结果:1 2 4 5 9 8
-------------------------------------------------------
第五趟排序:
除第1、2、4、5以外的其他数据{9 8}进行比较,8最小,8和9交换
排序结果:1 2 4 5 8 9
-------------------------------------------------------
注:每一趟排序获得最小数的方法:for循环进行比较,定义一个第三个变量temp,首先前两个数比较,把较小的数放在temp中,然后用temp再去跟剩下的数据比较,如果出现比temp小的数据,就用它代替temp中原有的数据。具体参照后面的代码示例,相信你在学排序之前已经学过for循环语句了,这样的话,这里理解起来就特别容易了
public static void main(String[] args) {
int [] arr={6,8,1,15,92,45,2,80};
System.out.print("没有排序前:");
for (int word:arr){
System.out.print(word+"\t");
}
//先暂定第一个数为最小值,比较n-1轮
for (int i=0;i<arr.length-1;i++){
int k=i;//k 是最小的值索引数
//内部每轮需要比较的n-i次
for (int j=k+1;j<arr.length;j++){
if (arr[k]>arr[j]){
k=j;//找到最小值的脚标索引值
}
}
//将找到的最小值跟i位值进行互换
if (i!=k){
int tmp=arr[i];
arr[i]=arr[k];
arr[k]=tmp;
}
}
System.out.println("排序后");
for (int word:arr){
System.out.print(word+"\t");
}
}
参考:https://blog.csdn.net/u012864854/article/details/79404463
https://www.jianshu.com/p/27e14bb8539e
https://blog.csdn.net/shujuelin/article/details/82423852