算法学习数组,链表,栈,队列总结

  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;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章