學排序看這一篇就夠了

排序

排序概念

  • 穩定性:兩個相等的數據,經過排序後,如果相對位置沒有變化則爲穩定排序,反之,爲不穩定排序。
  • 內部排序與外部排序:如果一次性可以將所有數據加載到內存中進行排序則爲內部排序;反之,爲外部排序

1.插入排序

1.1 直接插入排序
  • 整個區間被分爲:有序區間與無序區間;

  • 原理:每次選擇無序區間的第一個元素,在有序區間內選擇合適的位置插入。[默認要排序的數據第一個元素是有序的]

  • 應用場景:如果搬移元素個數比較少,就比較適合插入排序,序列接近有序或者數據較少時

  • 穩定性:穩定

  • 時間複雜度:O(n^2)

    • 最優:O(n) -> 有序時
    • 最差:O(n^2) -> 逆序
  • 空間複雜度:O(1)

  • 代碼實現:

    public static void insertSort(int[] array){
    	
        for(int i = 1; i < array.length;i++){
            //有序區間[0,i)
            //無序區間[i,array.length)
            int num = array[i]; //無序區間第一個數
            int end = i-1; //比較的元素位置
            
            while(end >= 0 && num < array[end]){ //當數組不越界同時當前數比相比較的數小的時候
                array[end+1] = array[end]; //移位
                end--;
            }
            
            //插入元素
            array[end+1] = num;
            
        }
    }
    
1.2 希爾排序
  • 原理:採用分組的方式,將序列變成:接近有序,將數據變少;[採用直接插入排序對分組後的數據排序]

  • 應用場景:元素比較凌亂,個數比較多

  • 穩定性:不穩定

  • 時間複雜度:O(n^2)

  • 空間複雜度:O(n)

  • 代碼實現:

    public static void shellSort(int[] array){
    	int gap = array.length;
        while(gap > 1){
            gap = gap/3+1; //分組數
            for(int i = gap;i < array.length;i++){
                
                int num = array[i]; //分組後當前組中無序的第一個元素
                int end = i - gap;
                
                while(end >= 0 && num < array[end]){
                    array[end+gap] = array[end];
                    end -= gap;
                }
                
                array[end+gap] = num;
            }
        }
    }
    

2.選擇排序

2.1 直接選擇排序
  • 原理:每次從無序區間選出最大(或最小)的一個元素,存放在無序區間的最後;直到全部元素排完[n個元素排n-1次]

  • 穩定性:不穩定

  • 時間複雜度:O(n^2)

  • 空間複雜度:O(1)

  • 代碼實現:

    public static void selectSort(int[] array){
    	for(int i = 0;i < array.length-1;i++){
            
            //無序區間:[0,array.length-i)
            //有序區間:[arrar.length-i,array.length)
            int max = 0;
            for(int j = 1; j < array.length-i;j++){
                if(array[j] > array[max]){
                    max = j;
                }
            }
            
            int temp = array[max];
            array[max] = array[array.length-1-i];
            array[array.length-1-i] = temp;
        }
    }
    
    
    //雙向選擇排序
     public static void selectSortPlus(int[] array){
            int begin = 0;
            int end = array.length-1;
    
            while(begin < end){
                int min = begin;
                int max = begin;
    
                for (int i = begin+1; i <= end; i++) {
                    if(array[i] < array[min]){
                        min = i;
                    }
                    if(array[i] > array[max]){
                        max = i;
                    }
                }
    
                swap(array,min,begin);
    
                if(max == begin){
                    max = min;
                }
    
                swap(array,max,end);
                begin++;
                end--;
            }
    
        }
    
2.2 堆排序
  • 原理:利用堆這種數據結構;首先建堆,利用堆刪除的思想來進行排序(用堆頂元素與堆中最後一個元素進行交換,將堆中元素減少一個,再將堆頂元素向下調整)

  • 穩定性:不穩定

  • 時間複雜度:O(n*log(n))

  • 空間複雜度:O(1)

    public satic void headSort(int[] array,int size){  //array排序數組,size數組長度
        creatHeap(array,size);
        
        for(int i = size-1;i >= 0;i--){
            swap(array,i,0); //將最大的節點與最後一個節點交換,以確保排序數組
           	shiftDown(array,size-1,0);//默認斬掉最後一個節點
        }
    }
    
    
    //構建堆
    private static void createHeap(int[] array,int size){
        int parent = (size-2)%2;
        
        for(int i = parent;i >= 0;i--){
            shiftDown(array,size,i);
        }
    }
    
    //向下調整
    public static void shiftDown(int[] array,int size,int index){
        int left = 2*index + 1;
        
        while(left < size){
            int max = left;
            int right = left + 1;
            if(right < size){
                if(array[right] > array[left]){
                    max = right;
                }
            }
            
            if(array[index] >= array[max]){
                break;
            }
            
            int temp = array[index];
            array[index] = array[max];
            array[max] = temp;
            
            index = max;
            left = 2*max+1;
        }
    }
    

3.冒泡排序

  • 原理:在無序區間,通過相鄰數的比較,將最大的數冒泡到無序區間的最後,持續這個過程,直到數組整體有序

  • 穩定性:穩定

  • 時間複雜度:O(n^2)

  • 空間複雜度:O(1)

    public static void bubbleSort(int[] arr){
        for(int i = 0;i < arr.length-1;i++){
            boolean isSorted = true;
            for(int j = 0;i < arr.length-1-i;j++){
                if(array[j] > arr[j+1]){
                    swap(array,j,j+1); //交換
                    isSorted = false; 
                }
            }
            if(isSorted){ //如果沒有交換則已排序完成
                break;
            }
        }
    }
    

4.快速排序

  • 原理:默認升序

    1. 從區間中取出一個數據作爲基準值,按照該基準值將區間分割左右兩個部分
    2. 按照快排思想排序左半部分
    3. 按照快排思想排序右半部分
  • 應用場景:數據量大比較隨機(數據雜亂)

  • 穩定性:不穩定

  • 時間複雜度:O(n*logn)

  • 空間複雜度:O(logn)

    //交換法
    //quickSort(array,0,array.length);[begin,end)
    public static void quickSort(int[] array,int begin,int end){
        if(end - begin > 1){
            int left = begin;
            int right = end-1;
            int baseNum = array[right];
            
            while(left < right){
                while(left < right && array[left] <= baseNum){
                    left++;
                }
                
                while(left < right && array[right] >= baseNum){
                    right--;
                }
                
                if(left < right){
                    swap(array,left,right);
                }
            }
            
            swap(array,end,left);
            
            quickSort(array,begin,left);
            quickSort(array,left+1,end);
        }
    }
    
    //挖坑法
    //quickSort(array,0,array.length-1);[begin,end]
     public static void quickSort(int[] array,int begin,int end){
            if(begin < end){
                int baseNum = array[begin]; //基數
                int left = begin;
                int right = end;
    
                while(left < right){
                    while(left < end && array[right] >= baseNum){
                        right--;
                    }
    
                    if(left < end){
                        array[left] = array[right];
                    }
    
                    while(left < end && array[left] =< baseNum){
                        left++;
                    }
    
                    if(left < end){
                        array[right] = array[left];
                    }
                }
    
                array[left] = baseNum;
                quickSort(array,begin,left-1);
                quickSort(array,left+1,end);
            }
        }
    
     //[ left , right):找到比基數小的給前面移動
        public static void quickSort(int[] array,int left,int right){
            if(right - left > 1){
                int cur = left;
                int prev = cur - 1;
                int key = array[right-1];
    
                while(cur < right){
                    if(key > array[cur] && ++prev != cur){  //++prev != cur是因爲中間沒有比基數大的了不用移動,自排序
                        swap(array,prev,cur);
                    }
                    ++cur;
                }
    
                if(++prev != right-1){
                    swap(array,prev,right-1);
                }
    
                quickSort(array,left,prev);
                quickSort(array,prev+1,right);
            }
        }
    

非遞歸實現:

    public static void quick(int[] array){
        Stack<Integer> stack = new Stack<Integer>();

        stack.push(array.length);
        stack.push(0);

        while(!stack.isEmpty()){
            int left = stack.pop();
            int right = stack.pop();

            if(right - left > 1){
                int div = quickSort(array, left, right);
                stack.push(right);
                stack.push(div+1);
                stack.push(div);
                stack.push(left);
            }
        }
    }

    public static int quickSort(int[] array,int left,int right){
            int cur = left;
            int prev = cur - 1;
            int key = array[right-1];

            while(cur < right){
                if(key > array[cur] && ++prev != cur){  //++prev != cur是因爲中間沒有比基數大的了不用移動,自排序
                    swap(array,prev,cur);
                }
                ++cur;
            }

            if(++prev != right-1){
                swap(array,prev,right-1);
            }

            return prev;
    }

5.歸併排序

  • 原理:將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使

    子序列段間有序。若將兩個有序表合併成一個有序表,稱爲二路歸併

  • 穩定性:穩定

  • 時間複雜度:O(NlogN)

  • 空間複雜度:O(n)

//合併兩個有序數組
private static void merget(int[] array,int left,int mid,int right,int[] temp){
        int index1 = left;
        int index2 = mid+1;
        int index = 0;
        while(index1 <= mid && index2 <= right){
            if(array[index1] <= array[index2]){
                temp[index++] = array[index1++];
            }else{
                temp[index++] = array[index2++];
            }
        }

        while(index1 <= mid){
            temp[index++] = array[index1++];
        }

        while(index2 <= right){
            temp[index++] = array[index2++];
        }

//        index = 0;
//        while(left <= right){
//            array[left++]=temp[index++];
//        }
        System.arraycopy(temp,0,array,left,right-left+1);
    }
//遞歸實現歸併排序[left,right]
 private static void mergeSort(int[] arr, int left, int right, int[] temp) {
        if(left < right){
            int mid = (left+right)/2;

            //向左遞歸
            mergeSort(arr,left,mid,temp);
            //向右遞歸
            mergeSort(arr,mid+1,right,temp);
            //合併
            merge(arr,left,mid,right,temp);
        }
    }
//非遞歸 實現歸併排序
public static void mergetSort(int[] array){
        int gap = 1;
        int[] temp = new int[array.length];
        while (gap < array.length){
            for(int i= 0;i < array.length;i+=gap*2){
                int mid = i+gap;
                int right = mid+gap;

                if(mid >= array.length){
                    mid = array.length-1;
                }

                if(right >= array.length){
                    right = array.length-1;
                }

                merget(array,i,mid,right,temp);
            }
            gap*=2;
        }
    }

總結

排序方法 最好 平均 最壞 空間複雜度 穩定性
冒泡排序 O(n) O(n^2) O(n^2) O(1) 穩定
插入排序 O(n) O(n^2) O(n^2) O(1) 穩定
選擇排序 O(n^2) O(n^2) O(n^2) O(1) 不穩定
希爾排序 O(n) O(n^1.3) O(n^2) O(1) 不穩定
堆排序 O(n*logn) O(n*logn) O(n*logn) O(1) 不穩定
快速排序 O(n*logn) O(n*logn) O(n^2) O(logn)~O(n) 不穩定
歸併排序 O(n*logn) O(n*logn) O(n*logn) O(1) 穩定
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章