排序
所谓排序
,就是指将一组数据,按照特定规则调换位置,使数据具有某种顺序关系(递增或递减)。
在排序过程中,数据的移动方式可分为直接移动
和逻辑移动
两种。直接移动
是直接交换存储数据的位置,而逻辑移动
并不会移动数据存储的位置,仅改变指向这些数据的指针。
两者之间的优劣在于直接移动会浪费许多时间进行数据的改动,而逻辑移动只要改变指针指向的位置就能达到排序的目的。数据在排序过后,往往会有以下几点好处:
- 数据比较容易阅读
- 数据比较利于统计及整理
- 可以大幅减少数据搜索的时间。
排序分类
排序可以按照执行时所使用的内存分为以下两种方式:
内部排序
:排序的数据量小,可以完全在内存中进行排序。外部排序
:排序的数据量无法直接在内存中进行排序,而必须使用辅助存储器(如硬盘)。
常见的内部排序法
有:冒泡排序、选择排序、插入排序、合并排序、快速排序、堆积排序、希尔排序、基数排序法等,比较常见的外部排序法
有:直接合并排序、k路合并、多相合并法等。
排序算法分析
时间复杂度
当数据量比较大时,排序算法所花费的时间就很重要。排序算法的时间复杂度可以分为最好情况、最坏请求和平均情况。最好情况看是指数据已经完成排序,最坏情况是指数据顺序为要求排序的倒序(两者前提是我们未知)。
空间复杂度
空间复杂度是指算法在执行过程中所需付出的额外内存空间,例如所选择的排序法必须借助递归的方式来进行,那么递归过程中会用到的堆栈就是这个排序法所必须付出的额外空间。同时在任何排序法中都会有数据位置互换的动作,数据位置互换也会用到一个额外的空间它也是排序法中空间复杂度要考虑的问题。排所用到的额外空间越少,它的空间复杂度就越好。
算法稳定性
稳定的排序算法在经过排序后,两个相同的值应仍然保持原来的次序,即原来谁在左谁在右,排完序后也一定是谁在左谁在右,如下所示:
原始数据 :5(左),9,1,4,3,5(右),6
稳定排序 :1,3,4,5(左),5(右),6,9
不稳定排序 :1,3,4,5(右),5(左),6,9
冒泡排序
冒泡排序又称交换排序法,是有观察水中气泡的变化发现的,水中气泡随着水深度不同压力会发生改变,当气泡在水底时,水压最大气泡最小,当气泡慢慢浮上水面时,气泡由小渐渐变大。因此冒泡排序的基本原理如下:
首先从数组第一个元素开始,比较相邻元素的大小,若大小顺序不符合要求顺序,则将元素对调,对调后进行下一个元素的比较。像这样比较一轮之后就能保证最后一个元素是最大或者最小的(符合我们的要求),接着进行第二轮扫描,以此保证倒数第二个元素是符合我们要求的,如此进行下去,知道保证所有的元素满足排序关系为止。
![ef6c08bf6ae4d8b98df9bfaab59bcbfd.png](https://i-blog.csdnimg.cn/blog_migrate/0ad408c504f6999d0c415d319c18eeba.jpeg)
![46fc93c05561caf43c05706f4952cb64.png](https://i-blog.csdnimg.cn/blog_migrate/a96f8fa4b695eec57b56704c958a4035.png)
代码如下:
JAVA
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args) {
int[] arrays = {6,5,9,7,2,8};
bubbleSort(arrays);
System.out.println(Arrays.toString(arrays));
}
public static void bubbleSort(int[] array) {
int temp;
/**
* 冒泡排序
* i 为扫描次数
* j 为比较值的坐标
*/
for (int i=array.length-1;i>0;i--) {
for (int j=0;j<i;j++) {
if (array[j]>array[j+1]) {
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
}
}
}
Python
def buttle_sort(array):
for i in range(len(array)-1):
for j in range(len(array)-i-1):
if array[j] > array[j+1]:
array[j],array[j+1] = array[j+1],array[j]
if __name__ == '__main__':
array = [6,5,9,7,2,8]
buttle_sort(array)
print(array)
改良的冒泡排序
上面的冒泡排序不难看出有一个缺点,即不管数据是否已经排序完成,算法都会固定的执行n(n-1)/2
次,而实际上如果数据执行中途已经完成排序,完全不必要多执行剩下的步骤,因此我们可以通过增加一个标志变量来判断数据是否已经完成排序,如果完成排序就让程序提前退出:
改良后的JAVA排序函数:
public static void bubbleSort(int[] array) {
int temp,flag;
/**
* 冒泡排序
* i 为扫描次数
* j 为比较值的坐标
*/
for (int i=array.length-1;i>0;i--) {
flag = 0;
for (int j=0;j<i;j++) {
if (array[j]>array[j+1]) {
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
flag++;
}
}
if (flag == 0)
break;
}
}
}
改良后的Python函数:
def buttle_sort(array):
for i in range(len(array)-1):
flag = 0
for j in range(len(array)-i-1):
if array[j] > array[j+1]:
array[j],array[j+1] = array[j+1],array[j]
flag += 1
if flag == 0:
break
选择排序
选择排序法可以使用两种方式排序,一为在所以的数据中,当由大到小排序,则将最大值放入第一位置;若由小到大排序,则将最大值放入位置末端。例如当N个数据需要由大到小排序时,首先以第一个位置的数据,依次向2、3、4...N个位置的数据作比较。
如果数据大于或等于其中一个位置,则两个位置的数据不变;若小于其中一个位置,则两个位置的数据互换。互换后,继续找下一个位置作比较,直到位置最末端,此时第一个位置的数据即为此排序数列的最大值。接下来选择第二个位置数据,依次向3、4、5...N个位置的数据作比较,将最大值放入第二个位置。依循此方法知道第N-1个位置最大值找到后,就完成选择排序法由大到小的排列。
代码如下:
JAVA
import java.util.Arrays;
public class SelectSort {
public static void main(String[] args) {
int[] arrays = {6,5,9,7,2,8};
selectSort(arrays);
System.out.println(Arrays.toString(arrays));
}
public static void selectSort(int[] array) {
int temp;
for (int i=0;i<5;i++) {
for (int j=i+1;j<6;j++) {
if (array[i]>array[j]) {
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
}
}
Python
def select_sort(array):
for i in range(len(array)-1):
for j in range(i+1,len(array)):
if array[i]>array[j]:
array[i],array[j] = array[j],array[i]
if __name__ == '__main__':
array = [6, 5, 9, 7, 2, 8]
select_sort(array)
print(array)