一.算法的分类
相关概念:
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。
时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。
二.冒泡排序(BubbleSort)
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
核心思想:相邻逆序就交换
1.1 算法描述
- 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
- 针对所有的元素重复以上的步骤,除了最后一个;
- 重复步骤1~3,直到排序完成。
1.2 动图演示
1.3 代码实现
import java.util.Scanner;
public class BubbleSort {
public static void main(String[] args) {
//int a[] = {12,1,3,4,7,2,5};
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一维数组,数组长度为10:");
int[] a = new int[10];
for(int i = 0;i < 10;i++){
a[i] = scanner.nextInt();
}
bubbleSort(a);
System.out.println("冒泡排序后的顺序:");
for(int i=0;i<a.length;i++){
if(a[i]!=a[a.length-1])
System.out.print(a[i]+",");
else
System.out.println(a[i]);
}
}
private static void bubbleSort(int[] a) {
//外层循环记录冒泡的趟数,总共进行a.length-1趟排序
for(int i=0;i<a.length-1;i++){
//内层循环记录一趟循环比较的次数
for(int j=0;j<a.length-i-1;j++){
if(a[j]>a[j+1]){
int temp = a[j+1];
a[j+1] = a[j];
a[j] = temp;
}
}
}
}
}
N个元素需要排序N-1轮;
第i轮需要比较N-i次;
N个元素排序,需要比较n(n-1)/2次;
冒泡排序的算法复杂度较高,为O(n*n)
三.快速排序(Quick Sort)
改进冒泡排序中一次只能消除一个逆序的缺点,即实现一次交换消除多个逆序
快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
package com.luo.sort;
public class QuickSort {
public static void main(String[] args) {
int arr[] = {3,5,1,4,7,9,8};
int start = 0;
int end = arr.length - 1;
quickSort(arr,start,end);
for (int i=0;i<arr.length;i++){
System.out.print(arr[i]+" ");
}
}
private static void quickSort(int[] arr,int lo,int hi) {
int left = lo;
int right = hi;
if(left>=right){
return;
}
int key = arr[left] ;
while (left<right){
//1.先从后往前扫描,找比key小的
while (arr[right]>=key && right>left){
right--;
}
//找到之后,替换掉基准位的值
if(left<right){
arr[left] = arr[right];
left++;
}
//2.从前往后找,找比key大的
while (arr[left]<=key && right>left){
left++;
}
//找到之后,填补右侧之前替换掉的基准位的值的位置空缺
if(left<right){
arr[right] = arr[left];
right--;
}
}
arr[right] = key;
quickSort(arr,lo,left-1);//比基准数小的再排序(左边)
quickSort(arr,right+1,hi);//比基准数大的再排序(右边)
}
}
四.选择排序
选择排序是对冒泡排序的改进,它的比较次数与冒泡排序相同,但交换次数要小于冒泡排序。当数据量较大时,效率会有很大的提升,但时间复杂度仍为O(n*n)
原理:
1、从第一个元素开始,分别与后面的元素向比较,找到最小的元素与第一个元素交换位置;
2、从第二个元素开始,分别与后面的元素相比较,找到剩余元素中最小的元素,与第二个元素交换;
3、重复上述步骤,直到所有的元素都排成由小到大为止。
待比较数据:7, 6, 9, 8, 5,1
第一轮:此时指针指向第一个元素7,找出所有数据中最小的元素,即1,交换7和1的位置,排序后的数据为:1,6,9,8,5,7
第二轮:第一个元素已经为最小的元素,此时指针指向第二个元素6,找到6,9,8,5,7中最小的元素,即5,交换5和6的位置,排序后的结果为:1,5,9,8,6,7
第三轮:前两个元素为排好序的元素,此时指针指向第三个元素9,找到9,8,6,7中最小的元素,即6,交换6和9的位置,排序后的结果为:1,5,6,8,9,7
第四轮:前三个元素为排好序的元素,此时指针指向第四个元素8,找到8,9,7中最小的元素,即7,交换8和7的位置,排序后的结果为:1,5,6,7,9,8
第五轮:前四个元素为排好序的元素,此时指针指向第五个元素9,找到9,8中最小的元素,即8,交换9和8的位置,排序后的结果为:1,5,6,7,8,9
到此,全部排序完成。
package com.luo.sort;
public class SelectSort {
public static void main(String[] args) {
int[] arr = {6,1,2,7,9,3,4,5,10,8};
selectSort(arr);
for (int i=0;i<arr.length;i++){
System.out.print(arr[i]+" ");
}
}
private static void selectSort(int[] arr) {
for(int i=0;i<arr.length-1;i++){
for(int j=i+1;j<arr.length;j++){
if(arr[i]>arr[j]){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
// private static void selectSort(int[] arr) {
//
//
// //外层循环记录排序的t趟数,和冒泡排序类似,N个元素进行N-1趟排序
// for(int i=0;i<arr.length-1;i++){
// //将i指向最小元素的下标
// int minIndex = i;
// for (int j=i+1;j<arr.length;j++){
// if(arr[j]<arr[minIndex]){
// minIndex = j;
// }
// }
// int temp = arr[i];
// arr[i] = arr[minIndex];
// arr[minIndex] = temp;
//
//
// }
// }
}
N个元素需要排序N-1轮;
第i轮需要比较N-i次;
N个元素排序,需要比较n(n-1)/2次;
选择排序的算法复杂度仍为O(n*n);
相比于冒泡排序,选择排序的交换次数大大减少,因此速度要快于冒泡排序
五.插入排序
插入排序是简单排序中最快的排序算法,虽然时间复杂度仍然为O(n*n),但是却比冒泡排序和选择排序快很多。
原理:插入排序算法有种递归的思想在里面,它由N-1趟排序组成。初始时,只考虑数组下标0处的元素,只有一个元素,显然是有序的。
然后第一趟 对下标 1 处的元素进行排序,保证数组[0,1]上的元素有序;
第二趟 对下标 2 处的元素进行排序,保证数组[0,2]上的元素有序;
.....
.....
第N-1趟对下标 N-1 处的元素进行排序,保证数组[0,N-1]上的元素有序,也就是整个数组有序了。
它的递归思想就体现在:当对位置 i 处的元素进行排序时,[0,i-1]上的元素一定是已经有序的了。
待比较数据:7, 6, 9, 8, 5,1
第一轮:指针指向第二个元素6,假设6左面的元素为有序的,将6抽离出来,形成7,_,9,8,5,1,从7开始,6和7比较,发现7>6。将7右移,形成_,7,9,8,5,1,6插入到7前面的空位,结果:6,7,9,8,5,1
第二轮:指针指向第三个元素9,此时其左面的元素6,7为有序的,将9抽离出来,形成6,7,_,8,5,1,从7开始,依次与9比较,发现9左侧的元素都比9小,于是无需移动,把9放到空位中,结果仍为:6,7,9,8,5,1
第三轮:指针指向第四个元素8,此时其左面的元素6,7,9为有序的,将8抽离出来,形成6,7,9,_,5,1,从9开始,依次与8比较,发现8<9,将9向后移,形成6,7,_,9,5,1,8插入到空位中,结果为:6,7,8,9,5,1
第四轮:指针指向第五个元素5,此时其左面的元素6,7,8,9为有序的,将5抽离出来,形成6,7,8,9,_,1,从9开始依次与5比较,发现5比其左侧所有元素都小,5左侧元素全部向右移动,形成_,6,7,8,9,1,将5放入空位,结果5,6,7,8,9,1。
第五轮:同上,1被移到最左面,最后结果:1,5,6,7,8,9。
package com.luo.sort;
public class InsertSort {
public static void main(String[] args) {
int[] arr = {6,1,2,7,9,3,4,5,10,8};
insertSort(arr);
for(int i=0;i<arr.length;i++){
System.out.print(arr[i]+" ");
}
}
private static void insertSort(int[] arr) {
//外层记录排序的趟数,总共进行n-1趟(第1个元素到最后一个元素)比较
for(int i=0;i<arr.length;i++){
//内层循环记录比较元素的次数,假设左边的元素是有序的,开始时arr[0]有序,第二次选中arr[1],然后对选中的元素分别从
// 选中的位置开始向左进行比较,若小于前一个,则交换位置,
for(int j=i; (j>0)&&(arr[j]<arr[j-1]); j--){
swap(arr,j,j-1);
}
}
}
private static void swap(int[] arr, int j, int i) {
int temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}