zoukankan      html  css  js  c++  java
  • 基本排序算法实现与分析

    冒泡排序、选择排序、插入排序、归并排序、快速排序、堆排序代码实现与特性分析

    冒泡排序

    实现代码

    public static void bubbleSort(int[] arr) {
    	if (arr == null || arr.length < 2) {
    		return;
    	}
    	//从0号开始比较,每次把最大的搬运到末尾,arr[i]为每次循环确定的位于最终位置的那个元素
        
        bool flag = true; //flag用来作为标记
    	for (int i = arr.length -  1; i > 0 && flag; i--) {
           flag = false;
    		for (int j = 0; j < i; j++) {
    			if (arr[j] > arr[j + 1]) {
    				swap(arr, j, j + 1);
                  flag = true; 
    			}
    		}
    	}
    }
    

    理论分析

    排序过程中局部有序。
    在应用了哨兵后初始序列有序情况下效率最高,为O(n)
    最坏、平均时间复杂度为O(n^2),最好时间复杂度为O(n)
    空间复杂度为O(1)

    插入排序

    实现代码

    //每次循环使得[0,i]成为有序序列,元素i找到从后向前正确的插入位置
    public static void insertionSort(int[] arr) {
    	if (arr == null || arr.length < 2) {
    		return;
    	}
    	for (int i = 1; i < arr.length; i++) {
    		for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
    			swap(arr, j, j + 1);
    		}
    	}
    }
    

    理论分析

    时间复杂度与数据初始状况相关,最好情况下也就是初始有序的情况下,时间复杂度为O(n),逆序为最差情况,为O(n2)。平均时间复杂度O(n2)。
    空间复杂度为O(1)
    稳定

    选择排序

    实现代码

    public static void selectionSort(int[] arr) {
    	if (arr == null || arr.length < 2) {
    		return;
    	}
    	//每轮循环找i到arr.length - 1中最小的,和i位置上的数交换
    	for (int i = 0; i < arr.length - 1; i++) {
    		int minIndex = i;
    		for (int j = i + 1; j < arr.length; j++) {
    			minIndex = arr[j] < arr[minIndex] ? j : minIndex;
    		}
    		swap(arr, i, minIndex);
    	}
    }
    
    public static void swap(int[] arr, int i, int j) {
    	int tmp = arr[i];
    	arr[i] = arr[j];
    	arr[j] = tmp;
    }
    

    理论分析

    最好、最差、平均时间复杂度均为O(n^2)
    空间复杂度为O(1)
    不稳定
    时间效率与初始序列无关

    归并排序

    实现代码

    public static void mergeSort(int[] arr) {
    	if (arr == null || arr.length < 2) {
    		return;
    	}
    	mergeSort(arr, 0, arr.length - 1);
    }
    
    public static void mergeSort(int[] arr, int l, int r) {
    	if (l == r) {
    		return;
    	}
    	int mid = l + ((r - l) >> 1);
    	mergeSort(arr, l, mid);//T(N/2)
    	mergeSort(arr, mid + 1, r);//T(N/2)
    	merge(arr, l, mid, r);//O(N)
    	//T(N) = 2*T(2/N) + O(N)
    }
    
    public static void merge(int[] arr, int l, int m, int r) {
    	int[] help = new int[r - l + 1];
    	int i = 0;
    	int p1 = l;
    	int p2 = m + 1;
    	while (p1 <= m && p2 <= r) {
    		help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
    	}
    	while (p1 <= m) {
    		help[i++] = arr[p1++];
    	}
    	while (p2 <= r) {
    		help[i++] = arr[p2++];
    	}
    	for (i = 0; i < help.length; i++) {
    		arr[l + i] = help[i];
    	}
    }
    

    理论分析

    根据master算法,T(N) = 2*T(2/N) + O(N) ,即a=2,b=2,d=1
    所以,时间复杂度为O(NlogN)
    归并排序的最好、最差、平均时间复杂度均为O(NlogN)
    归并排序的空间复杂度为O(N)
    算法效率与初始序列状态无关

    快速排序

    实现代码

    public class Sort {
        //经典快排思路,划分时选右边界做标准,然后从左向右挤
        public static int partition(int[] arr,int l,int r){
            int less = l - 1;
            for(int i = l;i <= r;i++){
                if(arr[i] < arr[r]){
                    swap(arr,++less,i);
                }
            }
            swap(arr,less + 1,r);
            return less + 1;
        }
    
        public static void quickSort(int[] arr,int l,int r){
            if(l >= r)
                return;
            int p = partition(arr,l,r);
            quickSort(arr,l,p - 1);
            quickSort(arr,p + 1,r);
        }
    
        public static void swap(int[] arr,int i,int j){
            int tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
        }
    

    理论分析

    最好、平均时间复杂度为O(nlogn),最坏时间复杂度为O(n^2)
    空间复杂度为O(1)
    时间效率与初始序列有关,初始有序时效率最差为O(n^2)
    不稳定

    堆排序

    代码实现

    //排序,因为每次堆顶元素都被放在了存储堆的数组后的第一个位置,所以排序后为增序,大根堆
    public static void heapSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        for (int i = 0; i < arr.length; i++) {
            heapInsert(arr, i);
        }
        int size = arr.length;
        swap(arr, 0, --size);
        while (size > 0) {
            heapify(arr, 0, size);
            swap(arr, 0, --size);
        }
    }
    
    //新的元素入堆,从下往上调整
    public static void heapInsert(int[] arr, int index) {
        while (arr[index] > arr[(index - 1) / 2]) {
            swap(arr, index, (index - 1) / 2);
            index = (index - 1) / 2;
        }
    }
    
    //当顶部元素出堆,堆内最后一个元素补上堆顶元素的位置,再对该元素进行调整,从上往下调整
    public static void heapify(int[] arr, int index, int size) {
        int left = index * 2 + 1;
        while (left < size) {
            int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
            largest = arr[largest] > arr[index] ? largest : index;
            if (largest == index) {
                break;
            }
            swap(arr, largest, index);
            index = largest;
            left = index * 2 + 1;
        }
    }
    
    
    public static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
    

    理论分析

    时间复杂度O(NlogN),额外空间复杂度O(1)
    建立堆的时间复杂度为O(N)   (应为log1+log2+…+logn收敛于n )
    大根堆:在表示堆的完全二叉树中,任何子树的最大值都是子树的根节点
    小根堆:在表示堆的完全二叉树中,任何子树的最小值都是子树的根节点 
    二叉树左子节点下标2*i+1,右子节点下标2*i+2
    父节点下标(i- 1)/2
    堆一定是完全二叉树,并且是用数组进行存储,使用下标关系进行子父节点对应

    java排序函数逻辑

    1. 长度小于60用插入排序,因为常数项极少,小样本下速度快
    2. 而大样本时使用快排/归并先划分,当样本数变少后再用插入排序。类似于原来是if(L==R) return;现在是LR之间有60个元素,再返回
    3. 基本数据类型(int,float,double...)排序用快排(因为基本数据类型只要相等就一样,没必要保证稳定性)
    4. 对象排序用归并(可以保证稳定性)
      注:一个数组,要求把奇数放左边,偶数放右边,并保持奇数偶数部分内部顺序不变,空间复杂度O(1)时间复杂度O(n)——无法做到,因为要是能做到快排的划分操作就可以保证稳定性,这两个本质上都是0-1操作划分

    总结

    排序算法 时间复杂度 空间复杂度 稳定性 效率是否与初始序列有关
    冒泡排序 最坏、平均为O(n^2),最好为O(n) O(1) 稳定 相关(哨兵机制,如不使用哨兵则无关),初始有序时最好时间复杂度为O(n)
    归并排序 最好、最差、平均均为O(NlogN) O(n) 稳定 无关
    插入排序 最好O(n),最差、平均为O(n^2) O(1) 稳定 相关,初始有序时最好时间复杂度为O(n),逆序时最差为O(n^2)
    选择排序 最好、最差、平均均为O(n^2) O(1) 不稳定 无关
    快速排序 最好、平均为O(nlogn),最坏为O(n^2) O(1) 不稳定 相关,初始有序时效率最差为O(n^2),解决方案为随机选择划分元素
    堆排序 O(NlogN) O(1) 不稳定 堆排这个就不说相不相关了吧
    基数排序 O(d*(r+n)),d为排序码的位数,r为排序码基数(10)d小n大时适合 O(1) 稳定
  • 相关阅读:
    使用CSVDE批量导入命令/出口AD用户
    Everything You Wanted to Know About Machine Learning
    android Vibrator 用法
    oc-25- @property @synthesize
    oc-24-点语法
    oc-23-static
    oc-22-sel
    oc-21-class对象
    oc-20-多态
    oc-19-成员变量修饰符
  • 原文地址:https://www.cnblogs.com/wunsiang/p/12765181.html
Copyright © 2011-2022 走看看