【戀上數據結構與算法】複雜度、斐波那契數列、LeetCode

複雜度

記錄小碼哥的戀上數據結構與算法(第一季) - 複雜度

什麼是算法

什麼是算法

算法是用於解決特定問題的一系列的執行步驟

eg:解決兩數相加的問題

// 計算a和b的和
public static int plue(int a, int b){
	return a + b;
}

eg:解決 n個數字的和 的問題

// 計算1+2+3+...+n
public static int sum(int n){
	int result = 0;
	for(int i = 1; i <= n; i++){
		result += i;
	}
	return result;
}

使用不同算法,解決同一個問題,效率可能相差非常大。

比如:求第n個斐波那契數 Fibonacci number

解決了什麼問題 比如兩數相加問題 、求和問題

能解決問題就是算法呢

解決同一個問題 方法非常多 效率可能相差非常大

如何評判一個算法的好壞

如果單從執行效率上進行評估,可能會想到這麼一種方案

  • 比較不同算法對同一組輸入的執行處理時間
  • 這種方案也叫做:事後統計法

事後統計法 寫代碼去測試 和硬件有關 和輸入有關

上述方案有比較明顯的缺點:

  • 執行時間嚴重依賴硬件以及運行時各種不確定的環境因素
  • 必須編寫相應的測算代碼
  • 測試數據的選擇比較難保證公正性

一般從以下維度來評估算法的優劣:

  • 正確性、可讀性、健壯性(對不合理輸入的反應能力和處理能力)
  • 時間複雜度(time complexity) 估算程序指令的執行次數(執行時間)
  • 空間複雜度(space complexity)估算所需佔用的存儲空間

算法首先要保證 正確性、可讀性、健壯性 對不合理輸入的反應能力和處理能力

時間、空間優化

由於現在硬件發展的較好,一般情況下我們更側重於時間複雜度。

時間換空間

時空

package cn.liuawen;

public class demo {
	// 計算 1+2+3+...+n 的和
	public static int sum1(int n) {
		int result = 0;
		for (int i = 1; i <= n; i++) {
			result += i;
		}
		return result;
	}

	// 計算 1+2+3+...+n 的和
	public static int sum2(int n) {
		return (1 + n) * n / 2;
	}

	public static void main(String[] args) {
		System.out.println("sum1(10):"+sum1(10));
		System.out.println("sum2(10):"+sum2(10));

	}
}

大O表示法 Big O

一般用大O表示法來描述複雜度 它表示的是數據規模n對應的複雜度

忽略常數、係數、低階

  • 9 >> O(1)
  • 2n + 3 >> O(n)
  • n2 + 2n + 6 >> O(n2)
  • 4n3 + 3n2 + 22n + 100 >> O(n3)
  • 寫法上,n3 等價於 n^3’

注意:大O表示法僅僅是一種粗略的分析模型 是一種估算 能幫助我們短時間內瞭解一個算法的執行效率

對數階的細節

對數階一般省略底數

  • log29 ∗ log9n = og2n

在這裏插入圖片描述

常數忽略掉 log2n/log29 = log9n

所以 O(log2n) 、O(log9n) 統稱爲 O(logn)

常見的複雜度

logn

在這裏插入圖片描述

可以藉助函數生成工具對比複雜度的大小

https://zh.numberempire.com/graphingcalculator.php

在這裏插入圖片描述

在這裏插入圖片描述

Leetcode

一個用於練習算法的網站 力扣 leetcode

https://leetcode-cn.com/

https://leetcode.com/

目的是練習算法

我們來練習一個道斐波那契數列吧

https://leetcode-cn.com/problems/fibonacci-number/

斐波那契數列複雜度分析

https://leetcode-cn.com/problems/fibonacci-number/

實現

/* 0 1 2 3 4 5
	 * 0 1 1 2 3 5 8 13 ....
	 */
	
	// O(2^n)
	public static int fib1(int n) {
		if (n <= 1) return n;
		return fib1(n - 1) + fib1(n - 2);
	}
	
	// O(n)
	public static int fib2(int n) {
		if (n <= 1) return n;
		
		int first = 0;
		int second = 1;
		for (int i = 0; i < n - 1; i++) {
			int sum = first + second;
			first = second;
			second = sum;
		}
		return second;
	}
	
	public static int fib3(int n) {
		if (n <= 1) return n;
		
		int first = 0;
		int second = 1;
		while (n-- > 1) {
			second += first;
			first = second - first;
		}
		return second;
	}

	public static int fib4(int n) {
		double c = Math.sqrt(5);
		return (int) ((Math.pow((1 + c) / 2, n) - Math.pow((1 - c) / 2, n)) / c);
	}

斐波那契數列-遞歸

// O(2^n)
public static int fib1(int n) {
	if (n <= 1) return n;
	return fib1(n - 1) + fib1(n - 2);
}

複雜度分析:

在這裏插入圖片描述

在這裏插入圖片描述

呈現的是指數級增長的趨勢

效率很低很低。

斐波那契數列-循環

不開闢任何空間,只使用循環完成。

// O(n)
public static int fib2(int n) {
	if (n <= 1) return n;
	
	int first = 0;
	int second = 1;
	for (int i = 0; i < n - 1; i++) {
		int sum = first + second;
		first = second;
		second = sum;
	}
	/*
	// 也可以使用while循環
	while (n-- > 1) {
		second += first;
		first = second - first;
	}
	*/
	return second;
}

速度變快了,內存消耗還是很多…

開闢新的數組空間,用空間換時間。

public static int fib3(int n){
	if(n <= 1) return n;
	
	int[] fib = new int[n+1];
	fib[0] = 0;
	fib[1] = 1;
	for(int i = 2; i < fib.length; i++){
		fib[i] = fib[i-1] + fib[i-2];
	}
	return fib[n];
}

fib函數的時間複雜度分析

在這裏插入圖片描述

有時候算法之間的差距,往往比硬件方面的差距還要大

在這裏插入圖片描述

算法之間的差距 比硬件差多了

寫代碼要考慮 算法

多個數據規模的情況

時間複雜度:O(n + k)

public static void test(int n, int k){
	for(int i = 0; i < n; i++){
		System.out.println("test");
	}
	for (int i = 0; i < k; i++){
		System.out.println("test");
	}
}

算法的優化方向

  • 用盡量少的存儲空間
  • 用盡量少的執行步驟(執行時間)
  • 根據情況,可以
    • 空間換時間
    • 時間換空間

更多知識

更多複雜度相關的知識,會在後續講解數據結構、算法的過程中穿插

最好、最壞複雜度

均攤複雜度

複雜度震盪

平均複雜度

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章