1. 程式人生 > >演算法設計與分析總結

演算法設計與分析總結

演算法複習總結

1 演算法的特性

1.1 定義

為了解決某類問題而規定的一個有限長的操作序列。

1.2 演算法的特性

有窮性,確定性,可行性,輸入,輸出

1.3 演算法的效能標準

正確性、可讀性、健壯性、高效率和低儲存需求

2 時間複雜度

2.1 常見演算法時間複雜度

  • O(1)O(1): 表示演算法的執行時間為常量
  • O(n)O(n): 表示該演算法是線性演算法
  • O(logn)O(logn): 二分查詢演算法
  • O(n2)O(n^2): 對陣列進行排序的簡單演算法,如直接插入排序。
  • O(n3)O(n^3): 做兩個n階矩陣的乘法運算
  • O(2n)O(2^n): 求具有n個元素集合的所有子集的演算法
  • O(n!)O(n!): 求具有n個元素的全排列的演算法

2.2 演算法複雜性分析

f(n)=O(g(n))f(n)g(n)f(n)=Ω(g(n))f(n)g(n)f(n)=θ(g(n))f(n)g(n)f(n)=o(g(n))f(n)g(n) f(n)=O(g(n)) \qquad f(n)的階≤g(n)的階\\ f(n)=Ω(g(n)) \qquad f(n)的階≥g(n)的階\\ f(n)=θ(g(n)) \qquad f(n)的階=g(n)的階\\ f(n)=o(g(n)) \qquad f(n)的階<g(n)的階\\

3 遞迴

3.1 二分查詢(遞迴)

int binary_search(int* a, int left, int right, int target){
    if(left > right) return -1;
    int mid = (left + right) / 2;
	if(a[mid] == target) return mid;
	if(a[mid] < target)
return binary_search(a, mid + 1, right, target); else return binary_search(a, left, mid - 1, target); }

3.2 二分查詢(非遞迴)

int binary_search(int* a, int left, int right, int target){
	while(left <= right){
		int mid = (left + right) / 2;
		if(a[mid] == target) return mid;
		if(a[mid] < target) left = mid + 1;
		else right = mid - 1;
	} 
	return -1;
} 

3.3 快速排序

  • 選擇劃分基準a§
  • 將陣列a劃分成兩個子陣列,使得a[l…p-1]<=a§,a[p+1…r]>=a§
  • 遞迴呼叫快速排序演算法,對a[l…p-1]和a[p+1…r]進行排序

3.3.1 遞迴

C參考實現

void quickSort(int* a, int left, int right){
	int i=left, j=right, base=a[left], tmp;
	if(left>=right) return;
	while(i!=j){
		while(a[j]>=base && i<j) j--;
		while(a[i]<=base && i<j) i++;
		if(i<j) tmp=a[i],a[i]=a[j],a[j]=tmp;
	}
	a[left]=a[i],a[i]=base;
	quickSort(a, left, i-1);
	quickSort(a, i+1,right); 
}

Java參考實現

public class QuickSort {
    private static void swap(int[] arr, int i, int j){
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

    private static int partition(int[] arr, int left, int right){
        int j = left - 1;
        for(int i = left; i < right; i++)
            if (arr[i] < arr[right]) swap(arr, i, ++j);
        swap(arr, right, ++j);
        return j;
    }

    public static void sort(int[] arr, int left, int right){
        if(left < right){
            int index = partition(arr, left, right);
            sort(arr, left, index - 1);
            sort(arr, index + 1, right);
        }
    }

    public static void main(String[] args){
        int[] arr = {4, 8, 2, 1, 5, 6, 7, 9, 3};
        sort(arr, 0, arr.length - 1);
        for (int x: arr) System.out.print(x+" ");
    }
}

3.3.2 非遞迴

import java.util.LinkedList;

public class QuickSort2 {
    private static void swap(int[] arr, int i, int j){
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

    private static int partition(int[] arr, int left, int right){
        int j = left - 1;
        for(int i = left; i < right; i++)
            if(arr[i] < arr[right]) swap(arr, i, ++j);
        swap(arr, right, ++j);
        return j;
    }

    public static void sort(int[] arr, int left, int right){
        if (left >= right) return;
        LinkedList<Integer> stack = new LinkedList<>();
        stack.push(left);
        stack.push(right);
        while (!stack.isEmpty()){
            int end = stack.pop();
            int begin = stack.pop();
            if (begin < end){
                int index = partition(arr, begin, end);
                stack.push(begin);
                stack.push(index - 1);
                stack.push(index + 1);
                stack.push(end);
            }
        }
    }

    public static void main(String[] args){
        int[] arr = new int[]{3, 1, 4, 9, 6, 0, 7, 2, 5, 8};
        sort(arr, 0, arr.length - 1);
        for (int e: arr) System.out.print(e + " ");
    }
}

4 分治法

4.1 基本思想

將求解的較大規模的問題分割成k個更小規模的子問題。對這k個子問題分別求解。如果子問題的規模仍然不夠小,則再劃分為k個子問題,如此遞迴的進行下去,直到問題規模足夠小,很容易求出其解為止。將求出的小規模的問題的解合併為一個更大規模的問題的解,自底向上逐步求出原來問題的解。

4.2 適用條件

分治法所能解決的問題一般具有以下幾個特徵:

  • 該問題的規模縮小到一定的程度就可以容易地解決;
  • 該問題可以分解為若干個規模較小的相同問題,即該問題具有最優子結構性質
  • 利用該問題分解出的子問題的解可以合併為該問題的解;
  • 該問題所分解出的各個子問題是相互獨立的,即子問題之間不包含公共的子問題。

4.3 歸併排序

其基本思想是:將待排序元素分成大小大致相同的2個子集合,分別對2個子集合進行排序,最終將排好序的子集合合併成為所要求的排好序的集合。

void merge(int* a, int x, int mid, int y, int* tmp){
    int i=x, j=mid+1, t=0;
    while(i<=mid && j<=y) tmp[t++] = (a[i]<=a[j]) ? a[i++] : a[j++];
    while(i<=mid) tmp[t++] = a[i++];
    while(j<=y) tmp[t++] = a[j++];
    t = 0;
    while(x<=y) a[x++] = tmp[t++]; 
}

void sort(int* a, int x, int y, int* tmp){
    if(x<y){
	    int mid = (x+y)/2;
	    sort(a,x,mid,tmp);
	    sort(a,mid+1,y,tmp);
	    merge(a,x,mid,y,tmp);
    }
}

4.4 求逆序對數

考慮1,2,,n1,2,…,n​的排列i1i2ini1,i2,…,in​,如果其中存在j,kj,k​,滿足j&lt;kj &lt; k​ij&gt;iki_j &gt; i_k​, 那麼就稱(ij,ik)(i_j,i_k)​是這個排列的一個逆序。

一個排列含有逆序的個數稱為這個排列的逆序數。例如排列 263451 含有8個逆序(2,1),(6,3),(6,4),(6,5),(6,1),(3,1),(4,1),(5,1),因此該排列的逆序數就是8。顯然,由1,2,…,n 構成的所有n!個排列中,最小的逆序數是0,對應的排列就是1,2,…,n;最大的逆序數是n(n-1)/2,對應的排列就是n,(n-1),…,2,1。逆序數越大的排列與原始排列的差異度就越大。

基本思路:

1.使用二分歸併排序法【分治法】進行求解;

2.將序列依此劃分為兩兩相等的子序列;

3.對每個子序列進行排序(比較a[i]>a[j],如果滿足條件,則求該子序列的逆序數count=mid-i+1,其中mid=(left+right)/2)

4.接著合併子序列即可。

void merge(int* a, int left, int mid, int right, int* tmp){
	int i=left, j=mid+1, t=0;
	while(i<=mid && j<=right){
		if(a[i]<=a[j]) tmp[t++] = a[i++];
		else{
			tmp[t++] = a[j++];
			count += mid-i+1;
		}
	}
	while(i<=mid) tmp[t++] = a[i++];
	while(j<=right) tmp[t++] = a[j++];
	t = 0;
	while(left <= right) a[left++] = tmp[t++];
}

void mergeSort(int* a, int left, int right, int* tmp){
	if(left < right){
		int mid = (left + right) / 2;
		mergeSort(a, left, mid, tmp);
		mergeSort(a, mid+1, right, tmp);
		merge(a, left, mid, right, tmp);
	}
} 

4.5 快速選擇演算法

基本思想:

快速選擇的總體思路與快速排序一致,選擇一個元素作為基準來對元素進行分割槽,將小於和大於基準的元素分在基準左邊和右邊的兩個區域。不同的是,快速選擇並不遞迴訪問雙邊,而是隻遞迴進入一邊的元素中繼續尋找。這降低了平均時間複雜度,從O(nlogn)O(nlogn)O(n)O(n),不過最壞情況仍然是O(n2)O(n^2)

int partition(int* a, int left, int right){
	int j = left - 1, tmp; //選擇a[right]作為劃分基準
	for(int i=left; i<right; i++){
		if(a[i]<=a[right]) tmp = a[i], a[i] = a[++j], a[j] = tmp; 
	} 
	tmp = a[right], a[right] = a[++j
            
           

相關推薦

演算法設計分析總結

演算法複習總結 1 演算法的特性 1.1 定義 為了解決某類問題而規定的一個有限長的操作序列。 1.2 演算法的特性 有窮性,確定性,可行性,輸入,輸出 1.3 演算法的效能標準 正確性、可讀性、健壯性、高效率和低儲存需求 2 時間複雜度 2.1 常見演算法時

《計算機演算法設計分析 第4版 (王曉東) 課後答案[1-9章]》pdf版電子書附下載連結+30個總結JVM虛擬機器的技術文排版好(收藏版)

技術書閱讀方法論 一.速讀一遍(最好在1~2天內完成) 人的大腦記憶力有限,在一天內快速看完一本書會在大腦裡留下深刻印象,對於之後複習以及總結都會有特別好的作用。 對於每一章的知識,先閱讀標題,弄懂大概講的是什麼主題,再去快速看一遍,不懂也沒有關係,但是一定要在不懂的

演算法設計分析課程總結

演算法分析與設計課程總結  演算法設計與分析是面向設計的核心課程,主要通過介紹常見的演算法設計策略及複雜性分析方法,培養學生分析問題和解決問題的能力,為開發高效的軟體系統及參加相關領域的研究工作奠定堅實的基礎。該課程理論與實踐並重,內容具有綜合性、廣泛性和系統性,是一門

計算機演算法設計分析觀後小總結

簡單略看了一遍王曉東的《計算機演算法設計與分析》,很多地方沒有細看,現在先做個小總結,方便以後回頭看的時候記憶起一些內容。 第二章:遞迴與分治策略 遞迴的概念: 直接或間接地呼叫自身的演算法稱為遞迴演算法。用函式自身給出定義的函式稱為遞迴函式。 分治法的基本思想: 分治法的

演算法設計分析-遞迴演算法總結

一、遞迴的思想和定義 (一)、孫子兵法道:凡治眾如治寡,分數是也。在使用計算機解決問題時我們經常遇到的問題是規模較大的問題,不能直接求解,最普遍的方法就是分解問題。遞迴就是一種特殊的分治思想,在解決一個規模為n的大問題時,先將這個大問題分解為規模為k的與原問題在形式上相同

《計算機演算法設計分析 第2版+第3版+第4版 (王曉東) 》原書附答案pdf版電子書附下載連結+30個總結JVM虛擬機器的技術文排版好(收藏版)

技術書閱讀方法論 一.速讀一遍(最好在1~2天內完成) 人的大腦記憶力有限,在一天內快速看完一本書會在大腦裡留下深刻印象,對於之後複習以及總結都會有特別好的作用。 對於每一章的知識,先閱讀標題,弄懂大概講的是什麼主題,再去快速看一遍,不懂也沒有關係,但是一定要在不懂的

演算法設計分析——動態規劃(一)矩陣連乘

動態規劃——Dynamic programming,可以說是本人一直沒有啃下的骨頭,這次我就得好好來學學Dynamic programming. OK,出發! 動態規劃通常是分治演算法的一種特殊情況,它一般用於最優化問題,如果這些問題能夠: 1.能夠分解為規模更小的子問題 2.遞迴的

演算法設計分析——分治法

前言 本文重點回顧了卜老師課堂上關於分治演算法的一些常見的問題。加油吧!ヾ(◍°∇°◍)ノ゙ 分治法(Divide and Conquer) 當面對一個問題的時候,我們可能一下子找不到解決問題的方法。此時,我們可以考慮將問題規模最小化,先看看當問題規模變小以後,我們如何去解決

演算法設計分析04-排序問題

①氣泡排序:量量比較待排序資料元素的大小,發現兩個資料元素的次序相反時進行交換,直到沒有反序的資料元素為止。時間複雜度是O(n*2)。穩定的。下面給出兩種排序演算法,我比較喜歡第二種,因為第二種才能真正解釋冒泡的原理 public class bubble1 {    &n

演算法設計分析03-十進位制轉二進位制問題

10進位制數轉2 進位制數 題目:2 進位制除了 0,1,還可以用 2 表示。例如: 1-> 1 2-> 10 or 02 3->11 4 ->100 or 020 or 012 問題:這樣一個十進位制數轉為二進位制數,就不是唯一的了。現求十進位制數 N 轉換為這種二進位制數

演算法設計分析02-走臺階問題

走臺階問題,一次可以走1,2,3級,都 N級臺階的方法數 。 初始:f(0) = 0; f(1) =1; f(2) = 1 + 1 = 2; 遞推公式:f(n) = f(n - 1) + f(n-2) + f(n - 3) 解題思路: 因為一次可以走1,2,3級,所以在第

演算法設計分析01-連結串列的逆置

一.最常用的方法: LNode*  ReverseList(LNode* head) {     if (head == NULL)         return NULL;  &nbs

演算法設計分析05-最近點對演算法

1.題目描述: 設S是平面上n個點的集合,在這一節中,我們考慮在S中找到一個點對p和q的問題,使其相互距離最短。換句話說,希望在S中找到具有這樣性質的兩點p1 = (x1,y1)和p2 = (x2,y2),使它們間的距離在所有S中點對間為最小 2.解題思路 一共分為三種情況 情況1:

演算法設計分析課程的時間空間複雜度

演算法設計與分析課程的時間空間複雜度: 總結 演算法 時間複雜度 空間複雜度 說明 Hanoi $ O(2^n) $ $ O(n) $ 遞迴使用 會場安排問題 \(O

【計算機演算法設計分析】——SVM

一.簡介 支援向量機(support vector machines)是一種二分類模型,它的目的是尋找一個超平面來對樣本進行分割,分割的原則是間隔最大化,最終轉化為一個凸二次規劃問題來求解。由簡至繁的模型包括: (1)當訓練樣本線性可分時,通過硬間隔最大化,學習一個線性可分支援向量機;(2)當訓練樣本近似

演算法設計分析作業題】第十一週:20. Valid Parentheses

題目 C++ solution class Solution { public: bool isValid(string s) { stack<char> cstack; for (int i = 0; i < s.si

演算法設計分析》第十一週作業

《演算法設計與分析》第十一週作業 標籤(空格分隔): 課堂作業 文章目錄 《演算法設計與分析》第十一週作業 @[toc] 題目概要 思路 具體實現 心得 原始碼:

演算法設計分析》第十二週作業

《演算法設計與分析》第十二週作業 標籤(空格分隔): 課堂作業 文章目錄 《演算法設計與分析》第十二週作業 @[toc] 題目概要 思路 具體實現 心得 原始碼:

演算法設計分析》第十週作業

《演算法設計與分析》第十週作業 標籤(空格分隔): 課堂作業 文章目錄 《演算法設計與分析》第十週作業 @[toc] 題目概要 思路 具體實現 心得 原始碼:

演算法設計分析》第九周作業

《演算法設計與分析》第九周作業 標籤(空格分隔): 課堂作業 文章目錄 《演算法設計與分析》第九周作業 @[toc] 題目概要 思路 具體實現 心得 原始碼: