十大經典排序【Java實現,手工做坊式】

  終於把排序這個硬骨頭,可是又很基礎的知識點,本身手撕了一遍!以前,使用Python看着算法導論的書手撕過一遍,印象不是很深入,容易忘記!好記性不如爛筆頭!多本身思考解決問題html

 

 

1,交換類CAS【最簡單】

  穩定,n^2

  1.1冒泡法【①普通冒泡;②雞尾酒法】

package com.cnblogs.mufasa.demo1_CAS;

import org.junit.Test;

public class Solution1_bubble {

    //1,普通冒泡,由左及右一遍遍來刷,時間複雜度O(n^2)
    public void normal_Bubble(int[] nums){//默認直接,小到大
        int len=nums.length;
        for(int i=0;i<len-1;i++){
            for(int j=0;j<len-i-1;j++){
                if(nums[j]>nums[j+1]){
                    int temp=nums[j+1];
                    nums[j+1]=nums[j];
                    nums[j]=temp;
                }
            }
        }
    }

    //2,雞尾酒冒泡,左右左右變換,時間複雜度O(n^2)可是理論上更加優化
    public void cocktail_Bubble(int[] nums) {
        int len = nums.length;
        int x=0,y=len-1;
        while (x<y){
            for(int i=x;i<y;i++){
                if(nums[i]>nums[i+1]){
                    int temp=nums[i+1];
                    nums[i+1]=nums[i];
                    nums[i]=temp;
                }
            }
            y--;
            for(int i=y;i>0;i--){
                if(nums[i]<nums[i-1]){
                    int temp=nums[i-1];
                    nums[i-1]=nums[i];
                    nums[i]=temp;
                }
            }
        }
    }

    public void printOut(int[] nums){
//        int len=nums.length;
        for(int temp:nums){
            System.out.print(temp+",");
        }
        System.out.println();
    }


    @Test
    public void test(){
        int[] nums={5,14,478,6,41,698,14,5,3};
        printOut(nums);

//        normal_Bubble(nums);
        cocktail_Bubble(nums);
        printOut(nums);

    }

}
View Code

 

圖-普通冒泡排序java

 

 

圖-雞尾酒排序node

  1.2快速排序【①單邊循環快排;②雙邊循環快排】

  CAS排序的一種,時間複雜度平均爲O(nlogn),最壞爲O(n^2)python

  Arrays.sort()底層就是使用雙軸快排實現的 http://www.javashuo.com/article/p-usbihhvi-cu.htmlgit

package com.cnblogs.mufasa.demo1_CAS;

import org.junit.Test;

public class Solution2_quick {

    //1,單邊循環法,快排
    public void quick_sort1(int[] nums){
        sin_Quick1(nums,0, nums.length-1);//前閉後閉
    }

    private void sin_Quick1(int[] nums,int x,int y) {
        if (y - x == 1) {
            if (nums[x] > nums[y]) {
                int temp = nums[x];
                nums[x] = nums[y];
                nums[y] = temp;
            }
            return;
        } else if (x >= y) {
            return;
        }

        int mark=x,povit=nums[x];
        for(int i=x;i<=y;i++){
            if(nums[i]<povit){
                mark++;
                int temp=nums[i];
                nums[i]=nums[mark];
                nums[mark]=temp;
            }
        }
        int temp=nums[mark];
        nums[mark]=nums[x];
        nums[x]=temp;

        sin_Quick1( nums, x, mark-1);
        sin_Quick1( nums, mark+1, y);
    }

    //2,雙邊循環法,快排
    public void quick_sort2(int[] nums){
        sin_Quick2(nums,0, nums.length-1);//前閉後閉
    }

    private void sin_Quick2(int[] nums,int x,int y){
        if(y-x==1){
            if(nums[x]>nums[y]){
                int temp=nums[x];
                nums[x]=nums[y];
                nums[y]=temp;
            }
            return;
        }else if(x>=y){
            return;
        }

        int pivot=nums[x];
        int left=x,right=y;
        boolean flag=true;

        while (left<right){
            if(flag){
                if(pivot<=nums[right]){
                    right--;
                }else {
                    flag=false;
                }
            }else {
                if(nums[left]<=pivot){
                    left++;
                }else {
                    int temp=nums[left];
                    nums[left]=nums[right];
                    nums[right]=temp;
                    flag=true;
                    right--;
                }
            }
        }
        int temp=nums[left];
        nums[left]=nums[x];
        nums[x]=temp;
        sin_Quick2(nums,x,left-1);
        sin_Quick2(nums,left+1,y);
    }



    public void printOut(int[] nums){
        for(int temp:nums){
            System.out.print(temp+",");
        }
        System.out.println();
    }

    @Test
    public void test(){
        int[] nums={13,14,478,6,41,698,12,5,3};
//        int[] nums={5,2,9,6,1,0,3,7,8};
        printOut(nums);

        quick_sort1(nums);
//        quick_sort2(nums);
        printOut(nums);
    }
}
View Code

 

 

圖-快速排序github

 

2,選擇排序【最簡單】

  原理:每次選擇極值往同一個方向推過去,有點像這樣:咱們在垃圾堆裏找值錢的物件,每次找最值錢的那一件丟到咱們的蛇皮袋子中,下一次在剩餘的垃圾中找最值錢的物件,再次丟到咱們的寶貝蛇皮袋子裏,依次循環,那麼最後咱們完成地球清潔工做後,咱們的蛇皮袋子裏的垃圾價值由下到上價值依次遞增!!!這個就是普通的選擇排序!!!不穩定,n^2;不穩定的緣由:由小到大排序下面的數組[6,6,1]算法

  堆排序,利用了堆這種數據結構的特性來輔助完成排序工做,時間複雜度爲O(nlogn),須要開闢額外空間【其實咱們不開闢額外空間也能夠,把原始數組空間直接利用當作堆的內存空間來用,以後出堆的時候前面出,後面進】shell

package com.cnblogs.mufasa.demo2_select;

import org.junit.Test;

import java.util.PriorityQueue;

public class Solution1_select {
    private static final int MAX=Integer.MAX_VALUE;

    //1,普通選擇排序,時間複雜度爲O(n^2)
    public void normal_select1(int[] nums){
        int len=nums.length,min,loc;
        for(int i=0;i<len;i++){
            min=MAX;
            loc=i;
            for(int j=i;j<len;j++){
                if(nums[j]<min){
                    min=nums[j];
                    loc=j;
                }
            }
            nums[loc]=nums[i];
            nums[i]=min;
        }
    }

    //2,利用最大堆、最小堆特性進行排序【Java容器中的優先隊列就是使用的堆元素】
    public void normal_select2(int[] nums){
        int len=nums.length;
        PriorityQueue<Integer> queue=new PriorityQueue<>(len);
        for(int temp:nums){
            queue.add(temp);
        }
        for(int i=0;i<len;i++){
            nums[i]=queue.poll();
        }
    }

    //3,數據結構堆的手動實現
    public void normal_select3(int[] nums) throws Exception {
        Heap heap=new Heap(false);
        for(int temp:nums){
            heap.add(temp);
        }
        for(int i=nums.length-1;i>=0;i--){
            nums[i]=heap.poll();
        }
    }

    public void printOut(int[] nums){
        for(int temp:nums){
            System.out.print(temp+",");
        }
        System.out.println();
    }

    @Test
    public void test() throws Exception {
        int[] nums={13,14,478,6,41,698,12,5,3};
//        int[] nums={5,2,9,6,1,0,3,7,8};
        printOut(nums);

//        normal_select1(nums);
//        normal_select2(nums);
        normal_select3(nums);
        printOut(nums);
    }
}
View Code

數據結構-堆 Heap.java數組

package com.cnblogs.mufasa.demo2_select;

public class Heap {
    private static final int CAPACITY=16;
    private static final boolean TYPE=true;

    private static int[] nums;
    private int capacity=16;
    int size=0;

    private boolean type=true;//true由小到大,false由大到小
    public Heap(){
        this(CAPACITY);
    }

    public Heap(int capacity){
        this(capacity,TYPE);
    }

    public Heap(boolean type){
        this(CAPACITY,type);
    }

    public Heap(int capacity,boolean type){
        this.capacity=capacity;
        this.type=type;
        nums=new int[capacity];
    }


    //數據添加
    public void add(int num){
        if(size+1>=capacity){
            dilatate();
        }
        nums[size+1]=num;
        reSortUp(size+1);
        size++;
    }

    private void reSortUp(int index){
        if(type){//由小到大
            while (index!=1){
                if(nums[index/2]>nums[index]){
                    int temp=nums[index];
                    nums[index]=nums[index/2];
                    nums[index/2]=temp;
                    index/=2;
                }else if(nums[index/2]==nums[index]){
//                    throw new IllegalArgumentException("數據結構-堆不接受重複數據輸入");
                    break;
                }else {
                    return;
                }
            }
        }else {//由大到小
            while (index!=1){
                if(nums[index/2]<nums[index]){
                    int temp=nums[index];
                    nums[index]=nums[index/2];
                    nums[index/2]=temp;
                    index/=2;
                }else if(nums[index/2]==nums[index]){
//                    throw new IllegalArgumentException("數據結構-堆不接受重複數據輸入");
                    break;
                }else {
                    return;
                }
            }
        }
    }

    //數據輸出,而且清楚該數據
    public int poll() throws Exception {
        if(size>0){
            int temp=nums[1];
            nums[1]=nums[size];
            reSortDown();
            size--;
            return temp;
        }else {
            throw new Exception("數據爲空");
        }
    }

    private void reSortDown(){
        int index=1;
        int L,R;
        if(type){//由小到大
            while (index<size){
                L=index*2;
                R=L+1;
                if(R<=size){
                    boolean flag=nums[L]<nums[R];
                    int min=(flag?nums[L]:nums[R]);
                    if(nums[index]>min){
                        if(flag){
                            int temp=nums[index];
                            nums[index]=nums[L];
                            nums[L]=temp;
                            index=L;
                        }else {
                            int temp=nums[index];
                            nums[index]=nums[R];
                            nums[R]=temp;
                            index=R;
                        }
                    }else {
                        return;
                    }
                }else if(L<=size){
                    if(nums[index]>nums[L]){
                        int temp=nums[index];
                        nums[index]=nums[L];
                        nums[L]=temp;
                    }
                    return;
                }else {
                    return;
                }
            }
        }else {//由大到小
            while (index<size){
                L=index*2;
                R=L+1;
                if(R<size){
                    boolean flag=nums[L]<nums[R];
                    int max=(flag?nums[R]:nums[L]);
                    if(nums[index]<max){
                        if(flag){
                            int temp=nums[index];
                            nums[index]=nums[R];
                            nums[R]=temp;
                            index=R;
                        }else {
                            int temp=nums[index];
                            nums[index]=nums[L];
                            nums[L]=temp;
                            index=L;
                        }
                    }else {
                        return;
                    }
                }else if(L<size){
                    if(nums[index]<nums[L]){
                        int temp=nums[index];
                        nums[index]=nums[L];
                        nums[L]=temp;
                    }
                    return;
                }else {
                    return;
                }
            }
        }
    }

    //數據輸出,不清除該數據
    public int peek() throws Exception {
        if(size>0){
            return nums[0];
        }else {
            throw new Exception("數據爲空");
        }
    }

    //數據擴容,二倍擴容
    private void dilatate(){
        capacity=capacity<<1;
        int[] pre=new int[capacity];
        for(int i=1;i<=size;i++){
            pre[i]=nums[i];
        }
        nums=pre;
    }

}

class Client{
    public static void main(String[] args) throws Exception {
        Heap heap=new Heap(4,true);
//        Heap heap=new Heap(4,false);
        heap.add(5);
        heap.add(3);
        heap.add(3);
        heap.add(7);
        heap.add(1);
        heap.add(0);
        heap.add(8);
        heap.add(8);

        int len=heap.size;
        for(int i=0;i<len;i++){
            System.out.print(heap.poll()+",");
        }
    }
}
/*
0,1,3,5,7,8,
8,7,5,3,1,0,
 */
View Code

 

 

圖-普通選擇排序數據結構

 

3,插入排序【簡單】

  穩定,n^二、希爾有點麻煩,可是理解其本質就很簡單了

package com.cnblogs.mufasa.demo3_insert;

import org.junit.Test;

public class Solution1_insert {

    //1,普通插入排序,時間複雜度O(n^2)
    public void normal_insert1(int[] nums){
        int len=nums.length;
        for(int i=1;i<len;i++){
            sinInsert(nums,i);
        }
    }

    private void sinInsert(int[] nums,int loc){
        for(int i=loc-1;i>=0;i--){
            if(nums[i]<=nums[loc]){
                break;
            }else {
                int temp=nums[i];
                nums[i]=nums[loc];
                nums[loc]=temp;
                loc--;
            }
        }
    }

    //2,希爾排序,多路進行併發排序,時間複雜度爲O(n^1.3)
    //只要是利用了分治併發的操做,後期能夠在Java併發學習中將這個進行知識整合,bingo
    public void shell_insert2(int[] nums){
        int len=nums.length;
        int step=len/2;
        while (step!=0){

            for(int i=0;i<step;i++){
                sinShellInsert1(nums,i,len, step);
            }
            step/=2;
        }
    }

    //2.1希爾排序的一級功能
    private void sinShellInsert1(int[] nums,int x,int len, int step){
        x+=step;
        while(x<len){
            sinShellInsert2(nums,x,step);
            x+=step;
        }
    }

    //2.2希爾排序的二級功能
    private void sinShellInsert2(int[] nums,int x, int step){
        while (x>=step){
            if(nums[x-step]<=nums[x]){
                break;
            }else {
                int temp=nums[x];
                nums[x]=nums[x-step];
                nums[x-step]=temp;
                x-=step;
            }
        }
    }


    public void printOut(int[] nums){
        for(int temp:nums){
            System.out.print(temp+",");
        }
        System.out.println();
    }

    @Test
    public void test(){
//        int[] nums={13,14,478,6,41,698,12,5,3};
        int[] nums={5,2,9,6,1,0,3,7,8};
        printOut(nums);

//        normal_insert1(nums);
        shell_insert2(nums);
        printOut(nums);
    }
}
View Code

 

 

 

圖-普通插入排序

 

圖-希爾插入排序

 

4,歸併排序【中等難度吧!仍是有點難度吧】

第一步:申請空間,使其大小爲兩個已經 排序序列之和,該空間用來存放合併後的序列
第二步:設定兩個 指針,最初位置分別爲兩個已經排序序列的起始位置
第三步:比較兩個指針所指向的元素,選擇相對小的元素放入到合併空間,並移動指針到下一位置
重複步驟3直到某一指針超出序列尾
將另外一序列剩下的全部元素直接複製到合併序列尾
package com.cnblogs.mufasa.demo4_merge;

import org.junit.Test;

public class Solution {
    static int[] arr;
    //1,由小往大歸併,2-4-8,將小份問題組合成大份問題
    public void merge_sort1(int[] nums){
        int len=nums.length;
        arr=new int[len];
        sort1(nums,len,1);
    }

    private void sort1(int[] nums,int len,int step){
        int L1=0,mid=step-1,R=mid+step;
        int addNum=2*step;
        while (mid<len-1){
            if(R<len){
                merge(nums,L1,mid,R);
            }else {
                merge(nums,L1,mid,len-1);
            }
            mid+=addNum;
            L1+=addNum;
            R+=addNum;
        }
        if(step<len){
            sort1(nums,len,step*2);
        }
    }
    
    //2,由大往小歸併,8-4-2【本質上仍是同樣,不過將問題由大拆分紅小的】
    public void merge_sort2(int[] nums){
        arr=new int[nums.length];
        sort2(nums, 0, nums.length-1);
    }

    //有點相似於二叉樹的後續遍歷coding
    private void sort2(int[] nums,int L,int R){//左閉右開
        if (L == R) {
            return;
        }
        int mid=(L+R)>>1;
        sort2(nums,L,mid);
        sort2(nums,mid+1,R);
        merge(nums,L,mid,R);
    }

    private void merge(int[] nums,int L,int mid,int R){
        int i = L;
        int p1 = L;
        int p2 = mid + 1;

        while(p1 <= mid && p2 <= R) {
            arr[i++] = nums[p1] < nums[p2] ? nums[p1++] : nums[p2++];
        }
        while(p1 <= mid) {
            arr[i++] = nums[p1++];
        }
        while(p2 <= R) {
            arr[i++] = nums[p2++];
        }

        for(i = L; i <= R; i++) {
            nums[i] = arr[i];
        }

    }


    public void printOut(int[] nums){
        for(int temp:nums){
            System.out.print(temp+",");
        }
        System.out.println();
    }

    @Test
    public void test(){
//        int[] nums={13,14,478,6,41,698,12,5,3};
        int[] nums={5,2,9,6,1,0,3,7,8};
        printOut(nums);

        merge_sort1(nums);
//        merge_sort2(nums);
        printOut(nums);
    }
}
View Code
 

 

圖-歸併排序

5,分割線小結

  上述的冒泡排序、選擇排序、插入排序、歸併排序都是屬於比較類排序,他們大多數不須要開闢額外地址空間,時間複雜度大體範圍爲O(N^2)~O(nlogn),其中希爾排序的時間複雜度爲O(n^1.3)

  下面將要給你們介紹的是另一類排序方法,非比較類排序!!!他們的時間複雜度能夠降的很低,可是代價是要開闢額外的內存空間。

6,計數排序【簡單】

  算法複雜度O(n+k)

  本質就是經過各個數值的個數,其中有個鍵值對——鍵爲這個數值大小,值爲其在原始數組中的個數;由鍵的大小及其個數進行數組還原。

package com.cnblogs.mufasa.demo5_count;

import org.junit.Test;

import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

public class Solution {

    //1,計數排序,原理很簡單,統計個數,還原!!!簡單粗暴
    //這裏直接使用TreeMap實現的
    public void count_sort1(int[] nums){
        TreeMap<Integer,Integer> hm=new TreeMap<>();
        for(int n:nums){
            Object temp=hm.get(n);
            if(temp==null){
                hm.put(n,1);
            }else {
                hm.put(n,(Integer)temp+1);
            }
        }
        int loc=0;
        for(Map.Entry<Integer,Integer> entry:hm.entrySet()){
            for(int i=0;i<entry.getValue();i++){
                nums[loc]=entry.getKey();
                loc++;
            }
        }
    }

    //2,直接判斷最大的數值是多少來進行數組存儲
    public void count_sort2(int[] nums){
        int[] cnts=new int[findMax(nums)+1];
        for(int temp:nums){
            cnts[temp]++;
        }
        int loc=0;
        for(int i=0;i<cnts.length;i++){
            for(int j=0;j<cnts[i];j++){
                nums[loc]=i;
                loc++;
            }
        }
    }

    private int findMax(int[] nums){
        int max=Integer.MIN_VALUE;
        for(int temp:nums){
            if(temp>max) max=temp;
        }
        return max;
    }

    public void printOut(int[] nums){
        for(int temp:nums){
            System.out.print(temp+",");
        }
        System.out.println();
    }

    @Test
    public void test(){
//        int[] nums={13,14,478,6,41,698,12,5,3};
        int[] nums={13,14,478,6,41,698,12,5,3,12,13,400,12};
//        int[] nums={5,2,9,6,1,0,3,7,8};
        printOut(nums);

//        count_sort1(nums);
        count_sort2(nums);
        printOut(nums);
    }
}
View Code

 

 

圖-計數排序

 

7,桶排序【簡單】

  這個和計數排序有點類似,雖然不是統計個數,可是他把各個位【十位、百位】分桶丟進不一樣的bucket中?!垃圾分類,不一樣的垃圾先進行大類劃分,以後在進行小類的劃分。

package com.cnblogs.mufasa.demo6_bucket;

import org.junit.Test;

public class Solution {
    static class Linked{
        int value;
        Linked pre;
        Linked next;
        public Linked(int value){
            this.value=value;
        }
        public void insert(Linked node,int value){
            if(value<node.value){
                if(next==null){
                    next=new Linked(node.value);
                    node.next.pre=node;
                    node.value=value;
                }else {
                    Linked newNode=new Linked(node.value);
                    newNode.next=node.next;
                    node.next.pre=newNode;
                    node.value=value;
                    node.next=newNode;
                    newNode.pre=node;
                }
            }else {
                if(node.next==null){
                    node.next=new Linked(value);
                    node.next.pre=node;
                }else {
                    insert(node.next,value);
                }
            }
        }
    }

    public void bucket_sort1(int[] nums){
        Linked[] linkeds=new Linked[10];
        for(int temp:nums){
            int highN=temp/10;
            if(linkeds[highN]==null){
                linkeds[highN]=new Linked(temp);
            }else {
                linkeds[highN].insert(linkeds[highN],temp);
            }
        }
        int loc=0;
        for(Linked linked:linkeds){
            Linked preNode=linked;
            while (preNode!=null){
                nums[loc]=preNode.value;
                loc++;
                preNode=preNode.next;
            }
        }
    }

    public void printOut(int[] nums){
        for(int temp:nums){
            System.out.print(temp+",");
        }
        System.out.println();
    }

    @Test
    public void test(){
        int[] nums={13,14,47,6,41,69,12,5,3};
//        int[] nums={5,2,9,6,1,0,3,7,8};
//        int[] nums={5,2,9,6,1,0,3,7,8,};
        printOut(nums);

        bucket_sort1(nums);
//        quick_sort2(nums);
        printOut(nums);
    }
}
View Code

 

 

圖-桶排序

 

8,基數排序【簡單】

  須要使用到隊列數據結構!把個位十位....就和垃圾分類同樣逐個丟進對應的隊列,所有丟進去以後在逐個出隊,還原反覆屢次【取決於最大值的位數】

package com.cnblogs.mufasa.demo7_radix;

import org.junit.Test;

import java.util.LinkedList;
import java.util.Queue;

public class Solution {
    static class Node{
        int value;
        public Node(int value){
            this.value=value;
        }
    }

    static class myRadixBucket<Node>{
        LinkedList<Node> [] queues=new LinkedList[10];
        public myRadixBucket(){
            for(int i=0;i<10;i++){
                queues[i]=new LinkedList<>();
            }
        }
        public LinkedList<Node>[] getInstance(){
            return queues;
        }
    }

    public void radix_sort1(int[] nums,int loop){
        myRadixBucket mr=new myRadixBucket();
        LinkedList<Node> [] queues=mr.getInstance();//須要用到隊列

        for(int i=0;i<loop;i++){
            int loc1=(int) Math.pow(10,i);
            for(int temp:nums){
                queues[temp/loc1%10].add(new Node(temp));
            }
            int loc=0;
            for(Queue<Node> queue:queues){
                int len=queue.size();
                Node preNode;
                for(int j=0;j<len;j++){
                    preNode=queue.poll();
                    nums[loc]=preNode.value;
                    loc++;
                }
            }
        }
    }


    public void printOut(int[] nums){
        for(int temp:nums){
            System.out.print(temp+",");
        }
        System.out.println();
    }

    @Test
    public void test(){
        int[] nums={13,14,678,6,41,498,12,5,3};
//        int[] nums={5,2,9,6,1,0,3,7,8};
        printOut(nums);

        radix_sort1(nums,3);//這裏的loop與原始數組中最大數值的位長度相等,這裏的原始數據最大值爲678爲百位取值loop=3
//        quick_sort2(nums);
        printOut(nums);
    }
}

/*
41,12,13,3,14,5,6,678,498,  【第一次,基數排序】
3,5,6,12,13,14,41,678,498,  【第二次,基數排序】
3,5,6,12,13,14,41,498,678,  【第三次,基數排序】
注意每一次都是進行了每一個位【個位、十位、百位】上的有序整理
 */
View Code

 

 

圖-基數排序

 

後面附上我以前使用Python寫的排序算法彙總:十大經典排序算法(python實現)(原創)

相關文章
相關標籤/搜索