可达鸭刷题记--剑指offer面试题

数组中重复的数字

思路一:

用hash表来实现。遍历数组,同时检查数组中元素是否已经在hash表中,如果在表中的话,说明元素重复,直接返回该数组元素;否则的话添加数组元素进hash表中。时间复杂度O(N),空间复杂度O(N)。

代码一:

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        unordered_map<int, int> mp;
        for(int i = 0; i<nums.size();++i){
            if(mp.find(nums[i]) != mp.end()){
                return nums[i];
            }
            else{
                mp[nums[i]]++;
            }
        }
        return -1;
    }
};

思路二:

先排序,然后遍历数组看是否有相邻的元素相等,有的话就说明数组中有重复的元素,返回重复的元素即可。遍历完数组即返回-1。时间复杂度:O(NlogN)

代码二:

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        for(int i = 1; i < nums.size();++i){
            if(nums[i-1] == nums[i]) return nums[i-1];
        }
        return -1;
    }
};

思路三:

遍历数组,扫描到下标为i 的元素m,比较 i和m,如果是相等的,说明该元素归位;不相等的话,将m和nums[m]比较,如果这两个数相等的话,则说明出现了重复的元素;不想等的话,则交换两个数的位置。重复这个比较、交换的过程,直到发现一个重复的数字。
时间复杂度O(N),空间复杂度O(1)。

PS:数组的长度为n,数组的元素取值范围是0~n-1

代码三:

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        for(int i = 0; i < nums.size();++i){
            while(i != nums[i]){
                if(nums[i] == nums[nums[i]]){
                    return nums[i];
                }
                //交换这块代码得注意
                int temp= nums[i];
                nums[i] = nums[temp];
                nums[temp] = temp;//式子的左边不能用nums[i],因为在上一个式子中nums[i]已经变了。
            }
        }
        return -1;
    }
};

二维数组中的查找


思路一:

暴力法,遍历二维数组,依次判断每一个元素是否和目标相等,是的话返回true,不是的话返回false。

代码一:省略

思路二:
利用二维数组行和列依次递增的规律,从整个矩阵的右上角开始判断。找到目标元素的话就返回true;如果矩阵中的数字小于目标值(则排除掉该列);如果矩阵中的数字大于目标值(则排除该行)。行(i)初始值为0,列(j)初始值为matrix[0].size()-1,循环执行的条件是(i <=rows && j >=0)。

代码二:

class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
        bool result = false;
        if(matrix.empty()){
            return result;
        }
        int rows = matrix.size()-1;
        int cols = matrix[0].size()-1;
        int i = 0, j = cols;
        while(i <= rows && j >= 0){
            if(target < matrix[i][j]){
                j--;
            }
            else if(target > matrix[i][j]){
                i++;
            }
            else{
                result = true;
                break;
            }
        }
        return result;
    }
};

替换空格


思路一:

声明一个结果字符串ans,遍历原来的字符串s,遇到空格的时候,在新字符串的位置用“%20”替代;否则直接把原字符串的字符复制到新的字符串即可。

代码一:

class Solution {
public:
    string replaceSpace(string s) {
        string ans;
        for(int i = 0; i < s.size();++i){
            if(s[i] == ' '){
                ans += "%20";
            }
            else{
                ans +=s[i];
            }
        }
        return ans;
    }
};

思路二:

  1. 遍历字符串,统计其中的空格数目。
  2. 重新resize原字符串的大小(一个空格用"%20"替代),一个空格会使原来的字符串多两个字符空间。
  3. 双指针,i指向字符串原来的末尾,j指向增加空间之后的末尾,当遇到空格的时候用“%20”替代,否则字符不变。循环执行的条件(j > i && i >=0)。

代码二:

class Solution {
public:
    string replaceSpace(string s) {
        int oldLength = s.size();
        int cnt = 0;
        for(int i = 0; i < oldLength;++i){
            if(s[i] == ' '){
                ++cnt;
            }
        }
        int newLength = oldLength + 2 * cnt;
        s.resize(newLength);
        for(int i = oldLength-1,j = newLength-1; j > i && i >= 0;--i){
            if(s[i] == ' '){
                s[j--] = '0';
                s[j--] = '2';
                s[j--] = '%';
            }
            else{
                s[j--] = s[i];
            }
        }
        return s;
    }
};

从尾到头打印链表


思路一:

从头到尾遍历链表,依次装入栈中。出栈,将元素push_back进ans向量,直到栈为空。

代码一:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        vector<int> ans;
        stack<int> st;
        ListNode* ptr = head;
        while(ptr){
            st.push(ptr->val);
            ptr = ptr->next;
        }
        while(!st.empty()){
            ans.push_back(st.top());
            st.pop();
        }
        return ans;
    }
};

思路二:

递归,直到链表尾的nullptr返回,返回后ans.push_back元素。最终返回ans向量。

代码二:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
    void helper(ListNode* node, vector<int>& ans){
        if(node == nullptr){
            return;
        }
        helper(node->next, ans);
        ans.push_back(node->val);
    }
public:
    vector<int> reversePrint(ListNode* head) {
        vector<int> ans;
        helper(head, ans);
        return ans;
    }
};

重建二叉树

思路:

题目意思是由二叉树的前序和中序,复原一块二叉树。根左右为(前序遍历),左右根为(中序遍历)。中序遍历是复原二叉树中的核心,遍历中序的数组,将其元素和下标存储在哈希表中,key和val依次为元素和下标的位置。从得到的前序遍历的值来new一个二叉树的节点,并将它们连接起来。PS:辅助函数的返回值是 TreeNode* 类型的。

代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
    TreeNode* helper(vector<int>& preorder, unordered_map<int, int>& mp, int ps ,int is, int ie){
        if(ps >= preorder.size() || is >= ie){
            return nullptr;
        }
        TreeNode* root = new TreeNode(preorder[ps]);
        int i = mp[preorder[ps]];
        root->left = helper(preorder,mp,ps+1,is,i);
        root->right = helper(preorder,mp,ps+1+(i-is),i+1,ie);
        return root;
    }
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        unordered_map<int , int> mp;
        for(int i = 0; i < inorder.size();++i){
            mp[inorder[i]] = i;
        }
        return helper(preorder,mp,0,0,inorder.size());
    }
};

二叉树的下一个节点


这题力扣还没有,专门跑到牛客刷的。。。

思路:

找到中序遍历的下一个节点。本题的树节点有指向父结点的指针,所以处理起来简单不少。二叉树的中序遍历是左根右。思路如下:

  1. 处理空树的情况直接返回
  2. 该节点有右子树的话,下一个节点为该右子树的最左侧节点
  3. 该节点没有右子树,其父结点不为空,该节点是父结点的左儿子直接返回父结点
  4. 否则的话一直向上重复判断,直到满足条件。

代码:

/*
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:
    TreeLinkNode* GetNext(TreeLinkNode* pNode)
    {
        if(!pNode){
            return pNode;
        }
        if(pNode->right){
            TreeLinkNode* node = pNode->right;
            while(node->left){
                node = node->left;
            }
            return node;
        }
        while(pNode->next){
            TreeLinkNode* root = pNode->next;
            if(root->left == pNode){
                return root;
            }
            pNode = pNode->next;
        }
        return nullptr;
    }
};

两个栈实现队列


思路一:

一个数据栈,一个辅助栈实现。入队操作复杂,出队简单。这样子实现,从data栈进,从data栈出。
总是将排列好的数据放在data栈,借助temp栈来辅助实现。
入队,data中的数据为空的话直接push,否则将data 中的数据倒到temp中去,然后再push,之后将temp中的数据倒回来,即实现队列的顺序。
出队,只需看data栈,为空的话返回-1;否则取data 的栈顶元素返回。PS:一定要记得pop啊

代码一:

class CQueue {
public:
    CQueue() {

    }
    
    void appendTail(int value) {
        stack<int> temp;
        while (!data.empty()){
            temp.push(data.top());
            data.pop();
        }
        data.push(value);
        while (!temp.empty()){
            data.push(temp.top());
            temp.pop();
        }
    }
    
    int deleteHead() {
        int val = -1;
        if(!data.empty()){
            val = data.top();
            data.pop();
        }
        return val;
    }
private:
    stack<int> data;
};

/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue* obj = new CQueue();
 * obj->appendTail(value);
 * int param_2 = obj->deleteHead();
 */

思路二:

双栈实现。入队操作复杂,出队简单。
这样子实现,队列从stack1进,从stack2出。
入队,直接push进stack1。
出队,如果stack1和stack2都为空的话,直接返回-1;stack2为空的话,将stack1的元素全部倒到stack2中;stack2不为空的话,返回栈顶元素,记得pop()。

代码二:

class CQueue {
public:
    CQueue() {

    }
    
    void appendTail(int value) {
        stack1.push(value);
    }
    
    int deleteHead() {
        if(stack1.empty() && stack2.empty()){
            return -1;
        }
        if(stack2.empty()){
            while(!stack1.empty()){
                int data = stack1.top();
                stack1.pop();
                stack2.push(data);
            }
        }
        int ret = stack2.top();
        stack2.pop();
        return ret;
    }
private:
    stack<int> stack1;
    stack<int> stack2;
};

/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue* obj = new CQueue();
 * obj->appendTail(value);
 * int param_2 = obj->deleteHead();
 */

PS:力扣上,方法二要快一些。

用两个队列实现一个栈


思路一:
一个数据队列data,一个辅助队列temp。从temp进,从data出。
入队复杂,出队简单。
代码一:

class MyStack {
public:
    /** Initialize your data structure here. */
    MyStack() {
        
    }
    
    /** Push element x onto stack. */
    void push(int x) {
        queue<int> temp_queue;
        temp_queue.push(x);
        while(!_data.empty()){
            temp_queue.push(_data.front());
            _data.pop();
        }
        while(!temp_queue.empty()){
            _data.push(temp_queue.front());
            temp_queue.pop();
        }
    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        int x = _data.front();
        _data.pop();
        return x;
    }
    
    /** Get the top element. */
    int top() {
        return _data.front();
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        return _data.empty();
    }
private:
    queue<int> _data;

};

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack* obj = new MyStack();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->top();
 * bool param_4 = obj->empty();
 */

思路二:

剑指offer思路。

代码二:

class MyStack {
public:
    /** Initialize your data structure here. */
    MyStack() {

    }
    
    /** Push element x onto stack. */
    void push(int x) {
        queue1.push(x);
        topval = x;//入队的时候,每次后入的一定是栈顶元素。
    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        if(queue1.empty() && queue2.empty()){
            return -1;
        }
        while(queue1.size() > 1){
            topval = queue1.front();//栈顶元素弹出之后,这里要同步更新新的栈顶元素
            queue1.pop();
            queue2.push(topval);
        }
        int topval = queue1.front();
        queue1.pop();
        swap(queue1, queue2);//交换两个队列
        return topval;
    }
    
    /** Get the top element. */
    int top() {
        return topval;
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        return queue1.empty() && queue2.empty();
    }
private:
    queue<int> queue1;
    queue<int> queue2;
    int topval;//记录栈顶元素
};

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack* obj = new MyStack();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->top();
 * bool param_4 = obj->empty();
 */

思路三:

一个队列也能实现。

代码三:

class MyStack {
public:
    queue<int> que;
    /** Initialize your data structure here. */
    MyStack() {

    }
    /** Push element x onto stack. */
    void push(int x) {
        que.push(x);
    }
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        int size = que.size();
        size--;
        while (size--) { // 将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部
            que.push(que.front());
            que.pop();
        }
        int result = que.front(); // 此时弹出的元素顺序就是栈的顺序了
        que.pop();
        return result;
    }

    /** Get the top element. */
    int top() {
        return que.back();
    }

    /** Returns whether the stack is empty. */
    bool empty() {
        return que.empty();
    }
};


作者:carlsun-2
链接:https://leetcode-cn.com/problems/implement-stack-using-queues/solution/225-yong-dui-lie-shi-xian-zhan-liang-ge-dui-lie-sh/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

矩阵中的路径


DFS + 回溯

class Solution {
public:
    int directs[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
    bool dfs(vector<vector<char>>& board, vector<vector<bool>>& visited, string& word,int x,int y,int depth){
        if(board[x][y] != word[depth]){
            return false;
        }
        if(depth == word.size()-1){
            return true;
        }
        visited[x][y] = true;
        for(int i = 0; i < 4;++i){
            int nextX = x + directs[i][0];
            int nextY = y + directs[i][1];
            if(nextX >=0 && nextX < board.size() && nextY >= 0 && nextY < board[0].size() && !visited[nextX][nextY]){
                if(dfs(board,visited,word,nextX,nextY,depth+1)){
                    //printf("a\n");
                    return true;
                }
            }
         }
         visited[x][y] = false;
         return false;
    }
    bool exist(vector<vector<char>>& board, string word) {
        if(word == ""){
            return false;
        }
        int rows = board.size();
        int cols = board[0].size();
        vector<vector<bool>> visited(rows,vector<bool>(cols,false));


        for(int i = 0; i < rows;++i){
            for(int j = 0; j < cols;++j){
                if(dfs(board,visited,word,i,j,0)){
                    return true;
                }
            }
        }
        return false;
    }
};

你可能感兴趣的:(剑指offer)