算法學習數組,鏈表,棧,隊列總結

  1. 數組
    數組基本上屬於使用最多的一種數據結構,因爲其連續的內存分佈,使得訪問和查找等方面都可以做到O(1)時間複雜度,缺點就在添加和刪除操作上,因爲需要保持內存的連續性,需要對後續的元素進行移動,所以該部分算法爲O(N)。
    數組在訪問方面,使用下標或者迭代器基本上沒有差別,唯一問題可能在使用迭代器會顯得比較高大上一些而已。
    關於本週練習的數組方面操作:

     刪除排序數組中的重複項:
    
	//使用stl內置函數實現
	int removeDuplicates(vector<int>& nums) {
        if(nums.size() < 2)
            return nums.size();
        vector<int>::iterator it = unique(nums.begin(), nums.end());
        return (it - nums.begin());
    }
    //或者自行移動元素
    int removeDuplicates(vector<int>& nums) {
        if(nums.size() < 2)
            return nums.size();

        int i = 0, j = 0;
        while(++i < nums.size())
        {
            if(nums[i] != nums[j])
                nums[++j] = nums[i];
        }
        return j + 1;
    }
    //上邊兩種做法都沒有真正的從數組中刪除元素,只是交換完元素後,把結束位置返回了出來,節省數組刪除元素時O(N)的操作。
  合併兩個有序數組:
	void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        if(n < 1)
            return;

        int i = m - 1;
        int j = n - 1;
        int k = m + n - 1;
        while(i >= 0 && j >= 0)
        {
            nums1[k--] = nums1[i] > nums2[j] ? nums1[i--] : nums2[j--];
        }
        if(j >= 0)
            std::copy(nums2.begin(), nums2.begin() + j + 1, nums1.begin());
    }
給定一個由整數組成的非空數組所表示的非負整數,在該數的基礎上加一:
	vector<int> plusOne(vector<int>& digits) {
        for(int i = digits.size() - 1; i >= 0; i--)
        {
            digits[i]++;
            if(digits[i] != 10)
                return digits;
            else
                digits[i] = 0;                                          
        }
        if(digits[0] == 0)
        {
            digits[0] = 1;
            digits.push_back(0);
        }
        return digits;
    }
	給定一個數組,將數組中的元素向右移動 k 個位置,其中 k 是非負數。
	//數組向右移動k位,就等於從(n-k)分開,反轉左右兩邊,然後再反轉整個數組
	void rotate(vector<int>& nums, int k) {
        std::reverse(nums.begin(), nums.end() - k % nums.size());
        std::reverse(nums.end() - k % nums.size(), nums.end());
        std::reverse(nums.begin(), nums.end());
    }
    //當然stl內置庫也有該函數
    //std::rotate(nums.begin(), nums.end() - k % nums.size(), nums.end());
	給定一個數組 nums,編寫一個函數將所有 0 移動到數組的末尾,同時保持非零元素的相對順序。
    void moveZeroes(vector<int>& nums) {
        if(nums.size() > 1)
        {
            for(int i = 0, j= 0; i < nums.size(); i++)
            {
                if(nums[i] != 0)
                    swap(nums[i], nums[j++]);                             
            }
        }
    }
	輸入一個矩陣,按照從外向裏以順時針的順序依次打印出每一個數字。
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector<int>res;      
        int m = matrix.size();
        if(m <= 1)
            return matrix[0];
        int n = matrix[0].size();
        res.resize(m * n);
        int index = 0;
        int lmin = 0, lmax = n - 1, hmin = 0, hmax = m - 1;   
        while(lmin <= lmax || hmin <= hmax)
        {           
            for(int l = lmin; l <= lmax; l++)
            {
                res[index++] = matrix[hmin][l];
            }

            for(int h = hmin + 1; h <= hmax; h++)
            {
                res[index++] = matrix[h][lmax];
            }           
         
            if(lmin < lmax && hmin < hmax)
            {
                for(int l = lmax - 1; l > lmin; l--)
                {
                    res[index++] = matrix[hmax][l];
                }
                for(int h = hmax; h > hmin; h--)
                {
                    res[index++] = matrix[h][lmin];
                }
            }
            lmin++;
            hmin++;
            lmax--;
            hmax--;
        }
        return res;
    }
		給你一個未排序的整數數組,請你找出其中沒有出現的最小的正整數。
	//將每個元素放在對應下標的位置上,然後遍歷找到第一個不等於下標+1的數即是所求。
    int firstMissingPositive(vector<int>& nums) {
        int n = nums.size();
        for (int i = 0; i < n; ++i) {
            while (nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) {
                swap(nums[nums[i] - 1], nums[i]);
            }
        }
        for (int i = 0; i < n; ++i) {
            if (nums[i] != i + 1) {
                return i + 1;
            }
        }
        return n + 1;
    }
	LRU緩存機制的實現
class LRUCache {
    int size = 0;
    vector<int> operVec;
    map<int, int> cacheMap;
public:
    LRUCache(int capacity) {
        size = capacity;
    }
    
    int get(int key) {
        if(cacheMap.find(key) != cacheMap.end())
        {
            if(operVec.size() == size)
                operVec.erase(operVec.begin() + getIndex(key));
            operVec.insert(operVec.begin(), key);
            return cacheMap[key];
        }
        return -1;
    }
    
    void put(int key, int value) {
        if(operVec.size() == size)
        {
            if(cacheMap.find(key) == cacheMap.end())
            {
                cacheMap.erase(operVec[size - 1]);
                operVec.erase(operVec.begin() + size - 1);
            }                
            else
            {
                operVec.erase(operVec.begin() + getIndex(key));
            }      
        }
        else
        {
            if(cacheMap.find(key) != cacheMap.end())
            {
                operVec.erase(operVec.begin() + getIndex(key));
            }
        }
        cacheMap[key] = value;
        operVec.insert(operVec.begin(), key);
    }

    int getIndex(int key)
    {
        for(int i = 0; i < operVec.size(); i++)
        {
            if(key == operVec[i])
                return i;
        }
        return -1;
    }
};
  1. 鏈表
    鏈表也是平時經常使用的一種數據結構,它在使用上剛好與數組的時間複雜度互補,刪除和插入操作都是O(1),但是在查找方面需要從頭到尾依次遍歷,所以爲O(N)。鏈表由分爲單向鏈表,雙向鏈表,循環鏈表等,在使用中主要掌握鏈表的添加和刪除操作。

    將兩個升序鏈表合併爲一個新的 升序 鏈表並返回。新鏈表是通過拼接給定的兩個鏈表的所有節點組成的。

	//遍歷的方法來實現
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode *preHead = new ListNode(-1);
        ListNode *node = preHead;
        while(l1 && l2)
        {
            if(l1->val <= l2->val)
            {
                node->next = l1;
                l1 = l1->next;
            }
            else
            {
                node->next = l2;
                l2 = l2->next;
            }
            node = node->next;
        }
        node->next = l1 == nullptr ? l2 : l1;
        return preHead->next;
    }
    //使用遞歸
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if(!l1)
            return l2;
        if(!l2)
            return l1;
        if(l1->val < l2->val)
        {
            l1->next = mergeTwoLists(l1->next, l2);
            return l1;
        }
        else
        {
            l2->next = mergeTwoLists(l1, l2->next);
            return l2;
        }
    }
	給定一個鏈表,兩兩交換其中相鄰的節點,並返回交換後的鏈表。
	//遍歷
    ListNode* swapPairs(ListNode* head) {
        ListNode *node = head, *prev = NULL, *prev2 = NULL;
        head = (head && head->next)  ? head->next : head;
        while(node)
        {
            if(!prev)
            {
                prev = node; 
                node = node->next;
            }         
            else
            {
                prev->next = node->next;
                node->next = prev;                
                if(prev2)
                    prev2->next = node;
                node = prev->next;
                prev2 = prev;
                prev = NULL;
            }         
        }
        return head;
    }
    //遞歸
	ListNode* swapPairs(ListNode* head) {
        if(!head || !(head->next))
            return head;
        ListNode *first = head, *second = head->next;
        first->next = swapPairs(second->next);
        second->next = first;
        return second;
    }
	編寫代碼,移除未排序鏈表中的重複節點。保留最開始出現的節點。
    ListNode* removeDuplicateNodes(ListNode* head) {
     unordered_set<int> st;
       ListNode *node = head, *prev=nullptr;
        while(node)
        {
            if(st.count(node->val))
            {
                prev->next = node->next;
            }
            else
            {
                prev = node;
                st.insert(node->val);
            }
            if(node->next == nullptr)
                prev->next = nullptr;
            node = node->next;
        }
        return head;
    }
	給定一個鏈表,判斷鏈表中是否有環。
	//快慢指針方法,快指針一次2步,慢指針一次1步,如果兩者相等,則說明有環狀存在。
    bool hasCycle(ListNode *head) {
        if(head == nullptr || head->next == nullptr){
            return false;
        }
        ListNode* slow = head;
        ListNode* fast = head->next;
        while(slow != fast){
            if(fast == nullptr || fast->next == nullptr){
                return false;
            }
            slow = slow->next;
            fast = fast->next->next;
        }
        return true;
    }
	接雨水 給定 n 個非負整數表示每個寬度爲 1 的柱子的高度圖,計算按此排列的柱子,下雨之後能接多少雨水。
    int trap(vector<int>& height) {
        if(height.size() < 3)
            return 0;
        int res = 0, left = 0, right = height.size() - 1;
        int leftMax = 0, rightMax = 0;
        while(left < right)
        {
            if(height[left] < height[right])
            {
                height[left] >= leftMax ? leftMax = height[left] : res += leftMax - height[left];
                left++;
            }
            else
            {
                height[right] >= rightMax ? rightMax = height[right] : res += rightMax - height[right];
                right--;
            }
        }
        return res;
    }

  1. 棧也是線性存儲結構的一種,遵守先進後出(FILO)原則,只能在棧頂進行添加和刪除操作。作爲數據存儲以及計算機系統的基本結構,各種編程語言在編譯時候也是使用棧的格則來進行操作的,在考慮問題時,如果該問題有連續相關性,基本就可以用棧來解決。比如二叉樹的DFS遍歷,就是棧使用的典型範例。
    二叉樹的遍歷
	//遍歷
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> res;
        if(root)
        {
            stack<TreeNode*> st;
            st.push(root);
            while(!st.empty())
            {
                TreeNode* temp = st.top();
                st.pop();
                if(temp)
                {
                    st.push(temp);
                    st.push(nullptr);
                    if(temp->right)
                        st.push(temp->right);
                    if(temp->left)
                        st.push(temp->left);                    
                }
                else
                {
                    res.push_back(st.top()->val);
                    st.pop();
                }         
            }
        }
        return res;
    }
   	/*
   	* 只需要更換中間幾行順序,就可以完美實現前序,中序,後序遍歷,用壓入null節點來作爲輸出標記
   	*/		
	給定一個 N 叉樹,返回其節點值的前序遍歷。
    vector<int> preorder(Node* root) {
        vector<int> res;
        if(root)
        {
            stack<Node *> st;
            st.push(root);
            while(!st.empty())
            {
                Node *temp = st.top();
                st.pop();
                res.push_back(temp->val);
                for(int i = temp->children.size() - 1; i >= 0; i--)
                {
                    st.push(temp->children[i]);
                }
            }
        }
        return res;
    }
	給定一個二叉樹,判斷其是否是一個有效的二叉搜索樹
    bool isValidBST(TreeNode* root) {
        if(!root || (!(root->left) && !(root->right)))
        {
            return true;
        }  

        long long left = LONG_MIN;
        stack<TreeNode*> st;
        st.push(root);
        while(!st.empty())
        {
            TreeNode* tm = st.top();
            st.pop();
            if(tm)
            {
                if(tm->right)  
                    st.push(tm->right);
                st.push(tm);
                st.push(nullptr);                               
                if(tm->left) 
                    st.push(tm->left);
            }
            else
            {
                tm = st.top();
                st.pop();
                if(tm->val > left)
                {
                    left = tm->val;
                }
                else
                {
                    return false;
                }
            }
        }
        return true;
	最小棧,給定一個棧可以快速的返回棧內最小值
	//解題思路爲維護兩個棧,min棧內存儲每個節點的對應的最小值
class MinStack {
    stack<int> st;
    stack<int> stMin;
public:
    /** initialize your data structure here. */
    MinStack() {
        stMin.push(INT_MAX);
    }
    
    void push(int x) {
        st.push(x);
        if(stMin.top() >= x)
            stMin.push(x);
    }
    
    void pop() {           
        if(stMin.top() == st.top())
            stMin.pop();
        st.pop();
    }
    
    int top() {
        return st.top();
    }
    
    int getMin() {
        return stMin.top();
    }
};
  1. 隊列
    隊列作爲與棧對應的一種結構,主要區別在先進先出(FIFO),只能在隊頭添加刪除元素,在隊尾添加元素。與棧類似,也是用於處理連續相關性比較多,常見於樹的BFS遍歷,界面的滑動取值等方面。在基礎數據結構中還存在雙向隊列(Deque)的數據結構,結合了棧和隊列的特點,在雙端皆可以進行添加和刪除操作。

     給定一個含有 n 個正整數的數組和一個正整數 s ,找出該數組中滿足其和 ≥ s 的長度最小的連續子數組,並返回其長度。如果不存在符合條件的連續子數組,返回 0。
    
    int minSubArrayLen(int s, vector<int>& nums) {
        int minSize = INT_MAX;
        deque<int> deq;
        int sum = 0;
        for(int i = 0; i < nums.size(); i++)
        {
            deq.push_back(nums[i]);
            sum += nums[i];
        
            while(sum - deq.front() >= s)
            {
                sum -= deq.front();
                deq.pop_front();
            }

            if(sum >= s && deq.size() < minSize)
            {
                minSize = deq.size();
            }
        }

        if(minSize > nums.size())
            return 0;
        return minSize;
    }
    //遍歷解法
    int minSubArrayLen(int s, vector<int>& nums) {
        int minSize = INT_MAX;
        int sum = 0, j = 0;
        for(int i = 0; i < nums.size(); i++)
        {
            sum += nums[i];
            while(sum >= s)
            {
                minSize = min(minSize, i - j + 1);
                sum -= nums[j++];
            }
        }
        return minSize > nums.size() ? 0 : minSize;
    }
	給你一個二叉樹,請你返回其按 層序遍歷 得到的節點值。 (即逐層地,從左到右訪問所有節點)。
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector <vector <int>> ret;
        if (!root) 
        	return ret;
        queue <TreeNode*> q;
        q.push(root);
        while (!q.empty()) {
            int currentLevelSize = q.size();
            ret.push_back(vector <int> ());
            for (int i = 1; i <= currentLevelSize; ++i) {
                auto node = q.front(); q.pop();
                ret.back().push_back(node->val);
                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }
        }
        return ret;
    }
	設計循環雙端隊列
class MyCircularDeque {
    int size;
    vector<int> values;
public:
    /** Initialize your data structure here. Set the size of the deque to be k. */
    MyCircularDeque(int k) {
        size = k;
    }
    
    /** Adds an item at the front of Deque. Return true if the operation is successful. */
    bool insertFront(int value) {
        if(values.size() == size)
            return false;
        values.insert(values.begin(), value);
        return true;
    }
    
    /** Adds an item at the rear of Deque. Return true if the operation is successful. */
    bool insertLast(int value) {
        if(values.size() == size)
            return false;
        values.push_back(value);
        return true;
    }
    
    /** Deletes an item from the front of Deque. Return true if the operation is successful. */
    bool deleteFront() {
        if(values.size() > 0)
        {
            values.erase(values.begin());
            return true;
        }
        return false;
    }
    
    /** Deletes an item from the rear of Deque. Return true if the operation is successful. */
    bool deleteLast() {
        if(values.size() > 0)
        {
            values.pop_back();
            return true;
        }
        return false;
    }
    
    /** Get the front item from the deque. */
    int getFront() {
        if(values.size() > 0)
            return values.front();
        return -1;
    }
    
    /** Get the last item from the deque. */
    int getRear() {
        if(values.size() > 0)
            return values.back();
        return -1;
    }
    
    /** Checks whether the circular deque is empty or not. */
    bool isEmpty() {
        return values.empty();
    }
    
    /** Checks whether the circular deque is full or not. */
    bool isFull() {
        return values.size() == size;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章