排序算法 | 时间复制度 |
---|---|
冒泡排序 | O(n2) |
选择排序 | O(n2) |
插入排序 | O(n2) |
希尔排序 | O(n1.5) |
快速排序 | O(N*logN) |
归并排序 | O(N*logN) |
堆排序 | O(N*logN) |
基数排序 | O(N*logN) |
参考:添加链接描述
冒泡排序:
public class Main {
public static void main(String[] args) {
int arr[] = new int[]{9,1,5,3,4,8,6,7,2};
System.out.println(Arrays.toString(arr));
/*
冒泡排序:
比较相临的两个数据,打的往后,小的往前
时间复杂度:O(n2)
*/
for (int j = arr.length; j > 0 ; j--){
for (int i = 0; i < j-1; i++){
if (arr[i] > arr[i+1]){
int temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
}
}
选择排序:
public class Main {
public static void main(String[] args) {
int arr[] = new int[]{9,1,5,3,4,8,6,7,2};
System.out.println(Arrays.toString(arr));
/*
选择排序:
先比较一遍所有数据,把最小的数和第一个位置的数交换位置,
然后比较第二到最后一个,共(n-1)个数,把最小的数和第二个数交换位置,一次类推
时间复杂度:O(n2)
*/
for (int i = 0 ; i < arr.length ; i++){
int minIndex = i; //最小数的下标
for (int j = i+1 ; j < arr.length ; j++){
if (arr[j] < arr[minIndex]){
minIndex = j; //跟新下标
}
}
if (minIndex != i){ //交换位置
int temp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = temp;
}
}
System.out.println(Arrays.toString(arr));
}
}
插入排序:
public class Main {
public static void main(String[] args) {
int arr[] = new int[]{9,1,5,3,4,8,6,7,2};
System.out.println(Arrays.toString(arr));
/*
插入排序:
假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是排好顺序的
时间复杂度:O(n2)
*/
for (int i = 0 ; i < arr.length-1 ; i++){
for (int j = i+1 ; j > 0 ; j--){ //把下标为i+1的数插入到前i个有序数中,形成新的有序数
if (arr[j] < arr[j-1]){
int temp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = temp;
}
else{ //已经是有序序列了,停止
break;
}
}
}
System.out.println(Arrays.toString(arr));
}
}
希尔排序
public class Main {
public static void main(String[] args) {
int arr[] = new int[]{9,1,5,3,4,8,6,7,2};
System.out.println(Arrays.toString(arr));
/*
希尔排序:
在要排序的一组数中,根据某一增量分为若干子序列,并对子序列分别进行插入排序。
然后逐渐将增量减小,并重复上述过程。直至增量为1,此时数据序列基本有序,最后进行插入排序。
时间复杂度:O(n1.5)
*/
int len = arr.length;
int temp = 0;
int incre = len;
while (true){
incre = incre/2; //间隔分量,用来从等间隔的从原序列中提取新序列
for (int k = 0; k < incre ; k++){ //根据分量分为若干子序列
for (int i = k + incre ; i < len ; i+=incre){ //这里不是用连续序列排序,而是间隔incre的下标的序列拿出来排序
for (int j = i ; j > k ; j-= incre){ //新序列的下标开头是k,结尾是i,然后进行插入排序
if (arr[j] < arr[j-incre]){ //新序列中前一个数比后一个数大(原始插入排序是相连的两个数),交换位置
temp = arr[j-incre];
arr[j-incre] = arr[j];
arr[j] = temp;
}
else{ //给新序列已经是有序序列了,停止循环(插入排序)
break;
}
}
}
}
if (incre == 1){ //当分量为1时,退出循环
break;
}
}
System.out.println(Arrays.toString(arr));
}
}
快速排序
public class Main {
public static void main(String[] args) {
int arr[] = new int[]{9,1,5,3,4,8,6,7,2};
System.out.println(Arrays.toString(arr));
/*
快速排序:
基本思想:分治
先从数组中选出一个数作为key值
将比这个数小的数放在它左边,大于或等于这个数的数放在它右边
对左右两个小数列重复上一步,直到小数列的长度为1为止
时间复杂度:O(nlogn)
*/
quikSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
public static void quikSort(int [] arr , int l, int r){
if (l >= r){
return;
}
int i = l; //左指针
int j = r; //右指针
int key = arr[l]; //选第一个数作为key
while(i<j){
//第一步,从右往左遍历
while(i<j && arr[j] >= key){
j--;
}
//当遇到比key小的数或者j=i时,退出小循环,判断此时如果j!=i,则说明此时j所指的数比key小
//在j所在的为止挖一个坑,把j所在位置的数放到i的位置上去
arr[i] = arr[j];
//第二步,从左往右遍历
while (i<j && arr[i] < key){
i++;
}
//当遇到比key大的数或者j=i时,退出小循环,判断此时如果j!=i,则说明此时i所指的数比key大
//在i所在的为止挖一个坑,把i所在位置的数放到j的位置(上一步挖的坑)上去
arr[j] = arr[i];
}
//第三步,把key值填在最后的坑内(此时i=j)
arr[i] = key;
//第四步,对左右两边的子序列分别递归的排序
quikSort(arr,l,i-1);
quikSort(arr,i+1,r);
}
}
归并排序:
public class Main {
public static void main(String[] args) {
int arr[] = new int[]{9,1,5,3,4,8,6,7,2};
System.out.println(Arrays.toString(arr));
/*
归并排序:
基本思想:分治
首先将数组分成2组A,B ,然后A,B两组依次二分,知道子序列长度为1,我们认为长度为1的序列是有序的( O(logn) )
然后讲子序列合并成有序的序列(O(n))
时间复杂度:O(nlogn)
*/
int[] temp = new int[arr.length];
mergeSort(arr,0,arr.length-1,temp);
System.out.println(Arrays.toString(arr));
}
public static void mergeSort(int[] arr, int l, int r, int[] temp){ //传入原序列,左指针,右指针,临时数组
if (l < r){ //若左指针小于右指针,说明此时的序列长度大于1
int mid = (l + r)/2; //从中间分割序列
mergeSort(arr,l,mid,temp); //继续分割左子序列
mergeSort(arr,mid+1,r,temp); //继续分割右子序列
merge(arr,l,mid,r,temp); //合并左右两边的子序列
}
}
public static void merge(int[] arr, int l, int mid, int r, int[] temp){
int i = l; //左子序列指针
int j = mid+1; //右子序列指针
int k = 0; //临时序列指针
while (i <= mid && j <= r){ //两个子序列都有数字
if (arr[i] <= arr[j]){ //当左子序列的头部数比右子序列的头部数小时,把这个数加入到临时序列中
temp[k] = arr[i];
k++; //临时序列指针前进一位
i++; //左子序列指针前进一位
}
else{ //反之,把右子序列的头部数加入到临时序列中
temp[k] = arr[j];
k++;
j++;
}
}
//处理当其中一个子序列的数全部被选出后的另一个子序列的剩余的数
while (i <= mid){
temp[k] = arr[i];
k++;
i++;
}
while (j <= r){
temp[k] = arr[j];
k++;
j++;
}
//用临时序列中的数修改原始序列
for (int t = 0; t < k ; t++){
arr[l+t] = temp[t];
}
}
}
堆排序:
public class Main {
public static void main(String[] args) {
int arr[] = new int[]{9,1,5,3,4,8,6,7,2};
System.out.println(Arrays.toString(arr));
/*
堆排序:
1:首先先建立一个大顶堆,大顶堆只能保证父节点比左孩子和右孩子都大,不能让左孩子大于右孩子
2:然后把第一个数,也就是大顶堆的根节点和尾节点交换,这样只需要考虑剩余的前(n-1)个节点了
3:对剩余的(n-1)个数重新建立创建大顶堆,这样,剩余的(n-1)个节点中最大的数就会在新的大顶堆顶部
4:新的大顶堆顶部的数和"此时新的大顶堆"的尾节点交换位置,然后再去调整剩余的前(n-2)个节点,以此类推
*/
int[] arr = {5,3,4,2,1,6};
System.out.println(Arrays.toString(arr));
minHeap_Sort(arr,arr.length);
System.out.println(Arrays.toString(arr));
}
public void minHeap_Sort(int[] arr, int len){
//创建堆
for(int i = (len-1)/2; i>= 0; i--){//从最后一个非叶子节点从下往上遍历
adjustHeap(arr, i, len);
}
//交换对顶元素和堆尾元素,然后调整堆
for (int i = len - 1; i >= 0; i--){
//将堆尾元素和堆顶元素交换
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
//调整堆,长度是i,因为最大的那个数(上一轮的堆顶已经被换到堆尾了),所以只需要对剩余的数进行调整就行了
adjustHeap(arr, 0, i);
}
}
public void adjustHeap(int[] arr, int parent, int len){
//调整堆
//讲temp作为父亲节点
int temp = arr[parent];
//左孩子
int lChild = 2 * parent + 1;
while(lChild < len){
//首先先比较左右孩子,找出最大的孩子之后,让最大的孩子去和父节点比较
//右孩子
int rChild = lChild + 1;
//如果右孩子比左孩子大,则选择右孩子
if (rChild < len && arr[rChild] > arr[lChild]){
lChild++;
}
//比较最大的孩子和父节点的大小,如果父节点比孩子节点大,直接结束
if (temp > arr[lChild]){
break;
}
//把孩子节点的值赋值给父节点
arr[parent] = arr[lChild];
//选取左孩子节点作为新的父节点,继续向下调整(这里的"左孩子"其实是左右孩子中大的那个)
parent = lChild;
//更新左孩子
lChild = parent * 2 + 1;
}
//把值赋值给此时正在探索的节点
arr[parent] = temp;
}
}