1. 程式人生 > 其它 >java資料結構與演算法

java資料結構與演算法

java資料結構與演算法


java演算法

  • 時間複雜度:演算法開始執行到結束執行時之間的時間,

    O(1):常量級時間複雜度 O(n):線性級時間複雜度 O(n^2):平方級時間複雜度

    O(n^3):立方級時間複雜度 O(logn):對數階時間複雜度

    • 演算法總的時間複雜度等於量級最大的那段程式碼的時間複雜度
    • 巢狀程式碼的複雜度等於巢狀內外層程式碼複雜度的乘積

java排序演算法

排序方式 時間複雜度(平均) 最好時間複雜度 最壞時間複雜度 空間複雜度 穩定性
選擇排序 O(n²) O(n²) O(n²) O(1) 不穩定
氣泡排序 O(n²) O(n) O(n²) O(1) 穩定
插入排序 O(n²) O(n) O(n²) O(1) 穩定
希爾排序 O(nlog₂n) O(nlog₂²n) O(nlog₂²n) O(1) 不穩定
歸併排序 O(nlog₂n) O(nlog₂n) O(nlog₂n) O(n) 穩定
快速排序 O(nlog₂n) O(nlog₂n) O(n²) O(log₂n) 不穩定
計數排序 O(n+k) O(n+k) O(n+k) O(k) 穩定
基數排序 O(n*k) O(n*k) O(n*k) O(n+k) 穩定
桶排序 O(n+k) O(n) O(n+k) O(n+k) 穩定
堆排序 O(n²) O(n²) O(n²) O(1) 穩定

選擇排序:

  • 每一次選擇陣列中最小的資料然後往陣列的開頭前移,將陣列截成array.lenth-1個數組後進行按照該演算法規則將每一個擷取的陣列進行運算

public static int[] selectSort(int[] array) {
    int minPro=0;
    for (int i = 0; i < array.length-1; i++) {
        minPro=i;
        for (int j = i+1; j < array.length; j++) {
            if (array[minPro]>array[j]) {
                minPro=j;
            }
        }
        int tmp=array[minPro];
        array[minPro]=array[i];
        array[i]=tmp;
    }
    return array;
}

氣泡排序:

  • 氣泡排序:每一輪迴圈依次比較陣列中的相鄰的兩個資料,每次比較出較大值後交換較大值與較小值的位置,直到遇到比它還大的資料時停止後進行下一輪迴圈

for (int i = array.length-1; i > 0; i--) {
    for (int j = 0; j < i; j++) {
        if (array[j]>array[j+1]) {
            int tmp=array[j];
            array[j]=array[j+1];
            array[j+1]=tmp;
        }
    }
}

插入排序:

  • 從陣列的第二個原始開始,每一輪迴圈依次向前比較相鄰的兩個資料,每次比較出較小值後較小值向前交換,直到遇到比它還小的資料進行下一輪迴圈


    for (int i = 0; i < array.length; i++) {
        for (int j = i; j >0 ; j--) {
            if (array[j]<array[j-1]) {
                int tmp=array[j];
                array[j]=array[j+1];
                array[j+1]=tmp;
            }
        }
    }
    

希爾排序:

  • 選擇一個間距值,從陣列頭開始,對間距為指定的間距值的倍數的元素進行分組後對組內進行插入排序後,間距值減小直到為1重複上述操作


//希爾排序:確定間距(間距值不一定開始間距值一般為陣列長度的一半)區間每一輪的間距減半(一定)直到間距為1,
//每次迴圈從確定的間距值開始直至陣列尾依次與它相距間距值的數進行比較後插入排序直至等於間距
//優化:間距值通過演算法算出 Knut序列(h=h*3+1)上間距與下間距之間的關係
//p確定每輪迴圈的間距值
for (int p = array.length/2; p > 0; p/=2) {
   //從間距值開始依次向前相同間距的數比較直到陣列尾
   for (int i = p; i < array.length; i++) {
       //對間距值為指定的間距的倍數的元素形成陣列進行比較
       for (int j = i; j > p-1; j-=p) {
           //哪個數值較小則往前插入即交換順序
           if (array[j]<array[j-p]) {
               int tmp=array[j];
               array[j]=array[j+1];
               array[j+1]=tmp;
           }
       }
   }
}

歸併排序:

  • 對陣列進行折半切割為左右兩個部分,分別對左右兩個部分再進行切割持續左右分割直到左右元素只有一個,分佈對左右進行排序並歸併起來,利用了遞迴呼叫思想


    //left左索引ringht右索引
    public static void sort(int[] array,int left,int right) {
        //當每一輪陣列的左右索引值相等時即只有一個元素時結束
        if (left==right) return;
        //每一輪陣列的中間索引
        int mid=left+(right-left)/2;
        //每一輪陣列的左邊排序
        sort(array, left, mid);
        //每一輪陣列的右邊排序
        sort(array, mid+1, right);
        //每一輪陣列的左右歸併
        merge(array, left, mid+1, right);
    }
    //left左索引ringht右索引rightBound右邊界
    public static void merge(int[] array,int left,int right,int rightBound) {
        //中間陣列儲存陣列中的拉下來的元素
        int[] tmp=new int[rightBound-left+1];
        // 左索引為頭 右索引-1為中間索引 j右索引
        int i=left,mid=right-1,k=0,j=right;
        //當左索引未達到中間索引時並且右索引未達到右邊界 比較左右兩邊的值哪邊較小則拉至臨時陣列k索引中,後索引後移
        while (i <=mid && j<=rightBound) {
            tmp[k++]= array[i]<=array[j]?array[i++]:array[j++];
        }
        //當上述操作完後存在幾個較大值未被移入臨時陣列中(左右兩邊剩下的較大的數已經排好序了)
        //1.當在左邊時將左邊的元素拉下來
        while (i<=mid) tmp[k++]=array[i++];
        //2.當在右邊時將右邊的元素拉下來
        while (j<=right) tmp[k++]=array[j++];
        //每一輪將左索引以後的第m位元素賦值回給原陣列
        for (int m = 0; m < tmp.length; m++) {
            array[m+left]=tmp[m];
        }
    }
    

快速排序:

  • 每一次選擇一個基準值,比基準值大的元素放在右邊序列,比基準值小的元素放在左邊序列,不斷迴圈直到左邊序列的指標超過右指標時(優化:基準值的設定可以隨機選擇)


    public static void sort(int[] array,int left,int right) {
        if (left>=right) return;
        //將每輪迴圈後的左指標作為下一輪迴圈的基準值
        int mid=partition(array, left, right);
        //左邊序列遞迴進行排序
        sort(array, left, mid-1);
        //右邊序列遞迴進行排序
        sort(array, mid+1, right);
    }
    public static int partition(int[] array,int left,int right) {
        int povint=array[right],leftBound=left,rightBound=right-1;
        //不斷迴圈直到左指標超過右指標
        while (leftBound<=rightBound) {
         //當滿足左指標的值小於基準值並且左指標未超過右指標時不斷向後尋找出第一個大於基準值的位置
            while (leftBound<=rightBound&&array[leftBound]<=povint) {
                leftBound++;
            }
         //當滿足右指標的值大於基準值並且左指標未超過右指標時不斷向後尋找出第一個小於於基準值的位置
            while (leftBound<=rightBound&&array[rightBound]>povint) {
                rightBound--;
            }
          //當左指標未超過右右指標時將左指標的較大值與右指標的較小值進行交換
            if (leftBound<=rightBound) {
                swap(array, leftBound, rightBound);
            }
        }
        //將基準值與迴圈結束後左指標的值進行交換
        swap(array, leftBound, right);
        return leftBound;
    }
    //實現陣列中的兩個元素進行交換
    public static void swap(int[] arr,int x,int y) {
    		int tmp=arr[x];
    		arr[x]=arr[y];
    		arr[y]=tmp;
    }
    

計數排序:

  • 統計原陣列中的的元素出現的次數,按照(原陣列的元素出現的次數作為臨時陣列的元素,原陣列的元素作為臨時陣列的索引值)對臨時陣列進行賦值後按照上述規則賦值回原陣列


    public class Test{
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            int[] array = new int[] { 2, 3, 4, 8, 10, 9, 1, 5, 6, 7, 0 };
            int findMax = findMax(array);
            int[] tmp = new int[findMax + 1];
            findCount(array, tmp);
            countSort(array, tmp);
            System.out.println(Arrays.toString(array));
        }
        /**
             * 
             * @Description:尋找陣列的最大值
             * @author lc1223
             * @date 2021年8月6日 下午11:18:10
             */
        public static int findMax(int[] array) {
            int max = 0;
            // (迴圈陣列(定義兩個索引)其中一個為最大值的索引,
            // 判斷當前索引的值與初始最大值的索引的值進行比較,
            // 若當前索引的值大則將當前索引的賦值給最大值的索引)
            for (int i = 0; i < array.length; i++) {
                max = array[max] > array[i] ? max : i;
            }
            return array[max];
        }
    
        /**
             * 
             * @Description: (規則:將原陣列的元素出現的次數作為臨時陣列的元素,將原陣列的元素作為臨時陣列的索引值)
             *               統計出原陣列中每個原始出現的次數並按照規則進行賦值給臨時陣列
             * @author lc1223
             * @date 2021年8月6日 下午11:25:00
             */
        public static void findCount(int[] array, int[] tmp) {
            for (int i = 0; i < array.length; i++) {
                // 外層迴圈為對陣列中每一個元素進行遍歷
                for (int j = i; j < array.length; j++) {
                    // 內層迴圈為對陣列中外層迴圈所指定的索引值以後進行遍歷並判斷外層索引的元素與內層索引的元素是否相等
                    if (array[i] == array[j]) {
                        // 相等則對臨時陣列[原陣列的元素]自增1
                        tmp[array[i]]++;
                        // 將外層迴圈所指定的索引移動至內層索引的下一位
                        i = j + 1;
                    }
                }
            }
        }
    
        /**
             * 
             * @Description:(規則:臨時陣列的索引值為原陣列的元素,臨時陣列的元素為原陣列元素出現的次數) 將臨時陣列中索引值對應的元素按照規則賦值回原陣列
             * @author lc1223
             * @date 2021年8月6日 下午11:38:24
             */
        public static void countSort(int[] array, int[] tmp) {
            // 臨時索引
            int k = 0;
            for (int i = 0; i < tmp.length; i++) {
                // 外層迴圈為對臨時陣列中每一個元素進行遍歷
                for (int j = tmp[i]; j > 0; j--) {
                    // 內層迴圈的次數為臨時陣列的元素
                    if (k < array.length) {
                        // 將臨時陣列的索引賦值給原陣列的臨時索引下的元素
                        array[k] = i;
                        // 將臨時索引不斷往下遍歷直到陣列尾
                        k++;
                    }
                }
            }
        }
    }
    

基數排序

  • 基數排序實際為對陣列中的元素的每一位的數進行計數排序


    int[] array = new int[] { 222, 300, 423, 84, 108, 99, 11, 55, 236, 117, 120 };
    int[] countArray=new int[10];
    int[] resultArray=new int[array.length];
    //對元素的每一位上的數進行計數排序從個位開始直到最高位
    int findMaxDigit = findMaxDigit(array);
    for (int i = 0; i < findMaxDigit; i++) {
        //獲取10的i次方以便獲取對應位上的數
        	int div=(int)Math.pow(10, i);
            for (int j = 0; j < array.length; j++) {
                //獲取每次迴圈位的資料
                int num=array[j]/div%10;
                countArray[num]++;
            }
            for (int j = 1; j < countArray.length; j++) {
                countArray[j]=countArray[j]+countArray[j-1];
            }
            for (int j = array.length-1; j >= 0 ; j--) {
                int num=array[j]/div%10;
                resultArray[--countArray[num]]=array[j];
            }
            for (int j = 0; j < countArray.length; j++) {
                countArray[j]=0;
            }
        	//將每一階段的結果陣列進行復制給原陣列以便於下一階段的排序
        	System.arraycopy(resultArray, 0, array, 0, array.length);
    	}		
    }
    	/**
    	 * 
    	 * @Description:尋找陣列最大值的位數
    	 * @author lc1223
    	 * @date 2021年8月7日 下午10:40:39
    	 */
    public static int findMaxDigit(int[] array) {
        int max=0;
        for (int i = 0; i < array.length; i++) {
            max=array[max]>array[i]?max:i;
        }
        String valueOf = String.valueOf(array[max]);
        return valueOf.length();
    }
    

桶排序:

  • 對陣列中的元素進行分桶按照區間[0%,25%],[25%,50%],[50%,75%],[75%,100%]在對應區間的元素進入一個桶中後將每個桶進行排序後依次輸出

int[] array = new int[] { 222, 300, 423, 84, 108, 99, 11, 55, 236, 117, 120 };
ArrayList<Double>[] arrayLists=new ArrayList[4];
int max=0,min=0;
//尋找出陣列中最大,最小值的索引值
for (int i = 0; i < array.length; i++) {
    max=array[max]>array[i]?max:i;
    min=array[min]>array[i]?i:min;
}
for (int i = 0; i < arrayLists.length; i++) {
    arrayLists[i]=new ArrayList<Double>();
}
//分為4個桶,計算出陣列元素中[0%,25%],[25%,50%],[50%,75%],[75%,100%]的元素的範圍並分別儲存在二維陣列中的桶中
double Twenty_five_percent=array[min]+((array[max]-array[min])*0.25);
double Fifty_percent=array[min]+((array[max]-array[min])*0.5);
double Seventy_five_percent=array[min]+((array[max]-array[min])*0.75);
for (int i = 0; i < array.length; i++) {
    if (array[i]>=array[min]&&array[i]<=Twenty_five_percent) {
        arrayLists[0].add(array[i]);
    }else if (array[i]<=Fifty_percent) {
        arrayLists[1].add(array[i]);
    }else if (array[i]<=Seventy_five_percent) {
        arrayLists[2].add(array[i]);
    }else if (array[i]<=array[max]) {
        arrayLists[3].add(array[i]);
    }else {
        throw new RuntimeException("不在範圍內");
    }
}
//對桶內的資料進行插入排序
for (ArrayList<Double> arrayList : arrayLists) {
    for (int i = 0; i < arrayList.size(); i++) {
        //Double num1 = arrayList.get(i);
        for (int j = arrayList.size()-1; j >0 ; j--) {
            if (arrayList.get(j)<arrayList.get(j-1)) {
                Double num1 = arrayList.get(j-1);
                arrayList.set(j-1, arrayList.get(j));
                arrayList.set(j, num1);
            }
        }
    }
}