1. 程式人生 > >【算法導論 in lambda】用lambda來重寫插入排序算法

【算法導論 in lambda】用lambda來重寫插入排序算法

就是 src 簡單測試 iter 類型 例子 應該 也不會 裏的

插入排序原本的實現方式之一:

 public int[] sort_ori(int[] ins) {
        for (int i = 1; i < ins.length; i++) {
            int currentIndex = i;
            int currentValue = ins[i];
            int switchIndex = -1;
            for (int j = 0; j < currentIndex; j++) {
                if (ins[j] > currentValue) {
                    switchIndex = j;
                    break;
                }
            }
            System.out.println(switchIndex);
            if (switchIndex >= 0) {
                if (currentIndex - switchIndex >= 0)
                    System.arraycopy(ins, switchIndex, ins, switchIndex + 1, currentIndex - switchIndex);
                ins[switchIndex] = currentValue;
            }
        }
        return ins;
    }

  插入排序算法對應的比喻例子是打牌時抽牌的過程,如果目標的數據類型為鏈表的話,用插入排序會特別適合。

  這邊的目標數據類型是數組,並且是原地算法,更貼切的比喻是按照顏色深淺整理蠟筆盒裏的蠟筆。

技術分享圖片

  步驟的話,就是取出某根蠟筆,然後找到它對應的位置,然後將這個位置之後的蠟筆都滾動到下一列。

  因此原來的算法中會有3個循環

  loop1:從頭到尾選擇被排列的對象

  loop2:在這個位置之前尋找對象應該放置的位置

  loop3:將該位置之後原對象位置之前的所有蠟筆滾到右邊

  嵌套其實是挺反人類的,它通常以一個for循環或者while循環作為標誌,但除非將其上下的代碼都看一遍,或者看註釋,不然很難第一眼就看出這個循環到底要做什麽。

  

  通過lambda表達式對插入排序進行重寫:

public int[] sort_lambda(int[] ins) {
        IntStream.range(1, ins.length).forEach(currentIndex -> {
            int currentValue = ins[currentIndex];
            IntStream.range(0, currentIndex).filter(j -> ins[j] > currentValue).findFirst().ifPresent(switchIndex -> {
                IntStream.iterate(currentIndex, a2 -> a2 - 1).limit(currentIndex - switchIndex).forEach(a2 -> {
                    ins[a2] = ins[a2 - 1];
                });
                ins[switchIndex] = currentValue;
            });
        });
        return ins;
    }

  這裏用3個IntStream的操作替代了之前3個loop

  其中loop2的代碼變成了“IntStream.range(0, currentIndex).filter(j -> ins[j] > currentValue).findFirst().ifPresent(xxx)”

  之前一直以為流操作都會從頭執行到尾,但今天測試過才發現並不是這樣,可以簡單測試一下。

  

 @Test
    public void run() {
        int[] a = new int[]{5, 4, 3, 2, 1};
        Arrays.stream(a).filter(a1->{
            System.out.println("comparing "+a1);
            return a1>3;
        }).findFirst();
    }

  這個方法會去判斷數組a中的值,找到第一個大於3的值。原本以為它的執行方式會像它的方法鏈一樣,將[5,4,3,2,1]轉換為[5,4]之後再根據findFirst()給出5。

但控制臺的輸出為:

  技術分享圖片

  也就是只執行了第一個比較。。。

  然後忽然想起來,之前常的limit。。也是處於方法鏈的後端(而不是作為參數放到stream的處理中),可能stream底層通過反射的機制,像sql一樣有個優化器的實現吧。(媽蛋以前一直以為流會從頭執行到尾,很多不需要遍歷到底的業務不敢用stream,白擔心了)

  既然這樣,實現loop2就可以放心大膽的使用stream了。

   loop2:在這個位置之前尋找對象應該放置的位置

  IntStream.range(0,currentIndex) ---- 對於loop1中指定對象之前的位置(左閉右開)

  .filter(a1-> ins[a1]>currentValue) ---- 判斷其是否大於loop1指定的值

  .findFirst() ---- 找到第一個就行了

  .ifPresent(loop3) ---- 如果存在的話,執行loop3

  能像寫sql一樣寫java,同時性能也不會有損失,挺開心的

  

【算法導論 in lambda】用lambda來重寫插入排序算法