算法學習第二週總結

Hash、樹、堆

本週主要學習了hashmap,映射,集合,樹,heap以及圖相關的基礎知識,以及對應的相關算法。

HashMap

關於hash的主要內容:
1. Hash表是根據關鍵碼值(key)進行直接訪問的數據結構,通過hash函數把key映射到表中一個位置來進行訪問,查找元素的時間複雜度爲O(1)。
2. hash映射函數也叫散列函數,存放記錄的數組叫做哈希表。
3. hash表的做法,就是把key值通過設定的hash函數轉換成一個int數字,然後將該數字對數組長度進行取餘,取餘結果即爲數組的下標,然後將value的值存儲在數組的該位置。如果hash值相同或者取餘結果相同時候,通過拉鍊模式來存儲多個元素,即該位置爲鏈表的root節點,往下依次遍歷數值來確定key的準確位置。一般來說拉鍊模式最大長度爲8,因爲該模式下,hash的時間複雜度退化到O(k),k爲該位置鏈表長度,所以一個好的hash函數儘量避免重複的hash key值顯得尤爲重要。
hash表內部結構簡單示意圖

  1. 樹作爲一種非常常見的二維數據結構,從子節點數量可以分爲二叉樹,N叉樹等,從使用特點上分爲二叉搜索樹,紅黑樹等。
  2. 樹的定義本身沒法有效的進行所謂的循環,而且樹對於父節點與子節點處理方式基本一致,所以樹一般使用遞歸算法來進行實現。
  3. 二叉搜索樹也叫二叉排序樹,指二叉樹每個節點的左子樹上的所有節點的值均小於該節點的值,右子樹上的所有節點值均大於該節點的值。因爲二叉搜索樹的有序性,在查找,添加,刪除等方面基本都爲O(logN)的時間複雜度。中序遍歷爲升序排列。
    關於二叉樹的前中後遍歷,使用棧的實現
//前序遍歷
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> res;
        if(root)
        {
            stack<TreeNode *> st;
            st.push(root);
            while(!st.empty())
            {
                TreeNode *node = st.top();
                st.pop();
                if(node)
                {
                    res.push_back(node->val);
                    if(node->right)
                        st.push(node->right);
                    if(node->left)
                        st.push(node->left);
                }
            }
        }
        return res;
    }

    //中序遍歷
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        if(root)
        {
            stack<TreeNode *> st;
            st.push(root);
            while(!st.empty())
            {
                TreeNode *node = st.top();
                st.pop();
                if(node)
                {
                    if(node->right)
                        st.push(node->right);
                    st.push(node);
                    st.push(nullptr);
                    if(node->left)
                        st.push(node->left);
                }
                else
                {
                    res.push_back(st.top()->val);
                    st.pop();
                }
            }
        }
        return res;
    }

    //後續遍歷
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> res;
        if(root)
        {
            stack<TreeNode *> st;
            st.push(root);
            while(!st.empty())
            {
                TreeNode *node = st.top();
                st.pop();
                if(node)
                {
                    st.push(node);
                    st.push(nullptr);
                    if(node->right)
                        st.push(node->right);
                    if(node->left)
                        st.push(node->left);
                }
                else
                {
                    res.push_back(st.top()->val);
                    st.pop();
                }
            }
        }
        return res;
    }

Heap

  1. Heap 可以迅速的找到一堆數中的最大值或者最小值的數據結構。
  2. 將根節點最大的堆叫做大頂堆或者大根堆,根節點最小的堆叫做最小頂堆或者小根堆,常見的堆有二叉堆、斐波那契堆等。
  3. C++中基本的堆數據結構爲優先隊列priority_queue,初始化方法如下
priority_queue<pair<int, int>, vector<pair<int, int>>, std::greater<pair<int, int>>> qu; //以第一個int排序,爲大根堆
priority_queue<int, vector<int>, std::less<int>> qu; //int類型小根堆
priority_queue<int> qu;  //int類型小根堆
	附帶二叉堆的簡單實現
//二叉堆, 完全二叉樹來實現的,所以可以使用數組來存放元素
class BinaryHeap
{
    int d = 2; //二叉樹,定義最大子節點數爲2
    vector<int> heap;
    int heapSize;

public:
    BinaryHeap(int capacity)
    {
        heapSize = 0;
        heap.resize(capacity + 1, -1);
    }

    //是否爲空
    bool isEmpty()
    {
        return heapSize == 0;
    }

    //是否已滿
    bool isFull()
    {
        return heapSize == heap.size();
    }

    //父節點下標
    int parent(int i)
    {
        return (i - 1) / d;
    }

    //子節點下標,k從1開始
    int kthChild(int i, int k)
    {
        return i * d + k;
    }

    //插入
    void insert(int val)
    {
        if(isFull())
        {
            cout << "Heap is full, No space to insert new element!" << endl;
            return ;
        }
        heap[heapSize++] = val;  //將插入的數值放入結尾,然後依次向上浮動
        heapifyUp(heapSize);  
    }

    //刪除i爲下標位置,返回刪除的值
    int remove(int i = 0)
    {
        if(isEmpty())
        {
            cout << "Heap is empty, No element to remove!" << endl;
            return -1;
        }
        int removeElement = heap[i];  
        heap[i] = heap[--heapSize]; //將最後一個值放在刪除的下標上,然後依次向下浮動
        heapifyDown(i);
        return removeElement;
    }

    //上浮
    void heapifyUp(int i)
    {
        int insertValue = heap[i];
        while(i > 0 && insertValue > heap[parent(i)])
        {
            heap[i] = heap[parent(i)];
            i = parent(i);
        }
        heap[i] = insertValue;
    }

    //下沉
    void heapifyDown(int i)
    {
        int child, temp = heap[i];
        while(kthChild(i, 1) < heapSize)
        {
            child = maxChild(i);
            if(temp >= heap[child])
                break;
            heap[i] = heap[child];
            i = child;
        }
        heap[i] = temp;
    }

    //最大子節點
    int maxChild(int i)
    {
        int leftChild = kthChild(i, 1);
        int rightChild = kthChild(i, 2);
        return heap[leftChild] > heap[rightChild] ? leftChild : rightChild;
    }

    //查找最大值
    int findMax()
    {
        if(isEmpty())
        {
            cout << "Heap is empty!" << endl;
            return -1;
        }
        return heap[0];
    }

    //打印堆
    void printHeap()
    {
        cout << "Heap = ";
        for(int i = 0; i < heapSize; i++)
            cout << heap[i] << " ";
        cout << endl;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章