找工作--笔试面试--准备5

1、Same Tree


Given two binary trees, write a function to check if they are equal or not.

Two binary trees are considered equal if they are structurally identical and the nodes have the same value.

 典型的递归树的问题。

bool isSameTree(TreeNode *p, TreeNode *q) {
        if(p==NULL|q==NULL){
			if(p!=q){
				return false;
			}
			else
				return true;
		}
		if(p!=q&&p->val==q->val){
			return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
		}
		else{
			return false;
		}
    }

2、Unique Binary Search Trees I II

Given n, how many structurally unique BST's (binary search trees) that store values 1...n?

For example,
Given n = 3, there are a total of 5 unique BST's.

   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3
也是递归的方式,从当前的数组中选出一个root,大于该root的push到右孩子,小于的则push到左孩子。动态规划来做,由于给定n个数,n个数组成的二叉树是一定的,那么,我们就可以这么来考虑了。记得当时这个也是参考别人的。

num(n) = num(i)+num(n-i);

int numTrees(int n) {//一维动态规划
		if(n<=1){
			return n;
		}
        int *way = new int[n+1];
		way[0] = 1;
		way[1] = 1;
		for(int i = 2;i<=n;i++){
			way[i]=0;
			for(int left = 0;left<i;left++){
				way[i]+=way[left]*way[i-1-left];
			}
		}
		int ret = way[n];
		delete way;
		return way[n];
    }

Given n, generate all structurally unique BST's (binary search trees) that store values 1...n.

For example,
Given n = 3, your program should return all 5 unique BST's shown below.

   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3
这次是寻找BST,将i-j形成的所有二叉树存入vector,这就是动态规划的中间数组。然后后面的继续添加。

vector<TreeNode *> generate(int s,int e){
		vector<TreeNode *> ret;
		if(s>e){
			ret.push_back(NULL);
			return ret;
		}
		for(int i = s;i<=e;i++){
			vector<TreeNode *> left = generate(s,i-1);
			vector<TreeNode*> right = generate(i+1,e);
			for(int j = 0;j<left.size();j++){
				for(int k = 0;k<right.size();k++){
					TreeNode * node = new TreeNode(i+1);//数字i为root
					ret.push_back(node);
					node->left = left[j];
					node->right = right[k];
				}
			}
		}
		return ret;//返回s-e形成的所有树
	}
	vector<TreeNode *> generateTrees(int n) {
        return generate(0,n-1);
    }
2、Interleaving String

Given s1s2s3, find whether s3 is formed by the interleaving of s1 and s2.

For example,
Given:
s1 = "aabcc",
s2 = "dbbca",

When s3 = "aadbbcbcac", return true.
When s3 = "aadbbbaccc", return false.

interleaving是交叉加错的意思,意思是s1和s2交叉得到的字符串,是否是s3。典型的动态规划。

m个s1和n个s2可以组成m+n个s3的前缀
A[m][n] = A[m-1][n](s1[m]==s3[m+n])||A[m][n-1](s2[n]==s3[m+n])

这个动态公式自己是当时想出来,现在整理有些忘了,额。。。

bool isInterleave(string s1, string s2, string s3) {
        if(s1.size()+s2.size()!=s3.size()){
            return false;
        }
        vector<vector<bool>> vec;
		for(int i = 0;i<=s1.size();i++){
			vector<bool> tmp(s2.size()+1);
			for(int j = 1;j<=s2.size();j++){
				tmp[j] = false;
			}
			vec.push_back(tmp);
		}
		vec[0][0]= true;
		for(int i = 1; i <= s1.size(); i++)
			vec[i][0] = vec[i-1][0] && (s3[i-1] == s1[i-1]);             
         for(int j = 1; j <= s2.size(); j++)
             vec[0][j] = vec[0][j-1] && (s3[j-1] == s2[j-1]);
		for(int i = 1;i<=s1.size();i++)
			for(int j = 1;j<=s2.size();j++)
				vec[i][j] =(s1[i-1]==s3[i+j-1]&&vec[i-1][j])||(s2[j-1]==s3[i+j-1]&&vec[i][j-1]);

		return vec[s1.size()][s2.size()];
		
    }

3、Validate Binary Search Tree && Recover Binary Search Tree

把这两个问题放在一块,一个是检测是否正确的tree,一个是BST有两个结点交换了,修复。

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.
这个问题想起来似乎很容易,但是仔细想象似乎又不那么容易,25.9%的通过率。

依然是递归方式,不过将中间结果进行存储,判断一个二叉查找数的正确与否,中序遍历就可以了。

class Solution {
public:
    TreeNode * pre;
    bool result;
    bool isValidBST(TreeNode *root) {
        pre = NULL;
        result = true;
        inorder(root);
        return result;
    }
    void inorder(TreeNode * root){
        if(!result){
            return;
        }
        if(NULL==root){
            return;
        }
        inorder(root->left);
        if(pre!=NULL&&pre->val>=root->val){//递减排序,前一个不可能大于等于第二个
            result = false;
        }
        pre = root;
        inorder(root->right);
    }
};

Two elements of a binary search tree (BST) are swapped by mistake.

Recover the tree without changing its structure.

Note:
A solution using O(n) space is pretty straight forward. Could you devise a constant space solution?

BST中两个结点交换了,进行修复,不能改变结构。两个指针进行保存出错的两个指针,这里需要考虑的是,这两个指针可能就是root和left child,所以如果第一次记录的时候,需要将违反规定的两个数据进行同时保存,后期如果遇到其他的错误,则将第二个指针进行覆盖。

依然是中序遍历。。。这个是参考别人的,虽然现在可以理解,但是却根本无法想到这条道路上去。todo

class Solution {
public:
	TreeNode * pre,*s1,*s2;
    void recoverTree(TreeNode *root) {
        pre = s1 = s2 = NULL;
		inorder_find(root);
		swap(s1->val,s2->val);
    }
	void inorder_find(TreeNode * root){
		if(NULL == root){
			return;
		}
		inorder_find(root->left);
		if(pre!=NULL&&pre->val>root->val){
			if(s1 == NULL){
				s1 = pre;
				s2 = root;
			}
			else{
				s2 = root;
			}
		}
		pre = root;
		inorder_find(root->right);
	}
};

4、Restore IP Addresses

 

Given a string containing only digits, restore it by returning all possible valid IP address combinations.

For example:
Given "25525511135",

return ["255.255.11.135", "255.255.111.35"]. (Order does not matter)

这次看,没想到我的方法是8ms过测试大集合,但是我用另外自己搜到的一个,虽然易懂,但是花费的时间趋势20多ms了。

自己的方法,比较繁琐,如果真要写的话,需要考虑很多情况。好长,不过也不怎么想解释了,其实就是建立三个循环,但是考虑某些位置上是0,或者不能通过的情况

class Solution {
public:
/**
j建立三个大循环,然后相当于分割成4份
但是这种方法不好,调试也是一会,如果是面试题,肯定会悲剧的

**/
    vector<string> restoreIpAddresses(string s) {
        vector<string> ret;
        if(s.size()>12||s.size()<4){
            return ret;
        }
		string dot = ".";
		int len = s.size();
		for(int i=0;i<3;i++){
			string tmp = "";
			if(s[0]=='0'&&i==0){
				tmp = '0';
			}
			else{
				if(s[0]=='0'){
					break;
				}
				tmp = s.substr(0,i+1);
				if(!GetData(tmp)){
					continue;
				}
			}
			for(int j=i+1;j<=i+3&j<=len-3;j++){
				if(s[i+1]=='0'&&j==i+1){
					tmp.push_back('.');
					tmp.push_back('0');
				}
				else{
					if(s[i+1]=='0'){
						break;
					}
					if(!GetData(s.substr(i+1,j-i))){
						continue;
					}
					tmp.append(".");
					tmp.append(s.substr(i+1,j-i));
				
				}
				
				for(int k = j+1;k<=len-2&&k<=j+3;k++){
					if(s[j+1]=='0'&&k==j+1){
						tmp.push_back('.');
						tmp.push_back('0');

					}
					else{
						if(s[j+1]=='0'){
							break;
						}
						if(!GetData(s.substr(j+1,k-j))){
							continue;
						}
						tmp.append(".");
						tmp.append(s.substr(j+1,k-j));
					}
					if(s[k+1]=='0'){
						if(k+1 == len-1){
							tmp.push_back('.');
							tmp.push_back('0');
							ret.push_back(tmp);
							while(tmp.back()!='.'){
								tmp.pop_back();
							}
							tmp.pop_back();
						}
						while(tmp.back()!='.'){
							tmp.pop_back();
						}
						tmp.pop_back();
						continue;
					}
					if(!GetData(s.substr(k+1,len-k))){
						while(tmp.back()!='.'){
							tmp.pop_back();
						}
						tmp.pop_back();
						continue;
					}
					tmp.append(".");
					tmp.append(s.substr(k+1,len-k));
					ret.push_back(tmp);
					while(tmp.back()!='.'){
						tmp.pop_back();
					}
					tmp.pop_back();
					while(tmp.back()!='.'){
						tmp.pop_back();
					}
					tmp.pop_back();
				}
				while(tmp.back()!='.'){
					tmp.pop_back();
				}
				tmp.pop_back();
			}
		}
		return ret;
    }
	bool GetData(string s){
		int sum = 0;
		for(int i = 0;i<s.size();i++){
			sum = sum*10+(s[i]-'0');
		}
		if(sum>0&&sum<=255){
			return true;
		}
		else
			return false;
	}
};
第二种方法,很容易懂了。。DFS的方法,

class Solution {
private:
    vector<string> ret;
    int pos[4];
public:
    bool check(string &s, int beg, int end)
    {
        string ip(s, beg, end - beg + 1);
        if (ip.size() == 1)
            return "0" <= ip && ip <= "9";
        else if (ip.size() == 2)
            return "10" <= ip && ip <= "99";
        else if (ip.size() == 3)
            return "100" <= ip && ip <= "255";
        else
            return false;
    }
    
    void dfs(int dep, int maxDep, string &s, int start)
    {
        if (dep == maxDep)
        {
            if (start == s.size())
            {
                int beg = 0;
                string addr;
                for(int i = 0; i < maxDep; i++)
                {
                    string ip(s, beg, pos[i] - beg + 1);
                    beg = pos[i] + 1;
                    addr += i == 0 ? ip : "." + ip;
                }
                ret.push_back(addr);    
            }
            return;
        }
        
        for(int i = start; i < s.size(); i++)
            if (check(s, start, i))
            {
                pos[dep] = i;
                dfs(dep + 1, maxDep, s, i + 1);               
            }
    }
    
    vector<string> restoreIpAddresses(string s) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        ret.clear();
        dfs(0, 4, s, 0);
        return ret;
    }
};

5、Reverse Linked List II && 

Rotate List

 

Reverse a linked list from position m to n. Do it in-place and in one-pass.

For example:
Given 1->2->3->4->5->NULLm = 2 and n = 4,

return 1->4->3->2->5->NULL.

Note:
Given mn satisfy the following condition:
1 ≤ m ≤ n ≤ length of list.

这个之所以加一个II,因为有一个题目是反转一个结点到结尾的一段距离。


将m-n之间的反转,然后记住反转之前的pre和end,然后指向就可以了,需要考虑m=1

class Solution {
public:
    ListNode *reverseBetween(ListNode *head, int m, int n) {
        if(m==n){
            return head;
        }
        int i = 0;
        ListNode * begin = head;
        ListNode * end = head;
        ListNode * beginpre = head;
        while(i<n-m){
            end = end->next;
            i++;
        }
        i = 1;
        while(i<m){
            beginpre = begin;
            begin = begin->next;
            end = end->next;
            i++;
        }
        ListNode * endnext = end->next;
        reverse(begin,end);
        
		if(begin == head){
			begin->next = endnext;
			return end;
		}
		else{
			begin->next = endnext;
			beginpre->next = end;
			return head;
		}
       
        
    }
    void reverse(ListNode * begin,ListNode * end){
        if(begin == end){
            return;
        }
        ListNode * cur = begin;
        ListNode * curnext = begin->next;
        ListNode * p = curnext;
        while(curnext!=end){
            p=p->next;
            curnext->next = cur;
            cur = curnext;
            curnext = p;
        }
        end->next = cur;
    }
};

Given a list, rotate the list to the right by k places, where k is non-negative.

For example:
Given 1->2->3->4->5->NULL and k = 2,
return 4->5->1->2->3->NULL.

和上面类似

class Solution {
public:
/*
遗忘的情况
1.只有一个元素的时候
2.K是链表的倍数的时候
*/
    ListNode *rotateRight(ListNode *head, int k) {
        if(head==NULL||k<=0){
            return head;
        }
        ListNode * tmphead = head;
        int len = 0;
        ListNode * p = head;
        while(p!=NULL){
            len++;
            p = p->next;
        }
        if(len == 1){
            return head;
        }
        k = k%len;//len是长度
        if(k == 0){
            return head;
        }
        
        k = len-k;
        p = head;
        while(--k>0){
            p = p->next;
        }
        
        head = p->next;
        p->next = NULL;
        p = head;
        while(p->next!=NULL){
            p = p->next;
        }
        p->next = tmphead;
        return head;
    }
};


以上大多是和链表有关的,下面的一些就是和数的操作有关了,排序,变换。


6、Subsets I  II

Given a set of distinct integers, S, return all possible subsets.

Note:

  • Elements in a subset must be in non-descending order.
  • The solution set must not contain duplicate subsets.

For example,
If S = [1,2,3], a solution is:

[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]
题目要求,不能包含重复的子集,而且是不能是降序的。

这个相对于第二题,有一个容易的地方,就是候选集是distinct的,所以递归进行添加数据就可以了。

这里的vector是传值进去的哦,不是引用,这样也就不用再费劲把元素push进去,然后pop出来。


class Solution {
public:
    vector<vector<int> > ret;
	int len;
    vector<vector<int> > subsets(vector<int> &S) {
		len = S.size();
		if(len == 0){
			return ret;
		}
		sort(S.begin(),S.end());
		vector<int> tmp;
		addinvec(0,tmp,S);
		return ret;
    }
	void addinvec(int index,vector<int> tmp,vector<int> &S){
		if(index==len-1){
			ret.push_back(tmp);
			tmp.push_back(S[index]);
			ret.push_back(tmp);
			return;
		}
		
		addinvec(index+1,tmp,S);
		tmp.push_back(S[index]);
		addinvec(index+1,tmp,S);
	}
};
第二题,允许存在相同的元素,但是不允许相同的子集出现。

这里利用了一个非常巧妙的办法,这里利用的是将interger作为一个  个体  参加,比如说,2,2,这两个数据,作为一个元素。1,这样distinct的数也是作为一个数据。

这样在递归的时候就可以控制数量了。

vector<vector<int> > ret;
    vector<vector<int> > subsetsWithDup(vector<int> &S) {
		sort(S.begin(),S.end());
		vector<int> tmp;
		Insert(tmp,0,S);
		return ret;
    }
	void Insert(vector<int> tmp,int begin,vector<int> &S){
		if(begin>=S.size()){
			ret.push_back(tmp);
			return;
		}
		int cur = S[begin];
		int next = begin;
		while(next<S.size()&&S[next]==cur){
			next++;
		}
		Insert(tmp,next,S);
		for(int i = begin;i<next;i++){
			tmp.push_back(S[i]);
			Insert(tmp,next,S);
		}
	}
7、Decode Ways

A message containing letters from A-Z is being encoded to numbers using the following mapping:

'A' -> 1
'B' -> 2
...
'Z' -> 26

Given an encoded message containing digits, determine the total number of ways to decode it.

For example,
Given encoded message "12", it could be decoded as "AB" (1 2) or "L" (12).

The number of ways decoding "12" is 2.

这个是转换,也可以硕士最简单的加密解密。

需要考虑是,按1个和2个元素作为操作单元,考虑0元素。是不是和青蛙跳的那个题目相似,跳一次台阶,跳两次台阶。

定义数组number,number[i]意味着:字符串s[0..i-1]可以有number[i]种解码方法。
回想爬楼梯问题一样,number[i] = number[i-1] + number[i-2]
但不同的是本题有多种限制:
第一: s[i-1]不能是0,如果s[i-1]是0的话,number[i]就只能等于number[i-2]
第二,s[i-2,i-1]中的第一个字符不能是0,而且Integer.parseInt(s.substring(i-2,i))获得的整数必须在0到26之间。

int numDecodings(string s) {
		int len = s.size();
		if(len==0||s[0]=='0'){
			return 0;
		}
		vector<int> vec(len+1);
		vec[0] = 1;
		vec[1] = 1;
		int tmp;
		for(int i = 2;i<=len;i++){
			if(s[i-1]!='0'){//这里可能有些疑惑的地方,这里的意思是,如果i-1不是0,则veci为vec i-1的值,如果不是,veci为0.
			    vec[i] = vec[i-1];
			}
			if(s[i-2]!='0'){
			    if(getchar(s.substr(i-2,2))){
			        vec[i]+=vec[i-2];//配合上面vec i的值,vec i = vec i(vec i-1 ) + vec i-2
			    }
			}
		}
		return vec[len];
    }
	bool getchar(string s){//判断2位字符在1-26之间
		if(s.size()>2||s[0]=='0'){
			return false;
		}
		if((s.size()==1&&s[0]>'0'&&s[0]<='9')||(s.size()==2&&s>="10"&&s<="26")){
			return true;
		}
		else{
			return false;
		}
	} 
8、Merge Sorted Array && 

Merge Two Sorted Lists

 

Given two sorted integer arrays A and B, merge B into A as one sorted array.

Note:
You may assume that A has enough space (size that is greater or equal to m + n) to hold additional elements from B. The number of elements initialized in A and B are m and nrespectively.

归并排序的第二部,merge,从后往前

void merge(int A[], int m, int B[], int n) {
        int i=m-1,j=n-1;
        int index = m+n-1;
        while(i>=0&&j>=0){
            if(A[i]>B[j]){
                A[index--]=A[i];
                i--;
            }
            else{
                A[index--]=B[j];
                j--;
            }
        }
        while(j>=0&&index>=0){
            A[index--]=B[j--];
        }
    }

list的合并,首先确定head,然后将数据依次比较大小添加进入合并list

ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) {
        if(l1 == NULL){
            return l2;
        }
        if(l2 == NULL){
            return l1;
        }
        ListNode * head = l1->val>l2->val?l2:l1;
        
        ListNode * p = head;
        ListNode * t1 = l1,*t2 = l2;
        if(l1->val>l2->val){
            t1 = l1;
            t2 = l2->next;
        }
        else{
            t1 = l1->next;
            t2 = l2;
        }
        while(t1!=NULL&&t2!=NULL){
            
            if(t1->val>t2->val){
                p->next = t2;
                p = p->next;
                t2 = t2->next;
            }else{
                p->next = t1;
                p = p->next;
                t1 = t1->next;
            }
        }
        
        while(t1!=NULL){
            p->next = t1;
            t1 = t1->next;
            p = p->next;
        }
        while(t2!=NULL){
            p->next = t2;
            t2 = t2->next;
            p = p->next;
        }
        p->next=NULL;
        return head;
    }


9、Gray Code

The gray code is a binary numeral system where two successive values differ in only one bit.

Given a non-negative integer n representing the total number of bits in the code, print the sequence of gray code. A gray code sequence must begin with 0.

For example, given n = 2, return [0,1,3,2]. Its gray code sequence is:

00 - 0
01 - 1
11 - 3
10 - 2

Note:
For a given n, a gray code sequence is not uniquely defined.

For example, [0,2,3,1] is also a valid gray code sequence according to the above definition.

For now, the judge is able to judge based on one instance of gray code sequence. Sorry about that.

维基百科上定义是格雷码,题意是,给一个bit的数量,然后从00000...开始,每一次只有一位不同。
这个是有数学规律的,自己一开始想到的是暴力的方法,但是这个其实可以用数学归纳出来的。

从第0个开始,第i个gray code为:(i>>1)^i(G(N) = (B(n)/2) XOR B(n),维基百科给出的公式。。。)

vector<int> grayCode(int n) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        vector<int> res;
    	int num = 1<<n;
        int i = 0;
		while(i<num)
			res.push_back((i>>1)^(i++));
		return res;
    }

10、Scramble String

 

Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty substrings recursively.

Below is one possible representation of s1 = "great":

    great
   /    \
  gr    eat
 / \    /  \
g   r  e   at
           / \
          a   t

To scramble the string, we may choose any non-leaf node and swap its two children.

For example, if we choose the node "gr" and swap its two children, it produces a scrambled string "rgeat".

    rgeat
   /    \
  rg    eat
 / \    /  \
r   g  e   at
           / \
          a   t

We say that "rgeat" is a scrambled string of "great".

Similarly, if we continue to swap the children of nodes "eat" and "at", it produces a scrambled string "rgtae".

    rgtae
   /    \
  rg    tae
 / \    /  \
r   g  ta  e
       / \
      t   a

We say that "rgtae" is a scrambled string of "great".

Given two strings s1 and s2 of the same length, determine if s2 is a scrambled string of s1.

判断一个字符串是否是另外一个字符串在树中表示反转的结果。

现在没有了思路,不知道当时是怎么想的,应该也是参考别人的,当时对动态规划不是怎么熟悉,竟然是动态规划。。。

考虑两个字符串ab组成的s,s = ab|ba。

所以s是由ab组合而成,也可以是由ba组合而成。而字符串a也可以是由a1和a2或者a2和a1拼接而成。


class Solution {
public:
/*
减少重复计算的方法就是动态规划。动态规划是一种神奇的算法技术,不亲自去写,是很难完全掌握动态规划的。

这里我使用了一个三维数组boolean result[len][len][len],其中第一维为子串的长度,第二维为s1的起始索引,第三维为s2的起始索引。
result[k][i][j]表示s1[i...i+k]是否可以由s2[j...j+k]变化得来。
*/
    bool isScramble(string s1, string s2) {
		int len = s1.length();
		if(len != s2.length()){
			return false;
		}
		if(len == 0){
			return true;
		}
		bool ***rev;
		rev = new bool**[len];
		for(int i = 0;i<len;i++){
			rev[i] = new bool*[len];
			for(int j = 0;j<len;j++){
				rev[i][j] = new bool[len];
				for(int k = 0;k<len;k++){
					rev[i][j][k] = false;
				}
			}
		}
		for(int i = 0;i<len;i++){
			for(int j = 0;j<len;j++){
				rev[0][i][j] = (s1[i]==s2[j]);
			}
		}
		for(int k = 2;k<=len;k++){//k是字符串长度
			for(int i = len-k;i>=0;i--){//第一个字符的起始位置
				for(int j = len-k;j>=0;j--){//第二个字符的起始位置
					bool r = false;
					for(int m = 1;m<k&&!r;m++){
						r = (rev[m-1][i][j]&&rev[k-m-1][i+m][j+m])||(rev[m-1][i][j+k-m]&&rev[k-m-1][i+m][j]);
					}
					rev[k-1][i][j] = r;
				}
			}
		}
		bool ret = rev[len-1][0][0];
		//销毁
		for(int i = 0;i<len;i++){
			for(int j = 0;j<len;j++){
				delete[] rev[i][j];
			}
			delete[] rev[i];
		}
		return ret;
		
		delete[] rev;
    }
};














  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值