數據結構與算法(java)

簡述

在編程過程當中,一般會遇到的一個問題就是,性能瓶頸。不少時候考慮的都是怎麼去作橫向擴展,但恰恰忽略掉了最基本的問題就是系統是否真的已經達到了瓶頸?
性能瓶頸一般的表象是資源消耗過多外部處理系統的性能不足;或者資源消耗很少但程序的響應速度卻仍達不到要求。
而調優的方式就是 尋找過分消耗資源的代碼 和 尋找未充分使用資源但程序執行慢的緣由和代碼。 基礎決定高度 就拿汽車來比較,一般不懂變速箱、發動機的原理但也是能開車,一樣一個不懂數據結構和算法的人也能編程。不少人以爲基本的數據結構及操做已經在高級語言中封裝,均可以直接調用庫函數,那麼到底有沒有必要好好學習數據結構?java

數據結構+算法=程序node

一般在程序中,遇到一個實際問題,充分利用數據結構,將數據及其之間的關係有效地存儲在計算機中,而後選擇合適的算法策略,並用程序高效實現,這纔是提升程序性能的主要方式。git

  • 如何有效地存儲數據,不一樣的數據結構產生什麼樣的算法複雜性,有沒有更好的存儲方法提升算法的效率?

若是沒有具有這塊相應的知識,怎麼完成上述的實現?若是脫離了原有的調用,怎麼完成程序的高效實現?而全部的應用實現都依賴於基礎,基礎就是數據結構和算法。瞭解這塊,才能作到無懼任何技術的演變。全部說基礎決定高度!github

基本的概念

數據結構表示數據在計算機中的存儲和組織形式,主要描述數據元素之間和位置關係等。選擇適當的數據結構能夠提升計算機程序的運行效率(時間複雜度)和存儲效率(空間複雜度)。算法

數據結構的三種層次編程

  1. 邏輯結構--抽象層: 主要描述的是數據元素之間的邏輯關係
  • 集合結構(集) 全部的元素都屬於一個整體,除了同屬於一個集合外沒有其餘關係。集合結構不強調元素之間的任何關聯性。
  • 線性結構(表) 數據元素之間具備一對一的先後關係。結構中必須存在惟一的首元素和惟一的尾元素。
  • 樹形結構(樹) 數據元素之間一對多的關係
  • 網狀結構(圖) 圖狀結構或網狀結構 結構中的數據元素之間存在多對多的關係

數據結構圖

  1. 物理結構--存儲層: 主要描述的是數據元素之間的位置關係
  • 順序結構 順序結構就是使用一組連續的存儲單元依次存儲邏輯上相鄰的各個元素
    優勢 只須要申請存放數據自己的內存空間便可,支持下標訪問,也能夠實現隨機訪問。
    缺點 必須靜態分配連續空間,內存空間的利用率比較低。插入或刪除可能須要移動大量元素,效率比較低數組

  • 鏈式結構 鏈式存儲結構不使用連續的存儲空間存放結構的元素,而是爲每個元素構造一個節點。節點中除了存放數據自己之外,還須要存放指向下一個節點的指針。
    優勢 不採用連續的存儲空間致使內存空間利用率比較高,克服順序存儲結構中預知元素個數的缺點 插入或刪除元素時,不須要移動大量的元素。
    缺點 須要額外的空間來表達數據之間的邏輯關係, 不支持下標訪問和隨機訪問。數據結構

  • 索引結構 除創建存儲節點信息外,還創建附加的索引表來標節點的地址。索引表由若干索引項組成。
    優勢 是用節點的索引號來肯定結點存儲地址,檢索速度塊
    `缺點: 增長了附加的索引表,會佔用較多的存儲空間。app

  • 散列結構 由節點的關鍵碼值決定節點的存儲地址。散列技術除了能夠用於查找外,還能夠用於存儲。
    優勢 散列是數組存儲方式的一種發展,採用存儲數組中內容的部分元素做爲映射函數的輸入,映射函數的輸出就是存儲數據的位置, 相比數組,散列的數據訪問速度要高於數組
    缺點 不支持排序,通常比用線性表存儲須要更多的空間,而且記錄的關鍵字不能重複。數據結構和算法

  1. 運算結構--實現層: 主要描述的是如何實現數據結構
  • 分配資源,創建結構,釋放資源
  • 插入和刪除
  • 獲取和遍歷
  • 修改和排序

數據結構
每種邏輯結構採用何種物理結構來實現,並無具體的規定。當一個結構,在邏輯結構中只有一種定義,而在物理結構中卻有兩種選擇,那麼這個結構就屬於邏輯結構;

數據結構比較

數據結構比較1
數據結構比較2

經常使用的數據結構:

經常使用的數據結構

數據結構選擇:

O符號

O在算法當中表述的是時間複雜度,它在分析算法複雜性的方面很是有用。常見的有:

  1. O(1):最低的複雜度,不管數據量大小,耗時都不變,均可以在一次計算後得到。哈希算法就是典型的O(1)
  2. O(n):線性,n表示數據的量,當量增大,耗時也增大,常見有遍歷算法
  3. O(n²):平方,表示耗時是n的平方倍,當看到循環嵌循環的時候,基本上這個算法就是平方級的,如:冒泡排序等
  4. O(log n):對數,一般ax=n,那麼數x叫作以a爲底n的對數,也就是x=logan,這裏是a一般是2,如:數量增大8倍,耗時只增長了3倍,二分查找就是對數級的算法,每次剔除一半
  5. O(n log n):線性對數,就是n乘以log n,按照上面說的數據增大8倍,耗時就是8*3=24倍,歸併排序就是線性對數級的算法

Array

在Java中,數組是用來存放同一種數據類型的集合,注意只能存放同一種數據類型。

//只聲明瞭類型和長度
數據類型 []  數組名稱 = new 數據類型[數組長度];
//聲明瞭類型,初始化賦值,大小由元素個數決定
數據類型 [] 數組名稱 = {數組元素1,數組元素2,......}
複製代碼

大小固定,不能動態擴展(初始化給大了,浪費;給小了,不夠用),插入快,刪除和查找慢

數組
Array
模擬實現

public class Array {
    private int[] intArray;

    private int elems;

    private int length;

    public Array(int max) {
        length = max;
        intArray = new int[max];
        elems = 0;
    }

    /** * 添加 * @param value */
    public void add(int value){
        if(elems == length){
            System.out.println("error");
            return;
        }
        intArray[elems] = value;
        elems++;
    }


    /** * 查找 * @param searchKey * @return */
    public int find(int searchKey){
        int i;
        for(i = 0; i < elems; i++){
            if(intArray[i] == searchKey)
                break;
        }
        if(i == elems){
            return -1;
        }
        return i;
    }

    /** * 刪除 * @param value * @return */
    public boolean delete(int value){
        int i = find(value);
        if(i == -1){
            return false;
        }
        for (int j = i; j < elems-1; j++) {
            //後面的數據往前移
            intArray[j] = intArray[j + 1];
        }
        elems--;
        return true;
    }

    /** * 更新 * @param oldValue * @param newValue * @return */
    public boolean update(int oldValue,int newValue){
        int i = find(oldValue);
        if(i == -1){
            return false;
        }
        intArray[i] = newValue;
        return true;
    }

    /** * 顯示全部 */
    public void display(){
        for(int i = 0 ; i < elems ; i++){
            System.out.print(intArray[i]+" ");
        }
        System.out.print("\n");
    }


    /** * 冒泡排序 * 每趟冒出一個最大數/最小數 * 每次運行數量:總數量-運行的趟數(已冒出) */
    public void bubbleSort(){
        for(int i = 0; i < elems-1; i++){//排序趟數 n-1次就好了
            System.out.println("第"+(i+1)+"趟:");
            for(int j = 0; j < elems-i-1; j++){//每趟次數 (n-i) -1是防止下標越界,後面賦值用到了+1
                if(intArray[j] > intArray[j+1]){ //控制按大冒泡仍是按小冒泡
                    int temp = intArray[j];
                    intArray[j] =  intArray[j+1];
                    intArray[j+1] = temp;
                }
                display();
            }
        }
    }

    /*** * 選擇排序 * 每趟選擇一個最大數/最小數 * 每次運行數量:總數量-運行的趟數(已選出) */
    public void selectSort(){
        for(int i = 0; i < elems-1; i++) {//排序趟數 n-1次就好了
            int min = i;
            for(int j = i+1; j < elems; j++){ //排序次數 每趟選擇一個數出來,-1次
                if(intArray[j] < intArray[min]){
                    min = j;
                }
            }
            if(i != min ){
                int temp = intArray[i];
                intArray[i] = intArray[min];
                intArray[min] = temp;
            }
            display();
        }
    }

    /** * 插入排序 * 每趟選擇一個待插入的數 * 每次運行把待插入的數放在比它大/小後面 */
    public void insertSort(){
        int j;
        for(int i = 1; i < elems; i++){
            int temp = intArray[i];
            j = i;
            while (j > 0 && temp < intArray[j-1]){
                intArray[j] = intArray[j-1];
                j--;
            }
            intArray[j] = temp;
        }
        display();
    }

    public static void main(String[] args) {
        Array array = new Array(10);
        array.add(6);
        array.add(3);
        array.add(8);
        array.add(2);
        array.add(11);
        array.add(5);
        array.add(7);
        array.add(4);
        array.add(9);
        array.add(10);
// array.bubbleSort();
// array.selectSort();
        array.insertSort();
// array.display();
// System.out.println(array.find(4));
// System.out.println(array.delete(1));
// array.display();
// System.out.println(array.update(2,6));
// array.display();
    }
}
複製代碼

Stack

  • 棧(stack)又稱爲堆棧或堆疊,棧做爲一種數據結構,它按照先進後出的原則存儲數據,先進入的數據被壓入棧底,最後的數據在棧頂
  • java中Stack是Vector的一個子類,只定義了默認構造函數,用來建立一個空棧。
  • 棧是元素的集合,其包含了兩個基本操做:push 操做能夠用於將元素壓入棧,pop 操做能夠將棧頂元素移除。
  • 遵循後入先出(LIFO)原則。
  • 時間複雜度:
  • 索引: O(n)
  • 搜索: O(n)
  • 插入: O(1)
  • 移除: O(1)
Stack()
複製代碼

棧
Stack
模擬實現

public class Stack {
    //小貼士:一般能夠利用棧實現字符串逆序,還能夠利用棧判斷分隔符是否匹配,如<a[b{c}]>,能夠正進反出,棧爲空則成功。

    /**基於數組實現的順序棧,連續存儲的線性實現,須要初始化容量**/
    //固定數據類型
    //private int[] array;
    //動態數據類型
    private Object[] objArray;

    private int maxSize;

    private int top;

    public Stack() {
    }

    public Stack(int maxSize) {
        if(maxSize > 0){
            objArray = new Object[maxSize];
            this.maxSize = maxSize;
            top = -1;
        }else{
            throw new RuntimeException("初始化大小錯誤:maxSize=" + maxSize);
        }
    }

    /** * 入棧 * @param obj */
    public void objPush(Object obj){
        //擴容
        grow();
        //++在前表示先運算再賦值,優先級高,在後表示先賦值再運算,優先級低
        objArray[++top] = obj;
    }

    /** * 出棧 * @return */
    public Object objPop(){
        Object obj = peekTop();
        //聲明原頂棧能夠回收空間(GC)
        objArray[top--] = null;
        return obj;
    }

    /** * 查看棧頂 * @return */
    public Object peekTop(){
        if(top != -1){
            return objArray[top];
        }else{
            throw new RuntimeException("stack is null");
        }
    }

    /** * 動態擴容 */
    public void grow(){
        // << 左移運算符,1表示乘以2的1次方
        if(top == maxSize-1){
            maxSize = maxSize<<1;
            objArray = Arrays.copyOf(objArray,maxSize);
        }
    }



    /**基於鏈式存儲,不連續存儲的非線性實現**/
    private class Node<Object>{
        private Object data;

        private Node next;

        public Node(Object data, Node next) {
            this.data = data;
            this.next = next;
        }
    }

    private Node nodeTop;

    private int size;

    public void nodePush(Object obj){
        //棧頂指向新元素,新元素的next指向原棧頂元素
        nodeTop = new Node(obj,nodeTop);
        size++;
    }

    public Object nodePop(){
        Node old = nodeTop;
        //聲明原頂棧能夠回收空間(GC)
        old.next = null;
        //棧頂指向下一個元素
        nodeTop = nodeTop.next;
        size--;
        return old.data;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("[ ");
        for(Node<Object> node = nodeTop; node != null; node = node.next){
            sb.append(node.data.toString() + " ");
        }
        return sb.toString()+"]";
    }

    public static void main(String[] args) {
// Stack stack = new Stack(1);
// stack.objPush("abc");
// stack.objPush(123);
// stack.objPush("de");
// stack.objPush("cd");
// stack.objPush("er");
// stack.objPush("hello");
// stack.objPush(666);
// stack.objPush(545);
// stack.objPush("word");
// while (stack.top != -1){
// System.out.println(stack.objPop());
// }
// System.out.println(stack.peekTop());
        Stack stack = new Stack();
        stack.nodePush("111");
        stack.nodePush("222");
        stack.nodePush("aaa");
        stack.nodePush("bbb");
        System.out.println(stack);
        while (stack.size > 1)
        System.out.println(stack.nodePop());
        System.out.println(stack);
    }
}
複製代碼

Queue

  • 隊列是元素的集合,其包含了兩個基本操做:enqueue 操做能夠用於將元素插入到隊列中,而 dequeue 操做則是將元素從隊列中移除。
  • 遵循先入先出原則 (FIFO)。
  • 時間複雜度:
  • 索引: O(n)
  • 搜索: O(n)
  • 插入: O(1)
  • 移除: O(1)
    隊列
    Queue
public class Queue {
    /*** * 1.單向隊列(Queue):只能在一端插入數據,另外一端刪除數據。 * 2.雙向隊列(Deque):每一端均可以進行插入數據和刪除數據操做。 * * 與棧不一樣的是,隊列中的數據不老是從數組的0下標開始的 * 選擇的作法是移動隊頭和隊尾的指針。 * 爲了不隊列不滿卻不能插入新的數據,咱們可讓隊尾指針繞回到數組開始的位置,這也稱爲「循環隊列」。 * */
    // 單向循環隊列,順序存儲結構實現
    private Object[] objQueue;
    //隊列大小
    private int maxSize;
    //頂部
    private int top;
    //底部
    private int bottom;
    //實際元素
    private int item;

    public Queue(int size) {
        maxSize = size;
        objQueue = new Object[maxSize];
        top = 0;
        bottom = -1;
        item = 0;
    }

    /** * 入隊 * @param obj */
    public void add(Object obj){
        if(item == maxSize){
            throw new RuntimeException(obj+" add error, queue is full");
        }
        //循環隊列,首尾結合,下標控制隊首和隊尾位置
        if(bottom == maxSize-1){
            bottom = -1;
        }
        objQueue[++bottom] = obj;
        item++;
    }

    /** * 出對 * @return */
    public Object out(){
        if(item == 0){
            throw new RuntimeException("queue is null");
        }
        Object obj = objQueue[top];
        //聲明原頂棧能夠回收空間(GC)
        objQueue[top] = null;
        top++;
        //重置下標
        if(top == maxSize){
            top = 0;
        }
        item--;
        return obj;
    }

    //鏈式存儲結構實現
    private class NodeQueue<Object>{
        private Object data;

        private NodeQueue next;

        public NodeQueue(Object data, NodeQueue next) {
            this.data = data;
            this.next = next;
        }
    }
    //隊列頭 出
    private NodeQueue queueTop;
    //隊列尾 進
    private NodeQueue queueBottom;
    //隊列大小
    private int size;

    public Queue() {
        queueTop = null;
        queueBottom = null;
        size = 0;
    }

    /** * 入隊 * @param obj */
    public void addNodeQueue(Object obj){
        if(size == 0){
            queueTop = new NodeQueue(obj,null);
            //指向同一存儲地址
            queueBottom = queueTop;
        }else{
            NodeQueue<Object> nodeQueue = new NodeQueue(obj,null);
            //讓尾節點的next指向新增的節點
            queueBottom.next = nodeQueue;
            //以新節點做爲尾節點
            queueBottom = nodeQueue;
        }
        size ++;
    }

    /** * 出隊 * @return */
    public Object removeNodeQueue(){
        if(size == 0){
            throw new RuntimeException("queue is null");
        }
        NodeQueue nodeQueue = queueTop;
        queueTop = queueTop.next;
        //聲明原隊列頭next能夠回收空間(GC)
        nodeQueue.next = null;
        size--;
        return nodeQueue.data;
    }


    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("{ ");
        for(NodeQueue nodeQueue = queueTop ; nodeQueue != null ; nodeQueue = nodeQueue.next){
            sb.append(nodeQueue.data.toString()+" ");
        }
        return sb.toString()+"}";
    }

    public static void main(String[] args) {
        Queue queue = new Queue();
        queue.addNodeQueue("123");
        queue.addNodeQueue("abc");
        queue.addNodeQueue("ddd");
        System.out.println(queue);
        queue.removeNodeQueue();
        System.out.println(queue);
        queue.removeNodeQueue();
        queue.removeNodeQueue();
        System.out.println(queue);
    }
}
複製代碼

Linked List

  • 鏈表便是由節點(Node)組成的線性集合,每一個節點能夠利用指針指向其餘節點。它是一種包含了多個節點的、可以用於表示序列的數據結構。
  • 單向鏈表: 鏈表中的節點僅指向下一個節點,而且最後一個節點指向空。
  • 雙向鏈表: 其中每一個節點具備兩個指針 p、n,使得 p 指向先前節點而且 n 指向下一個節點;最後一個節點的 n 指針指向 null。
  • 循環鏈表:每一個節點指向下一個節點而且最後一個節點指向第一個節點的鏈表。
  • 時間複雜度:
    • 索引: O(n)
    • 搜索: O(n)
    • 插入: O(1)
    • 移除: O(1)
      鏈表
      Linked List
public class LinkedList {
    /*** * 鏈表一般由一連串節點組成,每一個節點包含任意的實例數據(data fields)和一或兩個用來指向上一個/或下一個節點的位置的連接("links") */
    private Node head; //鏈表頭
    private Node tail; //鏈表尾
    private int size; //節點數

    /** * 雙端鏈表 */
    public class Node{
        private Object data;
        private Node prev; //上一個
        private Node next; //下一個

        public Node(Object data) {
            this.data = data;
        }
    }

    public LinkedList() {
        this.head = null;
        this.tail = null;
        this.size = 0;
    }

    /** * 向鏈表頭添加數據 * @param object */
    public void addHead(Object object){
        Node node = new Node(object);
        if(size == 0){
            head = node;
            tail = node;
            size++;
        }else{
            head.prev = node;
            node.next = head;
            head = node;
            size++;
        }
    }

    /** * 刪除頭 */
    public void deleteHead(){
        //頭部指向下一個,prev值爲null則說明是鏈表的頭部
        if(size != 0){
            head.prev = null;
            head = head.next;
            size--;
        }
    }


    /** *向鏈表尾添加數據 * @param object */
    public void addTail(Object object){
        Node node = new Node(object);
        if(size == 0){
            head = node;
            tail = node;
            size++;
        }else{
            node.prev = tail;
            tail.next = node;
            tail = node;
            size++;
        }
    }

    /** * 刪除尾部 */
    public void deleteTail(){
        //尾部指向上一個,next值爲null則說明是鏈表的尾部
        if(size != 0){
            tail.next = null;
            tail = tail.prev;
            size--;
        }
    }

    /** * 顯示數據 */
    public void display(){
        Node node = head;
        while (size > 0){
            System.out.print("["+node.data+"->");
            node = node.next;
            size--;
        }
    }

    public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        linkedList.addHead("123");
// linkedList.addHead("abc");
// linkedList.addHead("%$$");
// linkedList.addTail("+_+");
// linkedList.addTail("hello");
        linkedList.addTail("word");
        linkedList.deleteHead();
        linkedList.deleteTail();
        linkedList.display();
    }
}
複製代碼

Binary Tree

二叉樹(由一個根結點和兩棵互不相交的、分別稱爲根結點的左子樹和右子樹組成) 二叉樹便是每一個節點最多包含左子節點與右子節點這兩個節點的樹形數據結構。

  • 滿二叉樹: 樹中的每一個節點僅包含 0 或 2 個節點。
  • 完美二叉樹(Perfect Binary Tree): 二叉樹中的每一個葉節點都擁有兩個子節點,而且具備相同的高度。
  • 徹底二叉樹: 除最後一層外,每一層上的結點數均達到最大值;在最後一層上只缺乏右邊的若干結點。
    二叉樹
    Binary Tree
    紅黑樹
    紅黑樹(每節點五元素,顏色(若是一個節點是紅的,則它的兩個兒子都是黑的)、鍵值、左子節點、右字節的、父節點)

Heap

  • 堆(也被稱爲優先隊列(隊列+排序規則),圖一最大堆,圖二最小堆)
  • 堆是一種特殊的基於樹的知足某些特性的數據結構,整個堆中的全部父子節點的鍵值都會知足相同的排序條件。堆更準確地能夠分爲最大堆與最小堆,在最大堆中,父節點的鍵值永遠大於或者等於子節點的值,而且整個堆中的最大值存儲於根節點;而最小堆中,父節點的鍵值永遠小於或者等於其子節點的鍵值,而且整個堆中的最小值存儲於根節點。
  • 時間複雜度:
    • 訪問最大值 / 最小值: O(1)
    • 插入: O(log(n))
    • 移除最大值 / 最小值: O(log(n))
      堆

Hashing

  • 哈希可以將任意長度的數據映射到固定長度的數據。哈希函數返回的便是哈希值,若是兩個不一樣的鍵獲得相同的哈希值,即將這種現象稱爲碰撞。
  • Hash Map: Hash Map 是一種可以創建起鍵與值之間關係的數據結構,Hash Map 可以使用哈希函數將鍵轉化爲桶或者槽中的下標,從而優化對於目標值的搜索速度。
  • 碰撞解決
    • 鏈地址法(Separate Chaining): 鏈地址法中,每一個桶是相互獨立的,包含了一系列索引的列表。搜索操做的時間複雜度便是搜索桶的時間(固定時間)與遍歷列表的時間之和。
    • 開地址法(Open Addressing): 在開地址法中,當插入新值時,會判斷該值對應的哈希桶是否存在,若是存在則根據某種算法依次選擇下一個可能的位置,直到找到一個還沒有被佔用的地址。所謂開地址法也是指某個元素的位置並不永遠由其哈希值決定。

Alt text
Hashing

Graph

  • 圖是一種數據元素間爲多對多關係的數據結構,加上一組基本操做構成的抽象數據類型。
    • 無向圖(Undirected Graph): 無向圖具備對稱的鄰接矩陣,所以若是存在某條從節點 u 到節點 v 的邊,反之從 v 到 u 的邊也存在。
    • 有向圖(Directed Graph): 有向圖的鄰接矩陣是非對稱的,即若是存在從 u 到 v 的邊並不意味着必定存在從 v 到 u 的邊。

Alt text
Graph

算法

排序

快速排序

  • 穩定: 否
  • 時間複雜度:
    • 最優時間: O(nlog(n))
    • 最壞時間: O(n^2)
    • 平均時間: O(nlog(n))

Alt text
Quicksort

歸併排序

  • 歸併排序是典型的分治算法,它不斷地將某個數組分爲兩個部分,分別對左子數組與右子數組進行排序,而後將兩個數組合併爲新的有序數組。
  • 穩定: 是
  • 時間複雜度:
    • 最優時間: O(nlog(n))
    • 最壞時間: O(nlog(n))
    • 平均時間: O(nlog(n))

桶排序

  • 桶排序將數組分到有限數量的桶子裏。每一個桶子再個別排序(有可能再使用別的排序算法或是以遞歸方式繼續使用桶排序進行排序)。
  • 時間複雜度:
    • 最優時間: Ω(n + k)
    • 最壞時間: O(n^2)
    • 平均時間:Θ(n + k)

Alt text
Bucket Sort

基數排序

  • 基數排序相似於桶排序,將數組分割到有限數目的桶中;不過其在分割以後並無讓每一個桶單獨地進行排序,而是直接進行了合併操做。
  • 時間複雜度:
    • 最優時間: Ω(nk)
    • 最壞時間: O(nk)
    • 平均時間: Θ(nk)

圖算法

深度優先搜索

  • 深度優先算法是一種優先遍歷子節點而不是回溯的算法。
  • 時間複雜度: O(|V| + |E|)

DFS / BFS Traversal

廣度優先搜索

  • 廣度優先搜索是優先遍歷鄰居節點而不是子節點的圖遍歷算法。
  • 時間複雜度: O(|V| + |E|)

DFS / BFS Traversal

拓撲排序

  • 拓撲排序是對於有向圖節點的線性排序,若是存在某條從 u 到 v 的邊,則認爲 u 的下標先於 v。
  • 時間複雜度: O(|V| + |E|)

Dijkstra 算法

  • Dijkstra 算法 用於計算有向圖中單源最短路徑問題。
  • 時間複雜度: O(|V|^2)

Dijkstra's

Bellman-Ford 算法

  • **Bellman-Ford 算法**是在帶權圖中計算從單一源點出發到其餘節點的最短路徑的算法。
  • 儘管算法複雜度大於 Dijkstra 算法,可是它適用於包含了負值邊的圖。
  • 時間複雜度:
    • 最優時間: O(|E|)
    • 最壞時間: O(|V||E|)

Bellman-Ford

Floyd-Warshall 算法

  • Floyd-Warshall 算法 可以用於在無環帶權圖中尋找任意節點的最短路徑。
  • 時間複雜度:
    • 最優時間: O(|V|^3)
    • 最壞時間: O(|V|^3)
    • 平均時間: O(|V|^3)

Prim 算法

  • **Prim 算法**是用於在帶權無向圖中計算最小生成樹的貪婪算法。換言之,Prim 算法可以在圖中抽取出鏈接全部節點的邊的最小代價子集。
  • 時間複雜度: O(|V|^2)

Prim's Algorithm

Kruskal 算法

  • **Kruskal 算法**一樣是計算圖的最小生成樹的算法,與 Prim 的區別在於並不須要圖是連通的。
  • 時間複雜度: O(|E|log|V|)

Kruskal's Algorithm

位運算

  • 位運算便是在位級別進行操做的技術,合適的位運算可以幫助咱們獲得更快地運算速度與更小的內存使用。
  • 測試第 k 位: s & (1 << k)
  • 設置第 k 位: s |= (1 << k)
  • 第 k 位置零: s &= ~(1 << k)
  • 切換第 k 位值: s ^= ~(1 << k)
  • 乘以 2n: s << n
  • 除以 2n: s >> n
  • 交集: s & t
  • 並集: s | t
  • 減法: s & ~t
  • 交換 x = x ^ y ^ (y = x)
  • 取出最小非 0 位(Extract lowest set bit): s & (-s)
  • 取出最小 0 位(Extract lowest unset bit): ~s & (s + 1)
  • 交換值: x ^= y; y ^= x; x ^= y;

結語

未完待續......。
我的博客~
簡書~

相關文章
相關標籤/搜索