内部排序:待排序的记录全部放到内存中进行排序,时间复杂度也就等于比较的次数
外部排序:数据量很大,内存无法容纳,需要对外存进行访问再排序,把若干段数据一次读入内存使用内部排序的方法进行排序后写入外存,再将这若干个已经排序的数据进行归并,时间复杂度等于IO(访问外存)的次数
1.冒泡排序
是一种简单的排序,重复地从头到尾的走过整个数列,一次比较两个元素
1.比较相邻的两个, 前面的比后面大,就交换过来
2.重复步骤,直到没有需要交换的需要,就完成
动图演示
package chapter04;
import java.util.Arrays;
public class Demo01 {
public static void main(String[] args) {
int [] arr = {3,45,48,46,95,2,3,4,5,13,52,10};
bubblesort(arr);
System.out.println(Arrays.toString(arr));
//Arrays.toString()
// 作用:方便地输出数组。
// 这个方法是是用来将数组转换成String类型输出的,入参可以是long,float,double,int,boolean,byte,object
// 型的数组。
}
public static void bubblesort(int [] arr) {
if(arr == null || arr.length <= 1){ //数组长度小于1
return;
}
int length = arr.length;
for (int i =0 ; i < length; i++){//外部循环控制比较轮数
for (int j = 0; j < length - 1 - i; j++) {//内层循环控制每一轮比较的次数,每一轮排序都会找出一个最大值
//arr.length - 1 防止索引越界, arr.length - 1 - i 减少比较的次数
if (arr[j] > arr[j+1]){
int temp = arr[j +1];
arr [j+ 1] =arr [j];
arr [j] = temp;
}
}
}
}
}
2.选择排序
初始状态分为 有序区 和 无序区
无序区为R[1....n ]
有序区为空
当i趟排序 {i=1,2,3,4,5...n-1}
无序区为R[1.....i-1]
有序区为R[i.....n]
第 n-1 趟就有序化了
在无序区中选出关键字最小的记录为R[k],将它与无序区的第一个记录R交换,使R[1…i]和R[i+1…n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
动图演示
package chapter04;
import java.util.Arrays;
public class Demo02 {
public static void main(String[] args) {
int [] arr = {3,45,48,46,95,2,3,4,5,13,52,10};
selectionsort(arr);
System.out.println(Arrays.toString(arr));
}
public static void selectionsort(int [] arr){
if (arr == null || arr.length <= 1){
return;
}
int length = arr.length;
for (int i = 0; i < length -1; i++) {
//保存最小数的索引
int min = i;
for (int j = i+1 ; j < length; j++) {
//找到最小的数
if(arr[j] < arr[min]){
min = j;
}
}
if (i !=min){ // 将找到的最小值和i位置所在的值进行交换
swap(arr,min,i);
}
}
}
private static void swap(int [] arr, int a,int b){
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
3.插入排序
认定第一个元素开始,该元素认定已经被排序;
取出第二个元素a,从序列中从后往前扫描;
扫描到 元素(已经排序)大于元素a,就往下一个位置移一位
直到扫描到 元素(已经排序)小于或等于 元素a ,将元素a插入到元素(已经排序)位置后面
动图演示
package chapter04;
import java.util.Arrays;
public class Demo03 {
public static void main(String[] args) {
int [] arr ={ 3,45,48,46,95,2,3,4,5,13,52,10};
insertionSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void insertionSort(int [] arr) {
if (arr == null || arr.length <= 1){
return;
}
int length = arr.length;
int insertNum;
for (int i = 1; i < length; i++) {
insertNum = arr[i];
int j = i -1;
while (j>=0 && arr[j] >insertNum){
//从后往前循环当元素(排列好的)大于元素a就往后一位
arr[j + 1] = arr[j];
j--;
}
arr[j+1]= insertNum;//将元素a插入要插入的位置
}
}
}
4.快速排序
分治法
可以通俗的解释为:把一片领土分解,分解为若干块小部分,然后一块块地占领征服,被分解的可以是不同的政治派别或是其他什么,然后让他们彼此异化。
分治法的精髓:
分--将问题分解为规模更小的子问题;
治--将这些规模更小的子问题逐个击破;
合--将已解决的子问题合并,最终得出“母”问题的解;
选一个数为基准
i从前往后找比基准大的数,停止不动
j从后往前找比基准小的数,停止不动
重叠了i不动、·· j继续往前找比基准小的数
选定 元素3 为基准
i从前往后找比 3 (基准)大的数找到 44
j从后往前找比 3 (基准)小的数找到 2
交换 i 和 j
右边的数 永远都比 基准小 元素2 与元素3 (基准) 交换
当i往右移没有找到 比基准小的数,继续往右移 与 j 相遇 (i==j)
(i == j )与 基准 交换
动图演示
这时候就是分治法
分成左边跟右边
(左边)+ 第一次循环的基准(值)+ (右边)
左边里面
选一个基准继续循环
右边里面
选一个基准继续循环
package chapter04;
import java.util.Arrays;
public class Demo04 {
public static void main(String[] args) {
int[] arr = {3,45,48,46,95,2,3,4,5,13,52,10};
quickSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void quickSort(int[] array) {
quickSort(array, 0, array.length - 1);
}
private static void quickSort(int[] arr, int left, int right) {
if (arr == null || left >= right || arr.length <= 1) {
return;
}
int mid = partition(arr, left, right);
quickSort(arr, left, mid);
quickSort(arr, mid + 1, right);
}
private static int partition(int[] arr, int left, int right) {
int temp = arr[left];
while (right > left) {
// 先判断基准数和后面的数依次比较
while (temp <= arr[right] && left < right) {
--right;
}
// 当基准数大于了 arr[left],则填坑
if (left < right) {
arr[left] = arr[right];
++left;
}
// 现在是 arr[right] 需要填坑了
while (temp >= arr[left] && left < right) {
++left;
}
if (left < right) {
arr[right] = arr[left];
--right;
}
}
arr[left] = temp;
return left;
}
}