一、冒泡排序
(1)原理如下:
比较相邻的元素。如果前面的数比后面的大,就交换他们两个。
从下标为0的数开始,结束于下标为arr.length-1的数,则判断条件:if ( arr[ j ]>arr[ j+1] ),把大的数换到后面,小的数放前面。
时间复杂度: O(n²)
算法稳定性:稳定排序算法
(2)实现
public static void bubbleSort(int[] arr) {
for(int i=0;i<arr.length-1;i++) {
for(int j=0;j<arr.length-1-i;j++) {
if(arr[j]>arr[j+1]) {
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
二、快速排序
(1)基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
(2)按照这种思想,我们设第一个数为一个标志数,比它小的排左边,比它大的排右边。然后设两个变量来推进数与数之间的比较。然后递归再来排左右两边的数。
import java.util.Arrays;
public class QuickSort1 {
public static void main(String[] args) {
// 随便设的数组
int[] arr = { 3, 4, 1 };
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
public static void quickSort(int[] arr, int start, int end) {
// 递归要有一个结束条件,当start>=end的时候就不用比较了
if (start < end) {
// 设第一个为标志数
int pivot = arr[start];
// 第一个设start,最后一个数设end
// 然后再设两个变量作为临时变量,推进比较
int low = start;
int high = end;
while (low < high) {
//如果从最后一个来比较的话,arr[high]<pivot,说明后面的数比前面的数小,就要拿来排序
while (pivot < arr[high]) {
high--;
}
//arr[high]<pivot的话,前面大数的位置放小的数
arr[low] = arr[high];
//又从前面开始,如果前面的数小于标志数,就不用变前面的数,因为我们就是要前面的数小于标志数,后面的数大于标志数
//这里需要low<high,因为前面high在变化
while (low<high&&arr[low] <= pivot) {
low++;
}
//如果前面的数有>标志位的,就放在刚才卡住的high的位置
arr[high] = arr[low];
}
//上面就相当于把数排成两部分,中间要填入标志数
arr[low] = pivot;
// 递归:处理小的,将小的那部分从小到大再排
quickSort(arr, start, low - 1);
// 递归:处理大的
quickSort(arr, low + 1, end);
}
}
}
还有一种方法是为了更方便理解快速排序,按照思想,将其分为三部分,最后合并。
import java.util.ArrayList;
import java.util.Arrays;
/*
* 把需要排序的数组分成三份,按照快排的思想排序,最后讲三部分合在一起
*/
public class QuickSort {
public static void main(String[] args) {
int[] arr = { 2, 1, 0, 7, 9 };
quickSort(arr, 0, arr.length - 1);
}
public static void quickSort(int[] arr, int start, int end) {
//递归需要结束条件
if (start >= end) {
return;
}
int part = partion(arr, start, end);//标准数应该放的位置
quickSort(arr, start, part - 1);//左部分
quickSort(arr, part + 1, end);//右部分
}
public static int partion(int[] arr, int start, int end) {
//把第一个作为标志数
int pivot = arr[start];
//设两个动态数组,a存左边,b存右边
ArrayList<Integer> a = new ArrayList<>();
ArrayList<Integer> b = new ArrayList<>();
//从标志数下一个开始比较,两两比较,凡是比它小的放a,凡是比它大的放b
for (int i = start + 1; i <= end; i++) {
if (arr[i] > pivot) {
b.add(arr[i]);
} else if (arr[i] <= pivot) {
a.add(arr[i]);
}
}
//开始合并这三部分
for (int i = 0; i < a.size(); i++) {
arr[start++] = a.get(i);//比中间数小的
}
int temp = start;//记录中间数
arr[start++] = pivot;//start++(start先用,之后再+1是为了保证从中间数的下一个位置开始来合并b
for (int i = 0; i < b.size(); i++) {
arr[start++] = b.get(i);
}
System.out.println(Arrays.toString(arr));
return temp;//返回标志数
}
}
快速排序的一次划分算法从两头交替搜索,直到low和high重合,因此其时间复杂度是O(n);而整个快速排序算法的时间复杂度与划分的趟数有关。
理想的情况是,每次划分所选择的中间数恰好将当前序列几乎等分,经过log2n趟划分,便可得到长度为1的子表。这样,整个算法的时间复杂度为O(nlog2n)。
最坏的情况是,每次所选的中间数是当前序列中的最大或最小元素,这使得每次划分所得的子表中一个为空表,另一子表的长度为原表的长度-1。这样,长度为n的数据表的快速排序需要经过n趟划分,使得整个排序算法的时间复杂度为O(n2)。
快速排序的空间复杂度为O(log2n))
end.