日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构与算法--丑数

發布時間:2023/12/4 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构与算法--丑数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

找出排在第n位大的丑數

  • 丑數:我們將只包含質因子 2,3,5的數稱為丑數(ugly Number)。求按從小到大的熟悉怒排列的低1500 個丑數。例如6,8 都是丑數,但是14 不是丑數,因為他包含質因子7。1 是基礎丑數

  • 解法一:

    • 最直觀的解法,從1 開始逐個判斷每個整數是否丑數,
    • 所謂的一個數m是另一個數n的因子,那么n就能被m整除。也就是m%n == 0.
    • 根據丑數定義,丑數只能被2,3,5 整除。也就是說如果一個數能被2 整除,我們可以將它連續除以2。3 和5 也是類似,最后我們得到的是1 ,那么這個數就是丑數。
  • 解法一的實現如下:

public class FindUglyNumber {public static void main(String[] args) {Long time = System.currentTimeMillis();System.out.println(getNumberOfUglyNumber(900));Long time_1 = System.currentTimeMillis();System.out.println(time_1 - time);}/*** 方法一:逐個判斷從1 開始的數字是否丑數* */public static Integer getNumberOfUglyNumber(Integer position){if(position <= 0){return -1;}Integer count = 0;for (int i =0; true;i++){if(isUglyNumber(i)){count++;if(count.equals(position)){return i;}}}}/*** 判斷是否丑數* */public static boolean isUglyNumber(Integer number) {if(number <= 0){return false;}while (number % 2 == 0) {number /= 2;}while (number % 3 == 0) {number /= 3;}while (number % 5 == 0){number /= 5;}return number == 1;} }
  • 以上經過測試在求第1500 個丑數的耗時大概需要11秒以上,這個在線上是不可用額,那么我們應該有更好的優化方案

  • 方法二:

    • 既然要逐個判斷,如上實現中的isUglyNumber,其中有一個循環計算的過程,那么我們能否減少計算的步驟,直接判斷出是否丑數
    • 我們將已找出的丑數放在一個數組中,利用這個數組判斷
    • 當找出一個丑數s 時候,那么在這個丑數基礎上s * 2,s * 3,s * 5,必然也是丑數
    • 同理,如果有一個數 k, k/2,k/3,k/5 包含在已有的丑數中那么這個數也必然也是丑數
    • 我們利用如上數學原來,改造丑數的判斷方法,在每一次計算后判斷是否在已有丑數中,就可以減少計算量
    • 如下實現
/*** @author liaojiamin* @Date:Created in 10:57 2021/6/9*/ public class FindUglyNumber {public static void main(String[] args) {Long time_1 = System.currentTimeMillis();System.out.println(getNumberOfUglyNumber_1(900));Long time_2 = System.currentTimeMillis();System.out.println(time_2 - time_1);}/*** 方法二:保存之前判斷過的丑數,丑數* (2/3/5) 必然還是丑數* 更慢* */public static Integer getNumberOfUglyNumber_1(Integer position){if(position <=0 ){return -1;}List<Integer> uglyNumberList = new ArrayList<>();Integer count =0;for (int i=0;true;i++){if(isUglyNumber(i, uglyNumberList)){count ++;if(count.equals(position)){return i;}uglyNumberList.add(i);}}}/*** 判斷是否丑數* */public static boolean isUglyNumber(Integer number, List<Integer> uglyNumberList) {if(number <= 0){return false;}while (number % 2 == 0) {number /= 2;if(uglyNumberList.contains(number)){return true;}}while (number % 3 == 0) {number /= 3;if(uglyNumberList.contains(number)){return true;}}while (number % 5 == 0){number /= 5;if(uglyNumberList.contains(number)){return true;}}return number == 1;} }
  • 如上方案運行后結果并沒有如我們預期得到優化,時間反而更長了,基本要n分鐘才能出結果

  • 問題在循環運算的過程中我們每次運算都會判斷結果是否包含在已有丑數中

  • 但是 List的content方法的耗時比實際運算的時間多多了,因此它的耗時反而增加了

  • 本想空間換時間,但是算法應該沒寫好,是一次失敗的優化,接著找更優的方案

  • 方案三:

    • 以上兩種方案都是對數字逐個判斷,方案二中的思路是可以借鑒的,是否還有更優的解,無需逐個判斷我們通過算法求出還存在的丑數
    • 在方案二中我們提出了這個數學規律:當找出一個丑數s 時候,那么在這個丑數基礎上s * 2,s * 3,s * 5,必然也是丑數
    • 有如上規則的話,我們就有辦法通過這個規則找出還沒有界別出的丑數,但是難點在于我們需要按順序排列
    • 按順序找的話,我們只能找出比當前找出的最大丑數 大的最小丑數,比較拗口,舉例如下
    • 當前丑數有{1,2,3,4} 那么我們能通過2,3,5 的乘法找出2,3,4,6,10 ,但是符合當前要求的只有5
    • 因此我們用如下規則查詢:
      • 將2,3,5 與現有丑數數組中所有的數字依次做乘法,并且將大于當前最大丑數max的值記錄下來記為k
      • 找出記錄k中最小的值,就是我們需要找的下一個丑數
    • 經過如上分析有如下代碼:
/*** @author liaojiamin* @Date:Created in 10:57 2021/6/9*/ public class FindUglyNumber {public static void main(String[] args) {Long time_2 = System.currentTimeMillis();System.out.println(getNumberOfUglyNumber_2(1500));System.out.println(System.currentTimeMillis() - time_2);}/*** 方法三:不逐個判斷,我們先將1 是基礎丑數放入已經查找數組中,此時最大丑數是1 記為max,* 因為 uglyNum * (2/3/5) 還是uglyNum,我們直接找正好大于 當前max的最小丑數,* 此時將1 分別* (2/3/5) 得到 (2/3/5),其中最小值2,此時 max = 2* 以此類推* */public static Integer getNumberOfUglyNumber_2(Integer position){if(position <= 0){return -1;}Integer[] uglyArray = new Integer[position];uglyArray[0] = 1;Integer nowPosition = 1;Integer max = uglyArray[0];while (nowPosition < position){Integer newMax = findMinUgly(max, uglyArray, nowPosition);max = newMax;uglyArray[nowPosition] = newMax;nowPosition++;}return uglyArray[position - 1];}public static Integer findMinUgly(Integer max, Integer[] uglyArray, Integer nowPosition){Integer min = Integer.MAX_VALUE;for (int i = 0; i< nowPosition ; i++) {Integer temp_2 = uglyArray[i] * 2;if(temp_2 > max && temp_2 < min){min = temp_2;}Integer temp_3 = uglyArray[i] * 3;if(temp_3 > max && temp_3 < min){min = temp_3;}Integer temp_5 = uglyArray[i] * 5;if(temp_5 > max && temp_5 < min){min = temp_5;}}return min;} }
  • 經過如上優化后,第1500 位丑數的求解控制在46毫秒以內這基本上可以算合格的解法了
  • 但是其實還是有優化空間的,也就是我們在findMinUgly中循環做乘法運算求最小丑數的時候,有一部分乘法是完全沒必要的
  • 例如當我們當前的最大丑數是k的時候,在2,3,5與第 n個丑數做乘法的結果中 5*n < k
  • 其中5*n是n 以及n之前 所有丑數能計算出的最大丑數還是 小于 k,就說明n 之前的所有乘法都是不必要的
  • 通過對這個點的優化可以節省很多計算時間。
  • 小優化如下:
/*** @author liaojiamin* @Date:Created in 10:57 2021/6/9*/ public class FindUglyNumber {public static void main(String[] args) {Long time_2 = System.currentTimeMillis();System.out.println(getNumberOfUglyNumber_2(1500));System.out.println(System.currentTimeMillis() - time_2);}/*** 方法三:不逐個判斷,我們先將1 是基礎丑數放入已經查找數組中,此時最大丑數是1 記為max,* 因為 uglyNum * (2/3/5) 還是uglyNum,我們直接找正好大于 當前max的最小丑數,* 此時將1 分別* (2/3/5) 得到 (2/3/5),其中最小值2,此時 max = 2* 以此類推* */public static Integer getNumberOfUglyNumber_2(Integer position){if(position <= 0){return -1;}Integer[] uglyArray = new Integer[position];uglyArray[0] = 1;Integer nowPosition = 1;Integer max = uglyArray[0];while (nowPosition < position){Integer newMax = findMinUgly(max, uglyArray, nowPosition);max = newMax;uglyArray[nowPosition] = newMax;nowPosition++;}return uglyArray[position - 1];}public static Integer findMinUgly(Integer max, Integer[] uglyArray, Integer nowPosition){Integer min = Integer.MAX_VALUE;for (int i = 0; i< nowPosition ; i++) {Integer temp_5 = uglyArray[i] * 5;if(temp_5 < max){continue;}if(temp_5 > max && temp_5 < min){min = temp_5;}Integer temp_2 = uglyArray[i] * 2;if(temp_2 > max && temp_2 < min){min = temp_2;}Integer temp_3 = uglyArray[i] * 3;if(temp_3 > max && temp_3 < min){min = temp_3;}}return min;} }
  • 經過如上一個小的優化點,可以將1500 位的查找控制在30 毫秒以內

上一篇:數據結構與算法–將數組排成最小的數
下一篇:數據結構與算法–第一個只出現一次的字符

總結

以上是生活随笔為你收集整理的数据结构与算法--丑数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。