LEETCODE 刷题记录 MEDIUM类

版权声明:转载请附加出处 https://blog.csdn.net/tianhao199703/article/details/85223802

238. Product of Array Except Self

原问题

Given an array nums of n integers where n > 1, return an array output such that output[i] is equal to the product of all the elements of nums except nums[i].

Note: Please solve it without division and in O(n).

Follow up:
Could you solve it with constant space complexity? (The output array does not count as extra space for the purpose of space complexity analysis.)

心得

双向思维的再次应用。

vector<int> productExceptSelf(vector<int>& nums) {
        int n=nums.size();
        int fromBegin=1;
        int fromLast=1;
        vector<int> res(n,1);
        
        for(int i=0;i<n;i++){
            res[i]*=fromBegin;
            fromBegin*=nums[i];
            res[n-1-i]*=fromLast;
            fromLast*=nums[n-1-i];
        }
        return res;
    }

347. Top K Frequent Elements

原问题

Given a non-empty array of integers, return the k most frequent elements.

心得

我所首先想到的思路是优先队列,C++中priority_queue的使用。

vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int,int> freq;
        vector<int> output;
        for(int i=0;i<nums.size();i++)
        {
            freq[nums[i]]++;
        }
        priority_queue<pair<int,int>> pq; 
        for(auto i = freq.begin(); i != freq.end(); i++)
        {
            pq.push(make_pair(i->second, i->first));
            if(pq.size() > (int)freq.size() - k)
            {
                output.push_back(pq.top().second);
                pq.pop();
            }
        }
        return output;
    }

看了讨论区的解答后,还发现一种比较好用的方法,桶排序:

vector<int> topKFrequent(vector<int>& nums, int k) {
        vector<int> res;
        if (!nums.size()) return res;
        unordered_map<int, int> cnt;
        for (auto num : nums) cnt[num]++;
        vector<vector<int>> bucket(nums.size() + 1);
        for (auto kv : cnt) {
            bucket[kv.second].push_back(kv.first);
        }

        for (int i = bucket.size() - 1; i >= 0; --i) {
            for (int j = 0; j < bucket[i].size(); ++j){
                res.push_back(bucket[i][j]);
                if (res.size() == k) return res;
            }
        }

        return res;
    }

22. Generate Parentheses

原问题

Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

心得

记录有多少左括号,直到记录的括号数为0

vector<string> generateParenthesis(int n) {
        vector<string> res;
        addingpar(res, "", n, 0);
        return res;
    }
    //n表示待匹配的左括号数,m表示已匹配的左括号还剩对应的右括号数
    void addingpar(vector<string> &v, string str, int n, int m){
        if(n==0 && m==0) 
        {
            v.push_back(str);
            return;
        }
        if(m > 0){ addingpar(v, str+")", n, m-1); }
        if(n > 0){ addingpar(v, str+"(", n-1, m+1); }
    }

378.Kth Smallest Element in a Sorted Matrix

原问题

Given a n x n matrix where each of the rows and columns are sorted in ascending order, find the kth smallest element in the matrix.

Note that it is the kth smallest element in the sorted order, not the kth distinct element.

心得

我之前构思的方法是满足时间复杂度O(NlogN),但是讨论区给出了一种更快的O(N)的党阀,没有太看懂,放一个链接[论文出处][http://www.cse.yorku.ca/~andy/pubs/X+Y.pdf]

48.Rotate Image

原问题

You are given an n x n 2D matrix representing an image.

Rotate the image by 90 degrees (clockwise).

Note:

You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate another 2D matrix and do the rotation.

心得

原址旋转图像的操作记录

void rotate(vector<vector<int>>& matrix) {
        reverse(matrix.begin(), matrix.end());
        for (int i = 0; i < matrix.size(); ++i) {
            for (int j = i + 1; j < matrix[i].size(); ++j)
                swap(matrix[i][j], matrix[j][i]);
        }
    }

289. Game of Life

原问题

According to the Wikipedia’s article: “The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970.”

Given a board with m by n cells, each cell has an initial state live (1) or dead (0). Each cell interacts with its eight neighbors (horizontal, vertical, diagonal) using the following four rules (taken from the above Wikipedia article):

  1. Any live cell with fewer than two live neighbors dies, as if caused by under-population.
  2. Any live cell with two or three live neighbors lives on to the next generation.
  3. Any live cell with more than three live neighbors dies, as if by over-population…
  4. Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.

Write a function to compute the next state (after one update) of the board given its current state. The next state is created by applying the above rules simultaneously to every cell in the current state, where births and deaths occur simultaneously.

心得

这类问题可以采用有限状态机方式,同时保存旧的状态和新的状态来实现原址操作。

void gameOfLife(vector<vector<int>>& board) {
        int m = board.size(), n = m ? board[0].size() : 0;
        for (int i=0; i<m; i++) 
        {
            for (int j=0; j<n; j++) 
            {
                int count = 0;
                for (int I=max(i-1, 0); I<min(i+2, m); I++)
                    for (int J=max(j-1, 0); J<min(j+2, n); J++)
                    {
                        if(i==I&&j==J){continue;}
                        count += board[I][J] & 1;
                    }                        
                if ((board[i][j] & 1)==0)
                {
                    if(count==3) {board[i][j]=2;}
                    else{board[i][j]=0;}
                }
                else
                {
                    if(count==3||count==2) {board[i][j]=3;}
                    else{board[i][j]=1;}
                }
            }
        }
        for (int i=0; i<m; ++i)
            for (int j=0; j<n; ++j)
                board[i][j] >>= 1;
    }

36. Valid Sudoku

原问题

Determine if a 9x9 Sudoku board is valid.

心得

给出一个讨论区里面相对简洁的方法,采用flag矩阵判断是否valid

bool isValidSudoku(vector<vector<char> > &board)
    {
        int used1[9][9] = {0}, used2[9][9] = {0}, used3[9][9] = {0};
        
        for(int i = 0; i < board.size(); ++ i)
            for(int j = 0; j < board[i].size(); ++ j)
                if(board[i][j] != '.')
                {
                    int num = board[i][j] - '0' - 1, k = i / 3 * 3 + j / 3;
                    if(used1[i][num] || used2[j][num] || used3[k][num])
                        return false;
                    used1[i][num] = used2[j][num] = used3[k][num] = 1;
                }
        
        return true;
    }

300. Longest Increasing Subsequence

原问题

Given an unsorted array of integers, find the length of longest increasing subsequence.

心得

我们给出三个策略判断增加一个已存在的序列是否是安全的

  • 如果A[i]是所有lists中最小的,则开一个新的list,长度为1
  • 如果A[i]是所有lists中最大的,则扩展已有的lists
  • 如果不是以上两种情况,则clone并扩展所有最后一个元素小于A[i]的lists,同时销毁和扩展后lists同长度的lists

当计算最大长度时,则只需要计算末尾的元素即可。

int lengthOfLIS(vector<int>& nums) {
        vector<int> res;
        for(int i=0; i<nums.size(); i++) {
            auto it = lower_bound(res.begin(), res.end(), nums[i]);
            if(it==res.end())
            {
                res.push_back(nums[i]);
            }
            else
            {
                *it = nums[i];
            }
        }
        for(int i=0;i<res.size();i++)
        {
            cout<<res[i];
        }
        return res.size();
    }

该问题可以联动类似题目334. Increasing Triplet Subsequence

103.Binary Tree Zigzag Level Order Traversal

原问题

Given a binary tree, return the zigzag level order traversal of its nodes’ values. (ie, from left to right, then right to left for the next level and alternate between).

心得

我给出的解法是,先把每一层输出出来,然后对奇数层的vector做一个reverse。讨论区给了一种更直接的方法,采用一个队列保存节点,弹出的时候则判断是应该正向输入还是逆向输入。

vector<vector<int> > zigzagLevelOrder(TreeNode* root) {
    if (root == NULL) {
        return vector<vector<int> > ();
    }
    vector<vector<int> > result;

    queue<TreeNode*> nodesQueue;
    nodesQueue.push(root);
    bool leftToRight = true;

    while ( !nodesQueue.empty()) {
        int size = nodesQueue.size();
        vector<int> row(size);
        for (int i = 0; i < size; i++) {
            TreeNode* node = nodesQueue.front();
            nodesQueue.pop();

            // find position to fill node's value
            int index = (leftToRight) ? i : (size - 1 - i);

            row[index] = node->val;
            if (node->left) {
                nodesQueue.push(node->left);
            }
            if (node->right) {
                nodesQueue.push(node->right);
            }
        }
        // after this level
        leftToRight = !leftToRight;
        result.push_back(row);
    }
    return result;
}

200. Number of Islands

原问题

Given a 2d grid map of '1’s (land) and '0’s (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.

心得

本质搜索算法的一个应用,先贴我的代码:

int numIslands(vector<vector<char>>& grid) {
        if(!grid.size()) return 0;
        if(!grid[0].size()) return 0;
        int m=grid.size(),n=grid[0].size();
        int count=0;

        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(grid[i][j]=='1')
                {
                    count++;
                    mark(grid,i,j);
                } 
            }
        }
        return count;
    }
    void mark(vector<vector<char>>& grid,int i,int j){
        grid[i][j] = '0';
        if(i > 0 && grid[i-1][j] == '1')
            mark(grid, i-1, j);
        if(i < grid.size()-1 && grid[i+1][j] == '1')
            mark(grid, i+1, j);
        if(j > 0 && grid[i][j-1] == '1')
            mark(grid, i, j-1);
        if(j < grid[0].size()-1 && grid[i][j+1] == '1')
            mark(grid, i, j+1);
    }

只有等于1的时候才计数,同时通过DFS将与当前单元相连的全部‘1’单元找出来并置为‘0’。
BFS,宽度优先搜索算法,示例图如下
BFS

73. Set Matrix Zeroes

原问题

Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in-place.

心得

最难得地方在于要inplace完成目标。问题的核心在于如何保存哪些行和列需要被置为0,采用的方法是先找出最末的包含0的那行,用这一行来储存哪些列要被改变的信息,然后遍历找到哪些行要被改变即可。代码贴下:

void setZeroes(vector<vector<int>>& matrix) {
        int H = matrix.size();
        int W = matrix[0].size();
        // find the last 0 row
        int last_0_row = -1;
        for (int y = H - 1; y >= 0 && last_0_row == -1; y--)
            for (int x = 0; x < W; x++)
                if (matrix[y][x] == 0)
                {
                    last_0_row = y;
                    break;
                }
        if (last_0_row == -1)
            return;
        // go row by row
        for (int y = 0; y < last_0_row; y++)
        {
            bool this_is_a_0_row = false;
            
            for (int x = 0; x < W; x++)
            {
                if (matrix[y][x] == 0)
                {
                    this_is_a_0_row = true;
                    matrix[last_0_row][x] = 0;
                }
            }
            
            if (this_is_a_0_row)
            for (int x = 0; x < W; x++)
            {
                matrix[y][x] = 0;
            }
        }
        // set collums to 0
        for (int y = 0; y < H; y++)
        for (int x = 0; x < W; x++)
        {
            if (matrix[last_0_row][x] == 0)
                matrix[y][x] = 0;
        }
        // set the last 0 row 
        for (int x = 0; x < W; x++)
        {
            matrix[last_0_row][x] = 0;
        }
    }

105. Construct Binary Tree from Preorder and Inorder Traversal

原题目

Given preorder and inorder traversal of a tree, construct the binary tree.

心得

这个题目本身思路简单,记录一下遇到的一个坑:copy函数想要截取vector的时候,接受的vector需要初始化大小才可以。
举例子:copy(v1.begin(),v1.end(),v2.begin()),v2是必须初始化一个大小的,这个时候我们可以直接在初始化的时候完成,即:
vector<int> v2(v1.begin(),v1.end())
还有一个注意的地方是vector迭代器赋值是左闭右开的,经常就忘了。
该问题代码如下:

TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        TreeNode *root = new TreeNode(1);
        if(preorder.size()==0){return NULL;}
        root->val = preorder[0];
        auto it = find(inorder.begin(),inorder.end(),preorder[0]);
        vector<int> inleft(inorder.begin(),it),inright(it+1, inorder.end());
        vector<int> preleft(preorder.begin()+1, preorder.begin()+inleft.size()+1);
        vector<int> preright(preorder.begin()+inleft.size()+1, preorder.end()); 
        root->left = buildTree(preleft, inleft);
        root->right = buildTree(preright, inright);
        return root;
    }

395. Longest Substring with At Least K Repeating Characters

原问题

Find the length of the longest substring T of a given string (consists of lowercase letters only) such that every character in T appears no less than k times.

心得

我一开始的思路是,每次对子串进行统计每个字符出现次数,从第一个遇到的不满足次数大于k的字符那里把s分为两个子串,递归调用子串得到结果。代码放下:

int longestSubstring(string s, int k) {
        if(s.size() == 0 || k > s.size())   return 0;
        if(k == 0)  return s.size();
        unordered_map<char,int> show;
        for(int i=0;i<s.size();i++)
        {
            show[s[i]]++;
        }
        int idx =0;
        while(idx <s.size() && show[s[idx]] >= k)    idx++;
        if(idx == s.size()) return s.size();
        int left = longestSubstring(s.substr(0 , idx) , k);
        int right = longestSubstring(s.substr(idx+1) , k);
        
        return max(left, right);
    }

虽然结果是正确的,但是leetcode提示我超时了,评论区有一个迭代的做法。思路是这样的:逐位去寻找是否有合适的子串,并在找的过程中记录出现过的最大子串长度,如果找到了一个子串则没有必要检查它覆盖的子串。代码如下:

int longestSubstring(string s, int k) {
   int max_len = 0;
   for (int first = 0; first+k <= s.size();) {
       int count[26] = {0};//每次记录字符出现次数
       int mask = 0;
       int max_last = first;
       for (int last = first; last < s.size(); ++last) {
           int i = s[last] - 'a';
           count[i]++;
           if (count[i]<k) mask |= (1 << i);//记录当前子串中包含哪些字符
           else   mask &= (~(1 << i));//如果一个字符在当前子串中已经够了k个时,将它从mask中去除
           
           if (mask == 0) {//找到一个符合要求的子串
               max_len = max(max_len, last-first+1);
               max_last = last;//当前子串的末尾位置,如果之后没有变化,说明被截断了,之后不需要考虑这部分
           }
       }
       first = max_last + 1;//从新的截断处出发
   }
   return max_len;
}

116. Populating Next Right Pointers in Each Node

原问题

Given a binary tree

struct TreeLinkNode {
TreeLinkNode *left;
TreeLinkNode *right;
TreeLinkNode *next;
}
Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL.

Initially, all next pointers are set to NULL.

心得

注意利用好所有条件。next别忘了用。。。

void connect(TreeLinkNode *root) {
        if(!root)
        return;
        while(root -> left)
        {
            TreeLinkNode *p = root;
            while(p)
            {
                p -> left -> next = p -> right;
                if(p -> next)
                    p -> right -> next = p -> next -> left;
                p = p -> next;
            }
            root = root -> left;
        }
    }

207. Course Schedule

原题目

There are a total of n courses you have to take, labeled from 0 to n-1.

Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]

Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?

心得

这个问题是判断一个有向图是否含有闭环的问题。分别由两种解决方案。
BFS:方法是重复寻找一个入度为0的顶点,将该顶点从图中删除(即放进一个队列里存着,这个队列的顺序就是最后的拓扑排序,具体见程序),并将该结点及其所有的出边从图中删除(即该结点指向的结点的入度减1),最终若图中全为入度为1的点,则这些点至少组成一个回路。
代码如下:

 bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) {
        vector<unordered_set<int>> graph = make_graph(numCourses, prerequisites);
        vector<int> degrees = compute_indegree(graph);
        for (int i = 0; i < numCourses; i++) {
            int j = 0;
            for (; j < numCourses; j++)
                if (!degrees[j]) break;
            if (j == numCourses) return false;
            degrees[j] = -1;
            for (int neigh : graph[j])
                degrees[neigh]--;
        }
        return true;
    }
    vector<unordered_set<int>> make_graph(int numCourses, vector<pair<int, int>>& prerequisites) {
        vector<unordered_set<int>> graph(numCourses);
        for (auto pre : prerequisites)
            graph[pre.second].insert(pre.first);
        return graph;
    }
    vector<int> compute_indegree(vector<unordered_set<int>>& graph) {
        vector<int> degrees(graph.size(), 0);
        for (auto neighbors : graph)
            for (int neigh : neighbors)
                degrees[neigh]++;
        return degrees;
    }

DFS:假设图以邻接矩阵表示,一条深度遍历路线中如果有结点被第二次访问到,那么有环。我们用一个变量来标记某结点的访问状态(未访问,访问过,其后结点都被访问过),然后判断每一个结点的深度遍历路线即可。
代码如下:

bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) {
        vector<unordered_set<int>> graph = make_graph(numCourses, prerequisites);
        vector<bool> onpath(numCourses, false), visited(numCourses, false);
        for (int i = 0; i < numCourses; i++)
            if (!visited[i] && dfs_cycle(graph, i, onpath, visited))
                return false;
        return true;
    }
    vector<unordered_set<int>> make_graph(int numCourses, vector<pair<int, int>>& prerequisites) {
        vector<unordered_set<int>> graph(numCourses);
        for (auto pre : prerequisites)
            graph[pre.second].insert(pre.first);
        return graph;
    } 
    bool dfs_cycle(vector<unordered_set<int>>& graph, int node, vector<bool>& onpath, vector<bool>& visited) {
        if (visited[node]) return false;
        onpath[node] = visited[node] = true; 
        for (int neigh : graph[node])
            if (onpath[neigh] || dfs_cycle(graph, neigh, onpath, visited))
                return true;
        return onpath[node] = false;
    }

原题目

心得

这个题目本身不算很难,但是如果不采用排序的方法,而是用一个区间树来维护数据结构就有挑战性了。实现算法如下:

void QueryInterval(vector<Interval> &retV, IntervalTree *node){
//retV is the return vector
	vector<Interval> leftIntervals;
	vector<Interval> rightIntervals;

	bool mergeleft = false; //whether current node merge with any intervals from left child. 
	if(node->left)
	{
    	//return the merge of all intervals in left child. 
    	QueryInterval(leftIntervals, node->left);
	    //merge left interval with myself. 
	    MergeLeftInterval(leftIntervals, node, retV, mergeleft);
	}
	if(!mergeleft)
	{ //if we did not merge left intervals, add a new one
    	Interval newinterval;
    	newinterval.start = node->start;
    	newinterval.end = node->end;
    	retV.push_back(newinterval);
	}
	if(node->right)
	{
    	QueryInterval(rightIntervals, node->right);
    	MergeRightInterval(rightIntervals, node, retV);
	}
	return;
}

void MergeLeftInterval(vector<Interval> &leftIntervals, IntervalTree *node, vector<Interval> &retV, bool &merged){
	for(int i=0; i<leftIntervals.size(); i++)
	{
    	if(leftIntervals[i].end>=node->start)
    	{
        	Interval newinterval;
        	newinterval.start = min(leftIntervals[i].start, node->start);
        	newinterval.end = node->end;
        	retV.push_back(newinterval);
        	merged = true;
        	break;
    	}
    	else{
        	retV.push_back(leftIntervals[i]);
    	}
	}
}

void MergeRightInterval(vector &rightIntervals, IntervalTree *node, vector &retV)
{
	for(int i=0; i<rightIntervals.size(); i++)
	{
		if(rightIntervals[i].start<=node->end)
		{
			retV[retV.size()-1].end = max(rightIntervals[i].end, node->end);
		}	
		else
		{
			retV.push_back(rightIntervals[i]);
		}
	}
}

当然前提是需要自己维护一个区间树的数据结构:

class IntervalTree{
	public:
    	int middle;
    	int start, end;
    	IntervalTree *left, *right;
    	IntervalTree(int s, int e): start(s), end(e), middle((s+e)/2){
        	this->left=this->right=NULL;
    	}
};

void InsertInterval(IntervalTree *node, Interval ¤tInterval){
	if(node == NULL)
    	return;
	if(currentInterval.end<node->middle)
	{
    	if(node->left)
        	return InsertInterval(node->left, currentInterval);
    	else
    	{
        	IntervalTree *newnode = new IntervalTree(currentInterval.start, currentInterval.end);
        	node->left = newnode;
        	return;
    	}
	}
	if(currentInterval.start>node->middle)
	{
    	if(node->right)
        	return InsertInterval(node->right, currentInterval);
    	else
    	{
        	IntervalTree *newnode = new IntervalTree(currentInterval.start, currentInterval.end);
        	node->right = newnode;
        	return;
    	}
	}	
//insert it to current node
	node->start=min(node->start, currentInterval.start);
	node->end=max(node->end, currentInterval.end);
}

236. Lowest Common Ancestor of a Binary Tree

原问题

Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.

心得

我在一开始把问题想复杂了,虽然最后结果是对的但是时间上复杂多了。我的想法是每访问一个节点都需要遍历一遍这个节点的全部子树。但实际上是没有必要的,因为这里有个关键的特性:如果待寻找的两个点出现在不同的子树中,那么当前节点就是我们要的最终结果。简化后的代码如下:

TreeNode * dfsTraverse(TreeNode * root, TreeNode * p , TreeNode * q)
{
    if( root == p || root == q || root == NULL)
        return root;
    TreeNode * parent1 = dfsTraverse(root->left, p, q);
    TreeNode * parent2 = dfsTraverse(root->right, p, q);
    if( parent1 && parent2)//如果待查找的p,q分散在root的左右子树中,返回root
        return root;
    else
        return parent1 ? parent1:parent2;//否则返回对应存在的子树
}

148. Sort List

原问题

Sort a linked list in O(n log n) time using constant space complexity.

心得

只有一点,虽然想到归并排序的方法,但是把归并过程想复杂了,在讨论区看到一个简洁的归并,代码如下:

ListNode* merge( ListNode* head1 , ListNode * head2){
        ListNode* d = new ListNode (0);            // dummy node
        ListNode* e = d;
        while(head1||head2){
            if(head1 && (!head2 || head1->val <= head2 -> val) ){
                e=e->next= head1 ; 
                head1 = head1 -> next;
            }
            if(head2 && (!head1 || head2->val < head1 -> val) ){
                e=e->next= head2 ; 
                head2 = head2 -> next;
            }
        }
        e->next = NULL;
        return d->next;
    }

210. Course Schedule II

原问题

There are a total of n courses you have to take, labeled from 0 to n-1.

Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]

Given the total number of courses and a list of prerequisite pairs, return the ordering of courses you should take to finish all courses.

There may be multiple correct orders, you just need to return one of them. If it is impossible to finish all courses, return an empty array.

心得

之前有类似的问题,这次直接上代码

vector<int> findOrder(int numCourses, vector<pair<int, int>>& prerequisites) {
        vector<unordered_set<int>> graph = make_graph(numCourses, prerequisites);
        vector<int> degrees = compute_indegree(graph);
        queue<int> zeros;
        for (int i = 0; i < numCourses; i++)
            if (!degrees[i]) zeros.push(i);
        vector<int> toposort;
        for (int i = 0; i < numCourses; i++) {
            if (zeros.empty()) return {};
            int zero = zeros.front();
            zeros.pop();
            toposort.push_back(zero);
            for (int neigh : graph[zero]) {
                if (!--degrees[neigh])
                    zeros.push(neigh);
            }
        }
        return toposort;
    }

33. Search in Rotated Sorted Array

原问题

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]).

You are given a target value to search. If found in the array return its index, otherwise return -1.

You may assume no duplicate exists in the array.

Your algorithm’s runtime complexity must be in the order of O(log n).

心得

这个思路很清晰,首先通过二分法找到旋转点,获得旋转点以后,我们可以以旋转点为offset取余,这样对我们来说,nums这个数组就和旋转前的数组没区别了。

int search(vector<int>& nums, int target) {
        int lo=0,hi=nums.size()-1;
        while(lo<hi){
            int mid=(lo+hi)/2;
            if(nums[mid]>nums[hi]) lo=mid+1;
            else hi=mid;
        }
        int rot=lo;
        lo=0;hi=nums.size()-1;
        while(lo<=hi){
            int mid=(lo+hi)/2;
            int realmid=(mid+rot)%nums.size();
            if(nums[realmid]==target)return realmid;
            if(nums[realmid]<target)lo=mid+1;
            else hi=mid-1;
        }
        return -1;
    }

227. Basic Calculator II

原问题

Implement a basic calculator to evaluate a simple expression string.

The expression string contains only non-negative integers, +, -, *, / operators and empty spaces . The integer division should truncate toward zero.

心得

C++引入了ostringstream、istringstream、stringstream这三个类,要使用他们创建对象就必须包含这个头文件。

istringstream类用于执行C++风格的串流的输入操作。
ostringstream类用于执行C风格的串流的输出操作。
strstream类同时可以支持C风格的串流的输入输出操作。

istringstream的构造函数原形如下:
istringstream::istringstream(string str);
它的作用是从string对象str中读取字符。
非常好的思路,term 负责计算两个加减号之间的所有乘法的值,并在下一次读到加减号时,把结果加减在total上。`term *= 44 - op;该行代码用来区别加减号。
代码:

int calculate(string s) {
        istringstream in('+' + s + '+');
        long long total = 0, term = 0, n;
        char op;
        while (in >> op) {
            if (op == '+' or op == '-') {
                total += term;
                in >> term;
                term *= 44 - op;
            } else {
                in >> n;
                if (op == '*')
                    term *= n;
                else
                    term /= n;
            }
        }
        return total;
    }

55. Jump Game

原问题

Given an array of non-negative integers, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Determine if you are able to reach the last index

心得

用一个reach记录当前可以到达的最远距离,当i>reach意味着没办法从之前的路径到达之后的节点,所以认为是跳不过去。这个思路在之前有些题也有类似的应用。包括之前股票买入卖出问题(EASY121)也是设置了max_so_far用来记录出现过的最大值。这类问题要注意。

    bool canJump(vector<int>& nums) {     
        int i = 0;
        for (int reach = 0; i < nums.size() && i <= reach; ++i)
            reach = max(i + nums[i], reach);
        return i == nums.size();
    }

322. Coin Change

原问题

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

心得

换硬币问题,我首先想到了递归的方法去做,但是很明显时间复杂度很高。看到讨论区有这么一种方法,类似于动态规划的填表法,将每一个之前的最小值记录下来的操作。关于动态规划填表法的相关应用,我之前看过好几道类似题,但还是不长记性,希望之后再遇到类似的问题是能留意。这种可以说是典型的以空间换时间的操作了。

int coinChange(vector<int>& coins, int amount) {
        int Max = amount + 1;
        vector<int> dp(amount + 1, Max);
        dp[0] = 0;
        for (int i = 1; i <= amount; i++) {
            for (int j = 0; j < coins.size(); j++) {
                if (coins[j] <= i) {
                    dp[i] = min(dp[i], dp[i - coins[j]] + 1);
                }
            }
        }
        return dp[amount] > amount ? -1 : dp[amount];
    }

324. Wiggle Sort II

原问题

Given an unsorted array nums, reorder it such that nums[0] < nums[1] > nums[2] < nums[3]…
Can you do it in O(n) time and/or in-place with O(1) extra space?

心得

这个问题如果不考虑附加条件比较简单,只需要先排好序然后大小混着填即可。考虑到O(n)的时间复杂度和常量级别的空间复杂度就比较困难了。我看到评论区里面有人给出了一种基于“虚拟index”思想的方法。
这个方法的思路是首先利用nth_element找到中位数,然后#define A(i) nums[(1+2*(i)) % (n|1)]很巧妙,把给定数组按照先奇数后偶数的方式对应起来,
然后通过交换方法让左边的奇数位全部都是大于median的数,右边则全部是小于median的数,这样相应的原来的数组中顺序就是wiggle的。可以说是很巧妙了。

void wiggleSort(vector<int>& nums) {
    int n = nums.size();
    
    // Find a median.
    auto midptr = nums.begin() + n / 2;
    nth_element(nums.begin(), midptr, nums.end());
    int mid = *midptr;
    
    // Index-rewiring.
    #define A(i) nums[(1+2*(i)) % (n|1)]

    // 3-way-partition-to-wiggly in O(n) time with O(1) space.
    int i = 0, j = 0, k = n - 1;
    while (j <= k) {
        if (A(j) > mid)
            swap(A(i++), A(j++));
        else if (A(j) < mid)
            swap(A(j), A(k--));
        else
            j++;
    }
}

回文数专题

leetcode上面目前做到了有关回文数的题目一共有四道,在这里做一个整理和总结。回文数是一个比较麻烦的处理点,性质是从前往后读和从后往前读的结果相同。

125. Valid Palindrome

不考虑大小写,忽略非字符,判断给定的字符串是不是回文串。比较简单的问题,不过相比我给出的代码,C++本身提供了封装好的函数,可以相对简洁的完成这个任务:

bool isPalindrome(string s) {
    for (int i = 0, j = s.size() - 1; i < j; i++, j--) { // Move 2 pointers from each end until they collide
        while (isalnum(s[i]) == false && i < j) i++; // Increment left pointer if not alphanumeric
        while (isalnum(s[j]) == false && i < j) j--; // Decrement right pointer if no alphanumeric
        if (toupper(s[i]) != toupper(s[j])) return false; // Exit and return error if not match
    }
    
    return true;
}

234. Palindrome Linked List

这是一个将回文数和链表结合起来的问题。比较简单,没有空间要求的情况下可以利用栈解决问题。

131. Palindrome Partitioning

这个的思路是顺位从开始到结尾判断是否是回文数,是则加入最后的表中

5. Longest Palindromic Substring

这个的思路是记录出现过的最长的回文数的位置和长度,同样是从0开始扩展。

    string longestPalindrome(string s) {
        if (s.empty()) return "";
        if (s.size() == 1) return s;
        int min_start = 0, max_len = 1;
        for (int i = 0; i < s.size();) {
          if (s.size() - i <= max_len / 2) break;
          int j = i, k = i;
          while (k < s.size()-1 && s[k+1] == s[k]) ++k; // Skip duplicate characters.
          i = k+1;
          while (k < s.size()-1 && j > 0 && s[k + 1] == s[j - 1]) { ++k; --j; } // Expand.
          int new_len = k - j + 1;
          if (new_len > max_len) { min_start = j; max_len = new_len; }
        }
        return s.substr(min_start, max_len);
    }

3. Longest Substring Without Repeating Characters

原问题

Given a string, find the length of the longest substring without repeating characters.

心得

没什么特别难的,但是在讨论区中找到一个很好的解决方法,即:我们在本题中不需要给出最长无重复子串,只需要给出长度就好,这样没有必要吧之前的步骤都记录下来。

int lengthOfLongestSubstring(string s) {
        vector<int> dict(256, -1);
        int maxLen = 0, start = -1;
        for (int i = 0; i != s.length(); i++) {
            if (dict[s[i]] > start)
                start = dict[s[i]];
            dict[s[i]] = i;
            maxLen = max(maxLen, i - start);
        }
        return maxLen;
    }

98. Validate Binary Search Tree

原问题

Given a binary tree, determine if it is a valid binary search tree (BST).

Assume a BST is defined as follows:

The left subtree of a node contains only nodes with keys less than the node’s key.
The right subtree of a node contains only nodes with keys greater than the node’s key.
Both the left and right subtrees must also be binary search trees.

心得

这个问题没能给出答案,我脑中构想的一个复杂的算法是每个结点都做一次全盘检测,很显然这个算法的复杂度会非常的高。讨论区中有一个和巧妙的方法:它相当于每次座椅旋转,针对每个结点,都有它对应的最小值位置和最大值位置(当不存在是为NULL),只要满足三代关系即可是核心思想。代码如下:

bool isValidBST(TreeNode* root) {
        return isValidBST(root, NULL, NULL);
    }
    bool isValidBST(TreeNode* root, TreeNode* minNode, TreeNode* maxNode) {
        if(!root) return true;
        if(minNode && root->val <= minNode->val || maxNode && root->val >= maxNode->val)
            return false;
        return isValidBST(root->left, minNode, root) && isValidBST(root->right, root, maxNode);
    }

179. Largest Number

原问题

Given a list of non negative integers, arrange them such that they form the largest number.

心得

我一开始的想法是利用基数排序,对于高位相同的数来说,位数少的优先,但这样实现起来很复杂,讨论区给出了一种简单的方法。其实思路很简单,不对数字排序,而是对转化后的字符串排序,利用sort函数 自己写一个比较器,这样会简洁的多。

string largestNumber(vector<int>& nums) {
        vector<string> arr;
        for(auto i:nums)
            arr.push_back(to_string(i));
        sort(begin(arr), end(arr), [](string &s1, string &s2){ return s1+s2>s2+s1; });
        string res;
        for(auto s:arr)
            res+=s;
        while(res[0]=='0' && res.length()>1)
            res.erase(0,1);
        return  res;
    }

15. 3Sum

原问题

Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.

Note:

The solution set must not contain duplicate triplets.

心得

这个问题,我一上来准备使用暴力方法,利用multiset解决重复问题,显然时间复杂度为O( N 3 N^3 )是行不通的。因此评论区给了一个方法,思路是:先对数组进行排序,排好序之后再固定一个元素的情况下,分别从前后两端开始查找。

vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        sort(nums.begin(), nums.end());
        for (int i = 0; i < nums.size(); i++) {       
            int target = -nums[i];
            int front = i + 1;
            int back = nums.size() - 1;    
            while (front < back) {   
                int sum = nums[front] + nums[back];                
                // Finding answer which start from number num[i]
                if (sum < target)
                    front++;    
                else if (sum > target)
                    back--;    
                else {
                    vector<int> triplet(3, 0);
                    triplet[0] = nums[i];
                    triplet[1] = nums[front];
                    triplet[2] = nums[back];
                    res.push_back(triplet);                   
                    // Processing duplicates of Number 2
                    // Rolling the front pointer to the next different number forwards
                    while (front < back && nums[front] == triplet[1]) front++;    
                    // Processing duplicates of Number 3
                    // Rolling the back pointer to the next different number backwards
                    while (front < back && nums[back] == triplet[2]) back--;
                }               
            }    
            // Processing duplicates of Number 1
            while (i + 1 < nums.size() && nums[i + 1] == nums[i]) 
                i++;    
        }        
        return res;
    }

127. Word Ladder

原问题

Given two words (beginWord and endWord), and a dictionary’s word list, find the length of shortest transformation sequence from beginWord to endWord, such that:

Only one letter can be changed at a time.
Each transformed word must exist in the word list. Note that beginWord is not a transformed word.
Note:

Return 0 if there is no such transformation sequence.
All words have the same length.
All words contain only lowercase alphabetic characters.
You may assume no duplicates in the word list.
You may assume beginWord and endWord are non-empty and are not the same.

心得

这个题思路上来说是比较简单的,就是一个BFS的变种应用,但是我写的代码出了点问题,不知道是哪里考虑的不周全,贴一个别人的BFS:

int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        int length=INT_MAX;
        if(distance(beginWord,endWord)==0){return 1;}
        if(find(wordList.begin(),wordList.end(),endWord)==wordList.end()){return 0;}//如果endword不在List里面,返回0
        unordered_set<string> wordDict;
        for(auto &i:wordList){wordDict.insert(i);}
        unordered_set<string> head, tail, *phead, *ptail;
        head.insert(beginWord);
        tail.insert(endWord);
        int dist = 2;
        while (!head.empty() && !tail.empty()) {
            if (head.size() < tail.size()) {
                phead = &head;
                ptail = &tail;
            }
            else {
                phead = &tail; 
                ptail = &head;
            }
            unordered_set<string> temp; 
            for (auto itr = phead -> begin(); itr != phead -> end(); itr++) {
                string word = *itr;
                wordDict.erase(word);
                for (int p = 0; p < (int)word.length(); p++) {
                    char letter = word[p];
                    for (int k = 0; k < 26; k++) {
                        word[p] = 'a' + k;
                        if (ptail -> find(word) != ptail -> end())
                            return dist;
                        if (wordDict.find(word) != wordDict.end()) {
                            temp.insert(word);
                            wordDict.erase(word);
                        }
                    }
                    word[p] = letter;
                }
            }
            dist++;
            swap(*phead, temp);
        }
        return 0; 
    }
    int distance(string a,string b){
        int dist=0;
        for(int i=0;i<a.size();i++){
            if(a[i]!=b[i]){dist++;}
        }
        return dist;
    }

29. Divide Two Integers

原问题

Given two integers dividend and divisor, divide two integers without using multiplication, division and mod operator.

Return the quotient after dividing dividend by divisor.

The integer division should truncate toward zero.

心得

记录一下bit操作完成除法的过程,和ESAY371 联动。

int divide(int dividend, int divisor) {
        if (dividend == INT_MIN && divisor == -1)
            return INT_MAX;
        bool sign = ((dividend < 0) ^ (divisor < 0)) ? 0 : 1;
		long dvd = dividend<0?(~(long)dividend+1):(long)dividend;
        long dvs = divisor<0?(~(long)divisor+1):(long)divisor;
        int res = 0;
        while (dvd >= dvs) { 
            long temp = dvs, multiple = 1;
            while (dvd >= (temp << 1)) {
                temp <<= 1;
                multiple <<= 1;
            }
            dvd -= temp;
            res += multiple;
        }
        return sign == 1 ? res : -res; 
    }

31. Next Permutation

原问题

Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.

If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).

The replacement must be in-place and use only constant extra memory.

心得

一开始连题目都没弄懂,后来才搞清楚是要找排序的下一个。思路如下:
swap是给定下标的两个元素之间的交换,reverseSort则是颠倒给定区间的顺序。先判断从尾部开始的最长的逆序的开始位置,如果index为0则表明整个序列逆序,只需要将序列调为正序即可;不为0则记录下逆序起点的值,

void nextPermutation(vector<int>& nums) {
        int n_len=nums.size();
        if(n_len<2)
            return;
        int index=n_len-1;        
        while(index>0){
            if(nums[index-1]<nums[index])
                break;
            index--;
        }
        if(index==0){
            reverseSort(nums,0,n_len-1);
            return;
        }
        else{
            int val=nums[index-1];
            int j=n_len-1;
            while(j>=index){
                if(nums[j]>val)
                    break;
                j--;
            }
            swap(nums,j,index-1);
            reverseSort(nums,index,n_len-1);
            return;
        }
    }
    
    void swap(vector<int>& num, int i, int j){
        int temp=0;
        temp=num[i];
        num[i]=num[j];
        num[j]=temp;
    }
    
    void reverseSort(vector<int>& num, int start, int end){   
        if(start>end)
            return;
        for(int i=start;i<=(end+start)/2;i++)
            swap(num,i,start+end-i);
    }

43. Multiply Strings

原问题

Given two non-negative integers num1 and num2 represented as strings, return the product of num1 and num2, also represented as a string.

心得

我的思路没有问题,都是从低往高一位一位的算,不过这里有个非常优秀的代码:

string multiply(string num1, string num2) {
    string sum(num1.size() + num2.size(), '0');
    
    for (int i = num1.size() - 1; 0 <= i; --i) {
        int carry = 0;
        for (int j = num2.size() - 1; 0 <= j; --j) {
            int tmp = (sum[i + j + 1] - '0') + (num1[i] - '0') * (num2[j] - '0') + carry;
            sum[i + j + 1] = tmp % 10 + '0';
            carry = tmp / 10;
        }
        sum[i] += carry;
    }
    
    size_t startpos = sum.find_first_not_of("0");
    if (string::npos != startpos) {
        return sum.substr(startpos);
    }
    return "0";
}

47. Permutations II

原问题

Given a collection of numbers that might contain duplicates, return all possible unique permutations.

心得

讨论区给出了一个非常好的回答,他的思路是,先对给的数组进行排序,然后把每一个需要交换位置的元素都交换一遍位置,这样就保证了不重复。

void recursion(vector<int> num, int i, int j, vector<vector<int> > &res) {
        if (i == j-1) {
            res.push_back(num);
            return;
        }
        for (int k = i; k < j; k++) {
            if (i != k && num[i] == num[k]) continue;
            swap(num[i], num[k]);
            recursion(num, i+1, j, res);
        }
    }
    vector<vector<int> > permuteUnique(vector<int> &num) {
        sort(num.begin(), num.end());
        vector<vector<int> >res;
        recursion(num, 0, num.size(), res);
        return res;
    }

54,59 spiral matrix

心得

这两个问题都是螺旋矩阵的问题,对于这类问题的处理有一个通用的解法。这里将59的代码放下面:

//col,row是移动中的标志,而u,r,d,l,则是四个边界
while (true) {
            for (int col = l; col <= r; col++) matrix[u][col] = n_sqr++;//向右遍历
            if (++u > d) break;//判断是不是到达边界,同时向下移动一行
            for (int row = u; row <= d; row++) matrix[row][r] = n_sqr++;
            if (--r < l) break;//判断是不是到达边界,同时向左移动一列
            for (int col = r; col >= l; col--) matrix[d][col] = n_sqr++;
            if (--d < u) break;//判断是不是到达边界,同时向上移动一行
            for (int row = d; row >= u; row--) matrix[row][l] = n_sqr++;
            if (++l > r) break;//判断是不是到达边界,同时向右移动一列
        }

63. Unique Paths II

原问题

A robot is located at the top-left corner of a m x n grid (marked ‘Start’ in the diagram below).

The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked ‘Finish’ in the diagram below).

Now consider if some obstacles are added to the grids. How many unique paths would there be?

心得

与unique path I 不同,II中路径上出现了阻碍物。对于这类问题,我的第一反应是递归,事实证明时间复杂度太高。这时候动态规划就是一个很好的选择,可以保证在O(MN)内完成。附上代码:

int uniquePathsWithObstacles(vector<vector<int> > &obstacleGrid) {
        int m = obstacleGrid.size() , n = obstacleGrid[0].size();
        vector<vector<int>> dp(m+1,vector<int>(n+1,0));//构造一个存放到达该点的路径数的数组,初始值设为0
        dp[0][1] = 1;//这里是为了保证能够有一条路到达dp[1][1],当obstacleGrid[0][0]不为1
        for(int i = 1 ; i <= m ; ++i)
            for(int j = 1 ; j <= n ; ++j)
                if(!obstacleGrid[i-1][j-1])//堵塞的路就是到达该点的路径数为0
                    dp[i][j] = dp[i-1][j]+dp[i][j-1];//对于(i,j),因为只有两种走法,所以能到达他的路径等于能到他左边点的路径数+能到他上边点的路径数
        return dp[m][n];
    }

猜你喜欢

转载自blog.csdn.net/tianhao199703/article/details/85223802