2021-09-07剑指03-09

面试题3

题目1:

牛客网链接
思路:
1.先排序,再扫描,O(nlogn)
2.哈希表,时间O(n),空间O(n)
set1
set2
这里用集合来实现这个思路

int duplicate(vector<int> nums) {
	set<int> set;
	int repeat = -1; //输出,如果有重复,输出重复数字,否则输出-1
	for (auto num : nums) {
		if (set.count(num) == 1) { //set.count只有0或1,表示是否已插入set
			repeat = num;
			break;
		}
		else
			set.insert(num);
	}
	return repeat;
}

int main() {
	//printf("hello world");
	vector<int> a = { 2,3,1,0,2,5,3 };
	for (auto i:a){
		cout << i<<endl;
	}
	cout<<duplicate(a);
	return 0;
}

3.标答里面的,按序扫描,扫到下标i的时候,数字为m,若等于i,就扫描下一个,若不等于i,则跟第m个数字比较,相等,就是重复数字,不等就把第i个数字和第m个数字交换,相当于一次比较找到一个正确位置

int duplicate(vector<int> nums) {
	int repeat = -1;
	int len = nums.size();
	if (len <= 1) { //增加一个对长度0或者1的处理
		return repeat;
	}
	for (int i = 0; i < len; ++i) {
		while (nums[i] != i) { //这里注意可以用nums[i]赋值一个m
		//这里的while要注意,不能用if,因为一次交换以后i的位置数字不等于i,就要继续在i的位置判断
		//每次只是把m换到正确的位置上
		//这里不会无限循环,因为前提条件长度为n的数组,所有数字都在0——n-1,若没有重复,
		//则换到最后i的位置必能出现i,若有重复,必能交换出重复结果退出循环
			if (nums[i] == nums[nums[i]]) {
				repeat = nums[i];
				break;
			}
			swap(nums[i], nums[nums[i]]);
			//这里直接用了swap
		}
	}
	return repeat;
}

int main() {
	//printf("hello world");
	vector<int> a = { 2,3,1,0,2,5,3 };
	//vector<int> a = {1};
	for (auto i:a){
		cout << i<<endl;
	}
	cout<<duplicate(a);
	return 0;
}

面试题4

牛客网链接
为什么从右上角开始比较:
为了每次比较完,能缩小排查范围,要找的数比右上角小,排除最右列,比右上角大,排除第一行,左上角比较就做不到。

注意:遇到此类问题,先做个具体例子,比如4x4的矩阵,再进行分析。

    bool Find(int target, vector<vector<int> > array) {
        bool found = false;
        int rows = array.size(); //总的行数
        int cols = array[0].size(); //总的列数
        int row = 0,col = cols-1; //比较的数字位置
        if(rows>0 && cols>0){
           while(row < rows && col >= 0 ){
               if(target == array[row][col] ){
                   found = true;
                   break;
               }
               if(target > array[row][col]){
                   row += 1;
               }
               else{
                   col -= 1;
               }
           } 
        }
        return found;
    }

面试题5

牛客网链接
思路1:
顺序遍历替换,直接开辟一块新的空间,把非空格复制过来,空格换成"%20"连接字符串
时间复杂度O(n),空间O(n),如果要原地替换,就得往后移,时间复杂度为O(n^2),空间O(1)

string replaceSpace(string s) {
	string s1;
	for (auto i : s) {
		if (i != ' ') {
			s1 += i;
		}
		else {
			s1 = s1 + "%20";
			//这里,'%20'会导致We0Are0Happy
			//这个结果说明直接用+可以连接string
		}
	}

	return s1;
}

int main() {
	string s = "We Are Happy";
	int len = s.length();//长度没包括'/0',注意字符串长度是.length()
	cout << len << endl;
	cout << replaceSpace(s) << endl;
	return 0;
}

思路2:
剑指上的思路,从后往前逐个字符替换,这个思路是用字符数组的形式处理这个问题,相较而言,这个思路比较值得学习。
代码略,意义不大。

面试题6

牛客网链接
思路:直接用栈,存储里面数据,遍历完弹出即可。

vector<int> printListFromTailToHead(ListNode* head) {
	ListNode* ptr = head;
	stack<int> s;
	vector<int> v;
	while (ptr != nullptr) {
		s.push(ptr->val);
		ptr = ptr->next;
	}
	while (!s.empty()) {
		v.push_back(s.top());
		s.pop();
	}
	return v;
}

可以记一下这个建立链表节点的代码

typedef struct ListNode {
	int val;
	struct ListNode* next;
	ListNode(int x) :
		val(x), next(NULL) {
	}
};

面试题7

牛客网链接
要掌握二叉树3种遍历的6种写法,加上层序。

参考题解

class Solution {
public:
    vector<int> p, v;
    TreeNode* build(int pl, int pr, int vl, int vr){
        if(pl > pr) 
        //注意这个是pl>pr不是>=,等于的情况是叶子节点
            return nullptr;
        TreeNode* root = new TreeNode(p[pl]);
        int index=vl;//中序序列里面根节点的下标
        while (index <= vr && p[pl] != v[index]){
            ++index;
        }
        int len = index-vl;
        root->left = build(pl+1,pl+len,vl,index-1);
        //左子树在前序和中序里面的头尾下标
        root->right = build(pl+len+1,pr,index+1,vr);
        //右子树在前序和中序里面的头尾下标
        return root;
    }
    
    
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        if(pre.size() == 0 || vin.size() == 0)
            return nullptr;
        p = pre, v = vin;
        return build(0, pre.size()-1, 0, vin.size()-1);
    }
};

面试题8

牛客网链接
参考题解
思路1:
1.找根节点
2.中序建立二叉树,按遍历顺序将节点存进vector
3.扫描vector,取出下一个节点

/*
struct TreeLinkNode {
    int val;
    struct TreeLinkNode *left;
    struct TreeLinkNode *right;
    struct TreeLinkNode *next;
    TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
        
    }
};
*/
class Solution {
public:
    void In_order(TreeLinkNode* root, vector<TreeLinkNode*> &v){
        //递归中序建立二叉树,要记住
        if(!root)
            return;
        In_order(root->left, v);
        v.push_back(root);
        In_order(root->right, v);
    }
    
    TreeLinkNode* GetNext(TreeLinkNode* pNode) {
        //找根节点
        TreeLinkNode* root = pNode;
        while (root->next != nullptr){
            root=root->next;
        }
        
        vector<TreeLinkNode*> v;
        In_order(root, v);
        
        int len = v.size();
        for(int i=0; i<len; ++i){
            if(v[i]==pNode && i+1<len){
                return v[i+1];
            }
        }
        return nullptr;
    }
};

思路2:
有右子->右子下面最左的
无右子->有父节点->是父节点的左子节点->父节点
------------------------->是父节点的右子节点->向上追溯,若有节点是父节点的左子节点,则是该父节点
---------->无父节点->nullptr

class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* pNode) {
        if(pNode->right){
            pNode=pNode->right;
            while(pNode->left){
                pNode=pNode->left;
            }
            return pNode;
        }
        
        while(pNode->next){
            if(pNode == pNode->next->left){
                return pNode->next;
            }
            pNode=pNode->next;
        }
        return nullptr;
    }
};

面试题9

牛客网链接
思路:
画图1,2,3,4为例就可,push直接到s1,pop的时候,如果s2空,就把s1退栈反向存进s2,如果s2不空,就直接取s2栈顶并退栈。

class Solution
{
public:
    void push(int node) {
        stack1.push(node);
    }

    int pop() {
        if(stack2.empty()){
            while(!stack1.empty()){
                stack2.push(stack1.top());
                stack1.pop();
            }
        }
        
        int ans = stack2.top();
        stack2.pop();
        return ans;
    }

private:
    stack<int> stack1;
    stack<int> stack2;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值