本文章对几种常用的排序进行分析与代码实现,本文所有排序都按照从小到大的顺序排列。
冒泡排序
冒泡排序是最易学也是最简单的排序,它的实现原理是多次循环数据,两两对比取大的值和下一个数继续对比,看起来像水泡从小变大的过程,因此又称冒泡排序。
代码实现
public void bubbleSort(int[] arr) {
for (int i = 0;i < arr.length - 1;i++) {
for ( int j = 1;j < arr.length - i;j++) {
if (arr[j - 1] > arr[j]) {
swap(arr, j, j - 1);
}
}
}
}
swap函数用于将两数交换。
每次循环都将余下最大的数放至末尾,共进行n - 1次循环,其中最后一次无需比较。
如果排序的数组本身就是有序的,或者经过两次冒泡以后,数组已经变得有序了,冒泡排序依然会进行排序,这显然是不合理的,因此我对其做了改进
public void bubbleSort(int[] arr) {
for (int i = 0;i < arr.length - 1;i++) {
boolean sorted = true;
for ( int j = 1;j < arr.length - i;j++) {
if (arr[j - 1] > arr[j]) {
sorted = false;
swap(arr, j, j - 1);
}
}
if (sorted) return;
}
}
取一个标志位,如果内循环中没有进行交换数据,则认为数组已经有序,无需交换。
选择排序
选择排序每次都选择出最小的数的下标进行排序。
在第一轮遍历中,默认为第一个数是最小的,并将其下标取出保存至min变量中,然后从剩余数据中与该下标表示的数比较,如果找到更小的,就重置min为该下标,随后剩余的数继续与其比较,直至第一轮遍历完成。最后将min与0下标的数互换。
在第二轮遍历中,继续找到剩余数的最小值下标,然后与1下标的数互换。
以此类推
代码实现
public void selSort(int[] arr) {
int min;
for (int i = 0;i < arr.length - 1;i++) {
min = i;
for (int j = i + 1;j < arr.length;j++) {
if (arr[j] < arr[min]) {
min = j;
}
}
swap(arr, min, i);
}
}
选择排序相较于冒泡排序,比较的次数是一样的,但是选择排序每次外循环遍历时只需交换一次数据。
插入排序
插入排序的原理就是将一个数插入到一个本身已经有序的数组里。
遍历一个数组,从第二个数开始,默认它前面的数有序,然后将该数放入前面它应该有的位置,直至最后一个数,完成排序
代码如下
public void insertSort(int[] arr) {
for (int i = 1;i < arr.length;i++) {
if (arr[i] < arr[i - 1]) {
//出现逆序,从i往前遍历,找到小于它的数
int num = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > num) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = num;
}
}
}
从上述代码看出,如果一个数组本身是比较有序的,那么该排序的效率将非常高,最好的情况只需O(n)的时间复杂度。
快速排序
作为排序算法中最有牌面的一位,常常在面试中出场,身为程序员的我们自然要好好探究一番
快速排序每次将数据分割成两部分,一堆大,一堆小,然后继续对这两对数据分割,知道无法分割为止,这时整个序列已经有序
public void QuickSort(int[] arr, int low, int high) {
if (low < high) {
//分割数组,并获取下标
int p = partition(arr, low, high);
//分割左边
QuickSort(arr, low, p - 1);
//分割右边
QuickSort(arr, p + 1, high);
}
}
快速排序通过递归调用自身来进行排序,编写递归时要注意递归的三大要素:1、确定递归的参数 2、递归的结束条件 3、明确递归体的逻辑,要做什么事
上述代码中首先要明确参数,快速排序是对数组不断分割的过程,因此需要确定分割后数组的边界low和high;其次,当分割到只有一个元素,即low=high时,就可以结束了,因此low < high为结束条件;那么在实现逻辑部分最重要的就是partioion函数了,它的作用是将数组分成两部分,并返回中间的数。
public int partition(int[] arr, int low, int high) {
int flag = arr[low];//取第一个数为标志位
while (low < high) {
//从末尾向前搜索
while (low < high && arr[high] >= flag) {
high--;
}
//找到比flag小的数则交换位置
swap(arr, low, high);
//此时从前向后搜索
while (low < high && arr[low] <= flag) {
low++;
}
//找到比flag小的数则交换位置
swap(arr, low, high);
}
return low;
}
它通过多次的交换,将序列分成两部分,代码不难,不过要注意的是,迭代的数与flag相等时指针仍然需要移动,否则会无限循环下去。