剑指 offer 67题

文章目录

《剑指 offer》

剑指offer 001、二维数组中的查找

题目

image-20210908085618798

题解1

先观察,从右上角逐渐左下角逼近

  • 当前位置元素比target小,则 row++
  • 当前位置元素比target大,则col–
  • 若相等,返回true
  • 若越界了还未找到,说明该数组不存在与target相等元素,返回false
class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
        if (array.empty() || array[0].empty()) {
            return false;
        }
        
        int row = array[0].size() - 1, col = array.size() - 1;
        int x = 0, y = row;
        
        while (x <= col && y >= 0) {
            if (array[x][y] < target) {
                x++;
            }
            else if (array[x][y] > target) {
                y--;
            }
            else {
                return true;
            }
        }
        
        return false;
    }
};

题解2

用二分查找

class Solution {
public:
    bool binaryFind(vector<int> &arr, int target) {
        int low = 0, high = arr.size() - 1;
        while (low <= high) {
            int mid = low + (high - low) / 2;
            if (arr[mid] < target) 
                low = mid + 1;
            else if (arr[mid] > target) 
                high = mid - 1;
            else 
                return true;
        }
        
        return false;
    }
    
    bool Find(int target, vector<vector<int> > array) {
        if (array.empty() || array[0].empty()) {
            return false;
        }
        
        int row = array.size() - 1;
        for (int x = 0; x <= row; x++) {
            if (binaryFind(array[x], target)) {
                return true;
            }
        }
        return false;
    }
};

剑指offer 002、替换空格

题目

image-20210908085924085

题解1

class Solution {
public:
    string replaceSpace(string s) {
        int spaceCount = 0;	// 空格的数量
        int length = s.size() - 1;
        
        for (int i = 0; i <= length; ++i) {
            if (s[i] == ' ') spaceCount++;
        }
        
        int newLength = length + spaceCount * 2;	// 新字符串的长度
        s.resize(newLength + 1);
        
        for (int i = length; i >= 0; i--) {
            if (s[i] != ' ') {
                s[newLength--] = s[i];
            }
            else {
                s[newLength--] = '0';
                s[newLength--] = '2';
                s[newLength--] = '%';
            }
        }
        
        return s;
    }
};

剑指offer 003、从尾到头打印链表

题目

image-20210908090110042

题解1

直接用C++的reverse()函数 或 反向迭代器

/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        if (!head) return vector<int>();
        
        vector<int> result;
        while (head) {
            result.push_back(head->val);
            head = head->next;
        }
        
        /*
        reverse(result.begin(), result.end());
        return result;
        */
        return vector<int>(result.rbegin(), result.rend());
    }
};

剑指offer 004、重建二叉树

题目

image-20210908090417991

题解1

首先要熟练掌握二叉树先序遍历和中序遍历的原理。

先找pre中的其实元素为根节点,在vin中找到根节点的索引mid;那么pre中[1, mid + 1]为左子树,[mid + 1, end] 为右子树;vin中[0, mid]为左子树,[mid + 1, end] 为右子树

/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        if (pre.empty() || vin.empty()) {
            return NULL;
        }
        
        TreeNode* newTree = new TreeNode(pre[0]);
        int mid = distance(vin.begin(), find(vin.begin(), vin.end(), pre[0]));
        
        vector<int> left_vin(vin.begin(), vin.begin() + mid);
        vector<int> right_vin(vin.begin() + mid + 1, vin.end());
        
        vector<int> left_pre(pre.begin() + 1, pre.begin() + mid + 1);
        vector<int> right_pre(pre.begin() + mid + 1, pre.end());
        
        newTree->left = reConstructBinaryTree(left_pre, left_vin);
        newTree->right = reConstructBinaryTree(right_pre, right_vin);
        
        return newTree;
    }
};

剑指offer 005、用两个栈实现队列

题目

image-20210909180943849

题解

入队:将元素进栈stack1

出队:判断栈stack2是否为空,如果为空,则将栈stack1中所有元素pop,并push进栈stack2,最后栈stack2出栈

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

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

private:
    stack<int> stack1;
    stack<int> stack2;
};

剑指offer 006、旋转数组的最小数字

题目

image-20210909181323857

题解

直接使用二分法

如有疑惑,可以看看这个解析:(牛友FINACK)

image-20210909183001638

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        if (rotateArray.empty()) return 0;
        
        int low = 0, high = rotateArray.size() - 1;
        while (low < high) {
            int mid = low + (high - low) / 2;
            if (rotateArray[mid] < rotateArray[high]) {
                high = mid;
            }
            else if (rotateArray[mid] == rotateArray[high]) {
                high--;
            }
            else {
                low = mid + 1;
            }
        }
        return rotateArray[low];
    }
};

剑指offer 007、斐波那契数列

题目

image-20210909183109433

题解

注意,本题是从第0项开始,即数列项依次为:0 1 1 2 3 5 8…

class Solution {
public:
    int Fibonacci(int n) {
        if (n == 0) {
            return 0;
        }
        if (n == 1) {
            return 1;
        }
        
        int num1 = 0, num2 = 1;
        for (int i = 2; i <= n; i++) {
            num2 += num1;
            num1 = num2 - num1;
        }
        return num2;
    }
};

剑指offer 008、跳台阶

题目

image-20210909184254645

题解

用动态规划的来解决这道题,跳到第n个台阶只有两种可能

  • 从第n - 1个台阶跳1个台阶
  • 从第n - 2个台阶跳2个台阶

因此我们只需求出跳到第n - 1个台阶和第n - 2个台阶的可能跳法。

递推公式:f(n) = f(n - 1) + f(n - 2);
f(0) = 1, f(1) = 1;

class Solution {
public:
    int jumpFloor(int number) {
        if (number == 1) {
            return 1;
        }
        
        int num0 = 1, num1 = 1;
        for (int i = 2; i <= number; ++i) {
            num1 += num0;
            num0 = num1 - num0;
        }
        return num1;
    }
};

剑指offer 009、跳台阶扩展问题

题目

image-20210909192658882

题解

很简单,跳到第n个台阶时有f(n) = f(n - 1) + f(n - 2) + … + f(1) = 2 * f(n - 1) 种方法。

// 递归的方法
class Solution {
public:
    int jumpFloorII(int number) {
        if (number == 1) {
            return 1;
        }
        
        return 2 * jumpFloorII(number - 1);
    }
};
// 非递归
class Solution {
public:
    int jumpFloorII(int number) {
        if (number == 1) {
            return 1;
        }
        
        int count = 1;
        for (int i = 2; i <= number; ++i) {
            count *= 2;
        }
        return count;
    }
};
// 直接用pow函数
class Solution {
public:
    int jumpFloorII(int number) {
        if (number == 1) {
            return 1;
        }
        
        return pow(2, number - 1);
    }
};

剑指offer 010、矩形覆盖

题目

image-20210912100401319

image-20210912100422275

题解

emmm太笨了,刚开始没读懂题,找了好半天的题解才明白题目是什么意思。

这道题的意思是我们要用 n 个 2*1的小矩阵 横着 / 竖着 去覆盖更大的矩形
image-20210912104330320

规定:image-20210912104951577

那么,

  • n = 1 时
    • 只能横着覆盖,一种
  • n = 2 时
    • 横着覆盖,一种
    • 竖着覆盖,一种
    • 总共有2种
  • n = 3 时
    • 第三级横着覆盖,用了两块,剩下n = 1,有一种覆盖方法
    • 第三级竖着覆盖,用了一块,剩下n = 2,有两种覆盖方法
    • 总共有3种
  • n = 4 时
    • 第四级横着覆盖,用了两块,剩下n = 2,有两种覆盖方法
    • 第四级竖着覆盖,用了一块,剩下n = 3,有三种覆盖方法
    • 总共有5种
  • … …
  • n = n时
    • 第n级横着覆盖,用了两块,剩下n = n - 2,关注n = n - 2时有多少种覆盖方法
    • 第n级竖着覆盖,用了一块,剩下n = n - 1,关注n = n - 1时有多少种覆盖方法
    • 总共有n = n - 2 ,n = n - 1 两种情况之和
class Solution {
public:
    int rectCover(int number) {
        if (number <= 2) {
            return number;
        }
        
        int pre1 = 2;    // n = n - 1时的覆盖方法
        int pre2 = 1;    // n = n - 2时的覆盖方法
        int cur = 0;    // 当前数量的覆盖方法
        for (int i = 3; i <= number; ++i) {
            cur = pre1 + pre2;
            pre2 = pre1;
            pre1 = cur;
        }
        return cur;
    }
};

上面是比较清晰的写法,为了少几个变量,也可以写成这样

class Solution {
public:
    int rectCover(int number) {
        if (number <= 2) {
            return number;
        }
        
        int pre1 = 2, pre2 = 1; 
        for (int i = 3; i <= number; ++i) {
            pre1 += pre2;
            pre2 = pre1 - pre2;
        }
        return pre1;
    }
};

还可以采用递归的方法去做

class Solution {
public:
    int rectCover(int number) {
        if (number <= 2) {
            return number;
        }
       
        return rectCover(number - 1) + rectCover(number - 2);
    }
};

剑指offer 011、二进制中 1 的个数

题目

image-20210912110707527

题解

好吧,这道题俺不会做,这是自己写的错误解法:

// !!!!!!错误解法!!!!!!!
class Solution {
public:
     int  NumberOf1(int n) {
         if (n == 0) {
             return 0;
         }
         
         int binaryCount = 1;
         if (n < 0) {
             binaryCount = 2;
         }
         
         while (n / 2) {
             if (n % 2) {
                 binaryCount++;
             }
             n /= 2;
         }
         return binaryCount;
     }
};

image-20210912114809843

正确解法

(来自牛客大佬 @菩提旭光)

如果一个整数不为0,那么这个整数至少有一位是1。

如果把这个整数减1,原来处在整数最右边的1就会变为0,原来在1后面的所有0变成1(如果最右边的1后面还有0的话)。其余的位不会受到影响

就这样,把一个整数减去1,再和原整数做 与运算,会把该整数最右边的一个1变成0。该整数的二进制有多少个1,就可以进行多少次这样的操作。

class Solution {
public:
     int  NumberOf1(int n) {
         int count = 0;
         while (n != 0) {
             count++;
             n = n & (n - 1);
         }
         return count;
     }
};

剑指offer 012、数值的整数次方

题目

image-20210912131031737

题解

自己第一次做的,下面开始优化

class Solution {
public:
    double Power(double base, int exponent) {
        double result = 1;
        if (exponent == 0) {
            return 1;
        }
        else if (exponent > 0) {
            for (int i = 1; i <= exponent; ++i) {
                result *= base;
            }
        }
        else {
            exponent *= -1;
            for (int i = 1; i <= exponent; ++i) {
                result *= base;
            }
            result = 1 / result;
        }
        
        return result;
    }
};
class Solution {
public:
    double Power(double base, int exponent) {
        if (exponent == 0) { return 1.0; }
        if (base == 0) { return 0.0; }
        
        bool flag = false;
        if (exponent < 0) {
            flag = true;
            exponent *= -1;    // 指数变正
        }
        
        double result = base;
        for (int i = 2; i <= exponent; ++i) {
            result *= base;
        }
        
        if (flag) {
            return 1.0 / result;
        }
        else {
            return result;
        }
    }
};

剑指offer 013、调整数组顺序使奇数位于偶数前面

题目

image-20210912134747023

题解

暴力解法:开辟两个新数组

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param array int整型vector 
     * @return int整型vector
     */
    vector<int> reOrderArray(vector<int>& array) {
        // write code here
        vector<int> odd, even;
        for (auto x : array) {
            if (x % 2) {
                odd.push_back(x);
            }
            else {
                even.push_back(x);
            }
        }
        
        array.clear();
        for (auto x : odd) {
            array.push_back(x);
        }
        for (auto x : even) {
            array.push_back(x);
        }
        
        return array;
    }
};

暴力解法开辟一个新数组

class Solution {
public:
    vector<int> reOrderArray(vector<int>& array) {
        
        int length = array.size();
        vector<int> temp(length, 0);
        
        int low = 0;
        // 特别注意 按位与& 的优先级低于 ==
        for (int i = 0; i < length; ++i) {
            if ((array[i] & 1) == 1) {
                temp[low++] = array[i];
            }
        }
        
        for (int i = 0; i < length; ++i) {
            if ((array[i] & 1) == 0) {
                temp[low++] = array[i];
            }
        }
        
        array.assign(temp.begin(), temp.end());
        
        return array;
    }
};

时间和空间都是O(n)的做法

class Solution {
public:
    vector<int> reOrderArray(vector<int>& array) {
        vector<int> temp;
        int oddIndex = 0;
        
        for (auto x : array) {
            if ((x & 1) == 1) {
                array[oddIndex++] = x;
            }
            else {
                temp.push_back(x);
            }
        }
        
        for (int i = 0; i < temp.size(); ++i) {
            array[oddIndex + i] = temp[i];
        }
        
        return array;
    }
};
class Solution {
public:
    vector<int> reOrderArray(vector<int>& array) {
        vector<int> temp(array.size(), 0);	// 这里
        int oddIndex = 0, evenIndex = 0;	// 这里
        
        for (auto x : array) {
            if ((x & 1) == 1) {
                array[oddIndex++] = x;
            }
            else {
                temp[evenIndex++] = x;	// 这里
            }
        }
        
        for (int i = 0; i < evenIndex; ++i) {	// 这里
            array[oddIndex + i] = temp[i];
        }
        
        return array;
    }
};

剑指offer 014、链表中倒数第k个结点

题目

image-20210912150933199

题解

自己的解法

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param pHead ListNode类 
     * @param k int整型 
     * @return ListNode类
     */
    ListNode* FindKthToTail(ListNode* pHead, int k) {
        // write code here
        int length = 0;
        ListNode* newNode = pHead;
        
        while (newNode) {
            length++;
            newNode = newNode->next;
        }
        
        if (length < k) {
            return newNode;
        }
        
        for (int i = 1; i <= length - k; ++i) {
            pHead = pHead->next;
        }
        return pHead;
        
    }
};

快慢指针

画个图,模拟一下很清楚

class Solution {
public:
    ListNode* FindKthToTail(ListNode* pHead, int k) {
        ListNode* slowNode = pHead;
        while (k--) {
            if (pHead) {    // 判断链表长度是否大于k
                pHead = pHead->next;
            }
            else {        // 链表长度小于k
                return nullptr;
            }
        }
        
        while(pHead) {
            slowNode = slowNode->next;
            pHead = pHead->next;
        }
        return slowNode;
    }
};

剑指offer 015、反转链表

题目

image-20210912161827368

题解

自己做的,太太太太low了,虽然思路是对的,也是头插法,但是写的太繁琐了。还是看下面的头插法吧

class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        if (pHead == nullptr) return nullptr;
        if (pHead->next == nullptr) return pHead;
        ListNode* node = pHead->next;
        if (node->next == nullptr) {
            node->next = pHead;
            pHead->next = nullptr;
            return node;
        }
        
        pHead->next = nullptr;
        ListNode* preNode = pHead;
        ListNode* nextNode = node->next;
        while (nextNode) {
            node->next = preNode;
            preNode = node;
            node = nextNode;
            nextNode = nextNode->next;
        }
        node->next = preNode;
        
        return node;
    }
};

头插法

class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        ListNode* pre = nullptr;
        ListNode* cur = pHead;
        ListNode* nex = nullptr;
        
        while (cur) {
            nex = cur->next;
            cur->next = pre;
            pre = cur;
            cur = nex;
        }
        return pre;
    }
};

剑指offer 016、合并两个排序的链表

题目

image-20210912174343209

题解

迭代去做

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
        if(pHead1 == nullptr) return pHead2;
        if(pHead2 == nullptr) return pHead1;
        
        ListNode* newHead = (ListNode*)malloc(sizeof(struct ListNode));
        ListNode* node = newHead;
        
        while (pHead1 && pHead2) {
            if (pHead1->val < pHead2->val) {
                node->next = pHead1;
                pHead1 = pHead1->next;
            }
            else {
                node->next = pHead2;
                pHead2 = pHead2->next;
            }
            node = node->next;
        }
        
        node->next = (pHead1 ? pHead1 : pHead2);
        return newHead->next;
    }
};

递归版本

class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
        if (pHead1 == nullptr) return pHead2;
        if (pHead2 == nullptr) return pHead1;
        
        if (pHead1->val < pHead2->val) {
            pHead1->next = Merge(pHead1->next, pHead2);
            return pHead1;
        }
        else {
            pHead2->next = Merge(pHead1, pHead2->next);
            return pHead2;
        }
    }
};

剑指offer 017、树的子结构

题目

image-20210912211334815

题解

不会做。。参考大佬的:@Krahets 提供的思路写的

image-20210912221227515

思路:

若树 B 是树 A 的子结构,则子结构的根节点可能为树 A 的任意一个节点。因此,判断树 B 是否是树 A 的子结构,需完成以下两步工作:

  1. 先序遍历树 A 中的每个节点 n_A(对应函数 isSubStructure(A, B))
  2. 判断树 A 中 以 n_A 为根节点的子树 是否包含树 B (对应函数 recur(A, B))
/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    bool HasSubtreeCore(TreeNode* pRoot1, TreeNode* pRoot2) {
        if (pRoot2 == nullptr) return true;	// 说明树pRoot2已匹配完成(越过叶子节点),匹配成功
        if (pRoot1 == nullptr) return false;	// 说明已经越过树pRoot1叶子节点,匹配失败
        
        if (pRoot1->val != pRoot2->val) {
            return false;	// 当两结点的值不同时,说明匹配失败
        }
        else {	// 分别判断pRoot1和pRoot2的左右子结点是否相等
            return HasSubtreeCore(pRoot1->left, pRoot2->left) &&
                HasSubtreeCore(pRoot1->right, pRoot2->right);
        }
        
    }
    
    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2) {
        if (pRoot1 == nullptr || pRoot2 == nullptr) return false;
        // 若pRoot2是pRoot1的子结构,则必须满足下面三种情况之一:以结点pRoot1为根的子树包括树pRoot2 / 树pRoot2是树pRoot1的左子树的子结构 / 树pRoot2是树pRoot1的右子树的子结构
        return HasSubtreeCore(pRoot1, pRoot2) ||
            HasSubtree(pRoot1->left, pRoot2) || 
            HasSubtree(pRoot1->right, pRoot2); 
            

    }
};

剑指offer 018、二叉树的镜像

题目

image-20210912222227584

题解

观察两幅图,镜像即为颠倒原二叉树的左右位置(左右对称),可用递归的方法来解决

class Solution {
public:
    TreeNode* Mirror(TreeNode* pRoot) {
        if (pRoot == nullptr) return nullptr;
        
        swap(pRoot->left, pRoot->right);
        Mirror(pRoot->left);
        Mirror(pRoot->right);
        return pRoot;
    }
};

借助队列,迭代的方法来做

class Solution {
public:
    TreeNode* Mirror(TreeNode* pRoot) {
        if (pRoot == nullptr) return nullptr;
        
        // 类似于二叉树的层次遍历
        queue<TreeNode*> q;
        TreeNode* node = nullptr;
        q.push(pRoot);
        while (!q.empty()) {
            node = q.front();
            q.pop();
            if (node) {
                q.push(node->left);
                q.push(node->right);
                swap(node->left, node->right);
            }
        }
        return pRoot;
    }
};

剑指offer 019、顺时针打印矩阵

题目

image-20210913125649569

题解

自己做时思路都是对的,但是在边界的控制不到位,以至于旋转到矩阵最里层出现部分数字未打印。下面参考了一下大佬的处理方法才做出来

对于后面两个for循环,要加入条件判断,防止出现单行或单列的情况

class Solution {
public:
    vector<int> printMatrix(vector<vector<int> > matrix) {
        int row = matrix.size();
        int col = matrix[0].size();
        vector<int> res;
        
        // 输入的二维数组非法,返回空数组
        if (!row || !col) return res;
        
        // 四个变量用来限制上下左右打印范围
        int left = 0, right = col - 1;
        int top = 0, bottom = row - 1;
        
        while (left <= right && top <= bottom) {
            // left to right
            for (int i = left; i <= right; ++i) {
                res.push_back(matrix[top][i]);
            }
            // top to bottom
            for (int i = top + 1; i <= bottom; ++i) {
                res.push_back(matrix[i][right]);
            }
            // 还要加两个限制条件:top < bottom 和 left < right
            // right to left
            for (int i = right - 1; i >= left && top < bottom; --i) {
                res.push_back(matrix[bottom][i]);
            }
            // bottom to top
            for (int i = bottom - 1; i > top && left < right; --i) {    // 注意这里是 i > top
                res.push_back(matrix[i][left]);
            }
            left++, right--;
            top++, bottom--;
        }
        return res;
    }
};

剑指offer 020、包含min的栈

题目

image-20210913154821642

题解

自己首刷

这里主栈和辅助栈一直同步

class Solution {
public:
    stack<int> dataStack, minStack;
    
    void push(int value) {
        if (minStack.empty()) {
            minStack.push(value);
        }
        else if (value > minStack.top()) {
            minStack.push(minStack.top());
        }
        else {
            minStack.push(value);
        }
        dataStack.push(value);
    }
    
    void pop() {
        dataStack.pop();
        minStack.pop();
    }
    
    int top() {
        return dataStack.top();
    }
    
    int min() {
        return minStack.top();
    }
};

当然也可以这样:

压的时候,

  • 如果主栈的压入元素 > 辅助栈栈顶,辅助栈不压
  • 如果主栈的压入元素 ≤ 辅助栈栈顶,两栈同时压入

出栈,

  • 如果两栈栈顶元素不等,主栈出,辅助栈不出
  • 相等,一起出
class Solution {
public:
    stack<int> dataStack, minStack;
    
    void push(int value) {
        if(minStack.empty()) {
            minStack.push(value);
        }
        else if(value <= minStack.top()) {
            minStack.push(value);
        }
        dataStack.push(value);
    }
    
    void pop() {
        if(dataStack.top() == minStack.top())
            minStack.pop();
        dataStack.pop();
    }
    
    int top() {
        return dataStack.top();        
    }
    
    int min() {
        return minStack.top();
    } 
    
};

剑指offer 021、栈的压入、弹出序列

题目

image-20210913160439286

题解

emmmm,这道题刚拿出来就一脸懵逼,丝毫没有思路,在看了牛客大佬@Alisa提供的思路后才模仿写了出来

思路:

借用一个辅助栈,将压栈顺序的元素逐个放入辅助栈中,每放一个元素就进行判断:判断栈顶元素是否 等于 出栈序列的第一个元素(同时还要判断辅助栈是否为空)

  • 如果 等于,辅助栈中该元素出栈,并且将出栈序列索引指向下一个
  • 如果 不等,继续前行(将压栈顺序下一个元素放入辅助栈中)
class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        if (pushV.empty()) return false;
        
        stack<int> tempStack;
        int popIndex = 0;	// 弹出序列数组popV的索引
        for (int i = 0; i < pushV.size(); ++i) {
            tempStack.push(pushV[i]);
            // 如果辅助栈不为空且栈顶元素等于弹出序列
            while (!tempStack.empty() && tempStack.top() == popV[popIndex]) {
                // 出栈
                tempStack.pop();
                // 弹出序列后移一位
                popIndex++;
            }
        }
        
        return tempStack.empty();
    }
};

剑指offer 022、从上往下打印二叉树

题目

image-20210914142008612

题解

借助队列

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {
        if (root == nullptr) return vector<int>();
        
        vector<int> res;
        TreeNode* node = nullptr;
        queue<TreeNode*> q;
        q.push(root);
        while (!q.empty()) {
            node = q.front();
            q.pop();
            res.push_back(node->val);
            
            if (node->left) {
                q.push(node->left);
            }
            if (node->right) {
                q.push(node->right);
            }
        }
        return res;
    }
};

剑指offer 023、二叉搜索树的后序遍历序列

题目

image-20210914132548693

题解

对于一个序列,最后一个元素是根root,去掉最后一个元素的序列可以分为两部分,前一段即左子树小于root,后一段即右子树大于root,并且这两段子树都是合法的后序序列,因此可以采用递归的方法

class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) {
        if (sequence.empty()) return false;
        if (sequence.size() == 1) return true;
        return verify(sequence, 0, sequence.size() - 1);
    }
    
    bool verify(vector<int>& sequence, int left, int right) {
        if (left >= right) return true;    // 一定是 >=
        
        int i = right;
        while (i > left && sequence[--i] > sequence[right]);
        for (int j = i - 1; j >= left; --j) {
            if (sequence[j] > sequence[right]) {
                return false;
            }
        }
        return verify(sequence, left, i) && verify(sequence, i + 1, right - 1);
    }
};

还可以用非递归的方法去解决,感觉用非递归去做思路更清晰

class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) {
        if(sequence.empty()) return false;
        
        int length = sequence.size();
        int i = 0;
        while (--length) {
            while (sequence[i++] < sequence[length]);
            while (sequence[i++] > sequence[length]);
            
            if (i <= length) return false;
            i = 0;
        }
        return true;
    }
};

剑指offer 024、二叉树中和为某一值的路径

题目

image-20210914144501079

题解

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    vector<vector<int>> result;
    vector<int> temp;
    
    vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
        
        if (root == nullptr) return result;
        
        temp.push_back(root->val);
        expectNumber -= root->val;
        if (expectNumber == 0 && !root->left && !root->right) {
            result.push_back(temp);
        }
        FindPath(root->left, expectNumber);
        FindPath(root->right, expectNumber);
        // 移除最后一个元素,深度遍历完一条路径需要回退
        temp.pop_back();
        
        return result;
    }
};

剑指offer 025、复杂链表的复制

题目

image-20210915213459911

image-20210915213515329

题解

参考牛客大佬@chancy

分为三个步骤:

image-20210915221102997

/*
struct RandomListNode {
    int label;
    struct RandomListNode *next, *random;
    RandomListNode(int x) :
            label(x), next(NULL), random(NULL) {
    }
};
*/
class Solution {
public:
    RandomListNode* Clone(RandomListNode* pHead) {
        if (!pHead) return nullptr;
        
        RandomListNode* curNode = pHead;
        // 复制每个结点并插入到原结点后面
        while (curNode) {
            RandomListNode* nextNode = curNode->next;
            RandomListNode* cloneNode = (RandomListNode*)malloc(sizeof(RandomListNode));
            cloneNode->label = curNode->label;
            cloneNode->next = nextNode;
            curNode->next = cloneNode;
            curNode = nextNode;
        }
        
        curNode = pHead;
        // 重新遍历链表,复制原结点的随机指针给新结点
        while (curNode) {
            curNode->next->random = (curNode->random == nullptr ? nullptr : curNode->random->next);
            curNode = curNode->next->next;
        }
        
        // 拆分链表
        curNode = pHead;
        RandomListNode* pCloneHead = pHead->next;
        while (curNode) {
            RandomListNode* cloneNode = curNode->next;
            curNode->next = cloneNode->next;
            cloneNode->next = (cloneNode->next == nullptr ? nullptr : cloneNode->next->next);
            curNode = curNode->next;
        }
        
        return pCloneHead;
    }
};

剑指offer 026、二叉搜索树与双向链表

题目

image-20210915221243128

image-20210915221303291

题解

解法1

第一种解法是违背题目要求(不得创建新的结点)的做法:

将二叉搜索进行中序遍历即可得到由小到大的顺序排列,通过中序遍历将二叉树元素存储下来,然后将数组中的结点逐个连接。

class Solution {
public:
    vector<TreeNode*> treeList;    // 用来存储结点的数组
    void convertSort(TreeNode*& root) {
        if (!root) return;
        
        convertSort(root->left);
            treeList.push_back(root);    // 存储
        convertSort(root->right);
    } 
    TreeNode* Convert(TreeNode* pRootOfTree) {
        if (!pRootOfTree) return pRootOfTree;
        
        convertSort(pRootOfTree);
        // 将数组中的结点逐个连接
        for (int i = 1; i <= treeList.size() - 1; ++i) {
            treeList[i - 1]->right = treeList[i];
            treeList[i]->left = treeList[i - 1];
        }
        return treeList[0];
    }
};

解法2

还是通过中序遍历,用preNode指向当前结点的前继,preNode->right == root

class Solution {
public:
    TreeNode* preNode = nullptr;	// 一定是全局变量
    TreeNode* Convert(TreeNode* root) {
        if (!root) return root;
        
        TreeNode* Head = root;
        // 找到双向链表的开头
        while (Head->left) Head = Head->left;
        converAdjust(root);
        return Head;
    }
    void converAdjust(TreeNode* root) {
        if (!root) return;
        
        converAdjust(root->left);
        root->left = preNode;
        if (preNode) {
            preNode->right = root;
        }
        preNode = root;	// 更新preNode,指向当前结点
        converAdjust(root->right);
    }
};

剑指offer 027、字符串的排列

题目

image-20210916144117096

题解

DFS + 回溯

还不会做,看的秀哥的做法。会了继续补充

class Solution {
public:
    vector<string> result;
    
    void permutationAdjust(string &s,int begin,int end) {
        if(begin == end){
            result.push_back(s);
            return ;
        }
        unordered_map<int,int> visited;
        for(int i = begin; i<= end; ++i){
            if(visited[s[i]] == 1) continue;
            swap(s[i],s[begin]);
            permutationAdjust(s,begin+1,end);
            swap(s[i],s[begin]);
            visited[s[i]] =1;
        }
    }
    vector<string> Permutation(string str) {
        if (str.size() == 0) return vector<string>();
        
        permutationAdjust(str,0,str.size()-1);
        sort(result.begin(), result.end());
        return result;
    }
};

剑指offer 028、数组中出现次数超过一半的数字

题目

image-20210916172427194

题解

常规做法,借助哈希表

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        unordered_map<int, int> count;
        for (auto x : numbers) {
            ++count[x];
            // 这里是 > 号,考虑[2, 2, 2, 3, 3, 3, 3]
            if (count[x] > numbers.size() / 2) return x;
        }
        return 0;
    }
};

剑指offer 029、最小的K个数

题目

image-20210916173532228

题解

第一种方法不满足题意,时间复杂度为O(n),看第二种方法

解法1

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        if (k <= 0 || k > input.size()) return vector<int>();
        
        vector<int> result;
        sort(input.begin(), input.end());
        for (int i = 0; i < k; ++i) {
            result.push_back(input[i]);
        }
        return result;
    }
};

解法2

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        if (k <= 0 || k > input.size()) return vector<int>();
        
        vector<int> result;
        // 借助优先队列(最小堆)
        priority_queue<int, vector<int>, greater<int>> q;
        for (auto x : input) {
            q.push(x);
        }
        while (k--) {
            result.push_back(q.top());
            q.pop();
        }
        
        return result;
    }
};

剑指offer 030、连续子数组的最大和

题目

image-20210916180026593

题解

显然这是一道动态规划的题目

先看第一种解法

class Solution {
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
        if (array.size() == 0) return 0;
        
        int maxNum = array[0];
        for (int i = 1; i < array.size(); ++i) {
            array[i] = max(array[i], array[i - 1] + array[i]);
            maxNum = max(maxNum, array[i]);
        }
        return maxNum;
    }
};

由于有些题目要求不能修改原数组,而dp列表中dp[i] 只和 dp[i - 1]有关,所以创建两个变量循环利用即可。

class Solution {
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
        if (array.size() == 0) return 0;
        
        int max = array[0];
        int pre = 0;    // 记录dp[i - 1]的值
        int cur = array[0];    // 记录dp[i]的值
        
        for (auto x : array) {
            cur = x;
            if (pre > 0) cur += pre;
            if (cur > max) max = cur;
            pre = cur;
        }
        return max;
    }
};

剑指offer 031、整数中1出现的次数(从1到n整数中1出现的次数)

题目

image-20210917145455196

题解

太难了[/(ㄒoㄒ)/~~],真伤脑

copy @superkakayong 大佬的解法:

image-20210917151449976

image-20210917151504507

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n) {
        long digit = 1;    // 若n是INT_MAX digit会在最后一次循环越界
        int high = n / 10, cur = n % 10, low = 0;
        int res = 0;
        
        while (high != 0 || cur != 0) {
            if (cur == 0) {
                res += high * digit;
            }
            else if (cur == 1) {
                res += high * digit + low + 1;
            }
            else {
                res += (high + 1) * digit;
            }
            
            low += cur * digit;
            cur = high % 10;
            high /= 10;
            digit *= 10;
        }
        
        return res;
    }
};

剑指offer 032、把数组排成最小的数

题目

image-20210917155807628

题解

思路:

求拼接起来的最小数字,本质上是个排序问题。

设数组numbers中两数字的字符串为 x 和 y,则排序规则为:

  • 若 x + y < y + x,则 排序完数组中 x 应在 y 左侧
  • 若 x + y > y + x,则 排序完数组中 x 应在 y 右侧
class Solution {
public:
    // 写一个仿函数
    bool operator()(string x, string y) {
        return x + y < y + x;
    }
    string PrintMinNumber(vector<int> numbers) {
        vector<string> results;
        string res;
        for (auto x : numbers) {
            string temp = to_string(x);
            results.push_back(temp);
        }
        sort(results.begin(), results.end(), Solution());
        
        for (auto x : results) {
            res += x;
        }
        return res;
    }
};

剑指offer 033、丑数

题目

image-20210917163831939

题解

学习牛客大佬@事无巨细,悉究本末 的解法,

对于一个丑数,其因子只能为2,3,5,那么丑数 p = 2 ^ x * 3 ^ y * 5 ^ z,因此我们可以维护三个队列(乘以2的、乘以3的、乘以5的),选择队列头最小的数字加入丑数列中。

class Solution {
public:
    int GetUglyNumber_Solution(int index) {
        // 0- 6的丑数分别是0 - 6
        if (index < 7) return index;
        
        // i2, i3, i5分别为三个队列的指针,newNum为从队列头选出的最小值
        int i2 = 0, i3 = 0, i5 = 0;
        int newNum = 1;
        vector<int> array;
        array.push_back(newNum);
        
        while (array.size() < index) {
            // 求出三个队列头中最小的数
            newNum = min(array[i2] * 2, min(array[i3] * 3, array[i5] * 5));
            // 判断选出的newNum到底是哪个队列头,更新对应的队列头指针(可能存在多个的情况,所以用三条if语句)
            if (array[i2] * 2 == newNum) ++i2;
            if (array[i3] * 3 == newNum) ++i3;
            if (array[i5] * 5 == newNum) ++i5;
            array.push_back(newNum);
        }
        return newNum;
    }
};

剑指offer 034、第一个只出现一次的字符

题目

image-20210918142303369

题解

class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        int res = 0;    // 记录返回值
        unordered_map<char, int> array;    // 无序哈希表记录每个字符出现的次数
        
        for (char x : str) ++array[x];
        for (char x : str) {
            if (array[x] == 1) return res;
            ++res;
        }
        return -1;
    }
};

剑指offer 035、数组中的逆序对

题目

image-20210918143637162

题解

学习牛客大佬@rs勿忘初心 的解法

image-20210918213102972

image-20210918213119424

class Solution {
public:
    int InversePairs(vector<int> data) {
        if (data.empty()) return 0;
        vector<int> copy(data);    // 辅助数组,每次递归后有序
        long long count = InversePairsAdjust(data, copy, 0, data.size() - 1);
        return count % 1000000007;
    }
    long long InversePairsAdjust(vector<int> &data,vector<int> &copy,int begin,int end) {
        if (begin == end) return 0;
        int mid = begin + (end - begin) / 2;
        long long left = InversePairsAdjust(copy, data, begin, mid);
        long long right = InversePairsAdjust(copy, data, mid + 1, end);
        
        int last_left = mid;    // 左侧的尾端
        int last_right = end;    // 右侧的尾端
        int index_copy = end;    // 比较结果存入辅助数组尾部
        long long res = 0;
        
        // 归并排序, 相当于两个有序数组合成一个有序表(从尾端开始是为了计数)
        while (last_left >= begin && last_right >= mid + 1) {
            if (data[last_left] > data[last_right]) {
                copy[index_copy--] = data[last_left--];
                res += last_right - mid;
            }
            else {
                copy[index_copy--] = data[last_right--];
            }
        }
        
        while (last_left >= begin)
            copy[index_copy--] = data[last_left--];
        while (last_right >= mid + 1)
            copy[index_copy--] = data[last_right--];
        
        return left + right + res;
    }
};

剑指offer 036、两个链表的第一个公共结点

题目

image-20210918143934592

题解

分两种情况:

  1. 长度相同时:
    • 若两链表有公共结点,第一次就遍历到了
    • 若无公共结点,两链表同时走到尾部nullptr,返回nullptr
  2. 长度不同时:
    • 若有公共结点,第一遍差值会出来,第二遍就可以同时走到公共结点
    • 若无公共结点,第二遍时两链表同时走到尾部nullptr
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        if (!pHead1 || !pHead2) return nullptr;
        
        ListNode* p1 = pHead1;
        ListNode* p2 = pHead2;
        while (p1 != p2) {
            p1 = (p1 == nullptr ? pHead2 : p1 = p1->next);
            p2 = (p2 == nullptr ? pHead1 : p2 = p2->next);
        }
        return p1;
    }
};

剑指offer 037、数字在升序数组中出现的次数

题目

image-20210919143219674

题解

使用二分查找

class Solution {
public:
    int GetNumberOfK(vector<int> data ,int k) {
        if (data.empty()) return 0;
        
        int low = 0, high = data.size() - 1;
        while (low <= high) {
            int mid = low + (high - low) / 2;
            if (data[mid] < k) {
                low = mid + 1;
            }
            else if (data[mid] > k) {
                high = mid - 1;
            }
            else {    // 说明已找到k
                int count = 0;
                count++;
                int index = mid - 1;
                while (index >= 0 && data[index] == k) {
                    count++;
                    index--;
                }
                index = mid + 1;
                while (index <= data.size() - 1 && data[index] == k) {
                    count++;
                    index++;
                }
                return count;
            }
        }
        return 0;
    }
};

剑指offer 038、二叉树的深度

题目

image-20210919152059318

题解

class Solution {
public:
    int TreeDepth(TreeNode* pRoot) {
        if (!pRoot) return 0;
        
        int left = TreeDepth(pRoot->left);
        int right = TreeDepth(pRoot->right);
        return max(left, right) + 1;
    }
};

剑指offer 039、平衡二叉树

题目

image-20210919152439805

image-20210919152501482

题解

class Solution {
public:
    bool IsBalanced_Solution(TreeNode* pRoot) {
        if (!pRoot) return true;
        
        return abs(getDepth(pRoot->left) - getDepth(pRoot->right)) <= 1 && IsBalanced_Solution(pRoot->left) && IsBalanced_Solution(pRoot->right);
    }
    int getDepth(TreeNode* tree) {
        if (!tree) return 0;
        
        int left = getDepth(tree->left);
        int right = getDepth(tree->right);
        return max(left, right) + 1;
    }
};

剑指offer 040、数组中只出现一次的两个数字

题目

image-20210921205806917

题解

哇这道题根本没有想起来会用 异或 来解决的

力扣的官方解析

思路

image-20210922165044799

image-20210922165920544

算法

  • 先对所有数字进行一次异或,得到两个出现一次的数字的异或值。
  • 在异或结果中找到任意为 11 的位。
  • 根据这一位对所有的数字进行分组。
  • 在每个组内进行异或操作,得到两个数字。
class Solution {
public:
    vector<int> FindNumsAppearOnce(vector<int>& array) {
        vector<int> result;
        int res1 = 0, res2 = 0;
        int orSum = 0;
        for (auto x : array) {
            orSum ^= x;
        }
        
        int t = 1;    // 找出异或 结果中哪一位是1
        while ((orSum & t) == 0) {
            t = t << 1;
        }
        for (auto x : array) {
            if (t & x) {
                res1 ^= x;
            }
            else {
                res2 ^= x;
            }
        }
        result.push_back(min(res1, res2));
        result.push_back(max(res1, res2));
        return result;
    }
};

剑指offer 041、和为S的连续正数序列

题目

image-20210922170106482

题解

思路:

利用双指针技术,相当于有一个窗口,窗口的左右两边就是两个指针,我们根据窗口内值之和来确定窗口的位置和宽度。

注意,窗口始终是要往右移动的!不可以phigh--

class Solution {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        if (sum == 1) return vector<vector<int> >();
        
        vector<vector<int>> result;    // 保存结果
        int plow = 1, phigh = 2;    // 双指针,根据窗口内的值之和确定窗口的大小和位置,plow要从1开始,不包含0
        while (plow < phigh) {
            // 连续数字之和公式:(a0+an)*n/2
            int cur = (plow + phigh) * (phigh - plow + 1) / 2;
            if (cur == sum) {
                vector<int> temp;
                for (int i = plow; i <= phigh; ++i) {
                    temp.push_back(i);
                }
                result.push_back(temp);
                plow++;    // 窗口要不断向右移动
            }
            else if (cur < sum) {
                phigh++;
            }
            else {
                // 当前窗口之和大于 sum,左边窗口右移
                plow++;
            }
        }
        return result;
    }
};

剑指offer 042、和为S的两个数字

题目

image-20210922173038227

题解

和41题很像,设置两个头尾指针

  • 若ai + aj == sum,就是答案(相差越远乘积越小)

    证明相差越远,乘积越小:

    设x+y=C(C是常数),y-x=d>=0
    即x=(C-d)/2,y=(C+d)/2
    x*y=(C^2 - d^2)/4,画出图形:x*y是一个关于y轴对称的,开口向下的,变量为d的函数。d>=0,d越大,x*y越大。

  • 若ai + aj > sum,aj肯定不是答案之一(前面已得出 i 前面的数已是不可能),j –

  • 若ai + aj < sum,ai肯定不是答案之一(前面已得出 j 后面的数已是不可能),i ++

class Solution {
public:
    vector<int> FindNumbersWithSum(vector<int> array,int sum) {
        vector<int> result;
        int low = 0, high = array.size() - 1;
        while (low < high) {
            if (array[low] + array[high] == sum) {
                result.push_back(array[low]);
                result.push_back(array[high]);
                break;
            }
            else if (array[low] + array[high] < sum) {
                low++;
            }
            else {
                high--;
            }
        }
        return result;
    }
};

剑指offer 043、左旋转字符串

题目

image-20210922181419227

题解

自己第一次做的:

class Solution {
public:
    string LeftRotateString(string str, int n) {
        string temp;
        for (int i = 0; i < n; ++i) {
            temp.push_back(str[i]);
        }
        str.erase(0, n);
        str += temp;
        
        return str;
    }
};

不错的解法

class Solution {
public:
    string LeftRotateString(string str, int n) {
        int length = str.size();
        if(length==0) return str;
        if (n >= length) n = n % length;    // 若循环左移n位的长度 大于 字符串的长度
        str += str;
        // substr()函数:获得字符串s中从第n位开始的长度为length的字符串
        return str.substr(n, length);
    }
};

剑指offer 044、翻转单词序列

题目

image-20210922183024788

题解

从前往后一直读取,遇到空格之后就把之前读取到的压到结果的前面并添加空格。

class Solution {
public:
    string ReverseSentence(string str) {
        string result = "", temp = "";	// temp是当前正在处理的单词
        for (auto s : str) {
            if (s == ' ') {
                result = " " + temp + result;
                temp = "";
            }
            else {
                temp += s;
            }
        }
        if (temp.size()) {
            result = temp + result;
        }
        return result;
    }
};

剑指offer 045、扑克牌顺子

题目

image-20210922185954305

image-20210922190007592

题解

排序 + 遍历

class Solution {
public:
    bool IsContinuous( vector<int> numbers ) {
        int zeroNum = 0;
        sort(numbers.begin(), numbers.end());    // 给五张牌排序
        
        for (int i = 0; i < 4; ++i) {    // 总共有五张牌
            if (numbers[i] == 0) {    // 统计0的数量(大小王)
                zeroNum++;
            }
            else if (numbers[i] == numbers[i + 1]) {    // 如果相邻两个元素相等,返回false
                return false;
            }
        }
        return numbers[4] - numbers[zeroNum] < 5;        // 最大牌 - 最小牌 < 5才能构成顺子
    }
};

剑指offer 046、孩子们的游戏(圆圈中最后剩下的数)

题目

image-20210922212947979

题解

来自力扣大佬@Sweetiee的小号 的解法

image-20210922225617576

image-20210922225637376

第二张图可能不容易理解,再看一个非常清晰的解析

image-20210922232752077

class Solution {
public:
    int LastRemaining_Solution(int n, int m) {
        if(n <= 0 || m < 0) return -1;
        
        int result = 0;
        // 最后一轮剩下2人,从2开始反推
        for (int i = 2; i <= n; ++i) {
            result = (result + m) % i;
        }
        return result;
    }
};

剑指offer 047、求1+2+3+…+n

题目

image-20210922220053896

题解

class Solution {
public:
    int Sum_Solution(int n) {
        int sum = n;
        // 递归调用,同时利用&&操作的短路特性实现递归终止
        n > 0 && (sum += Sum_Solution(n - 1));
        return sum;
    }
};

剑指offer 048、不能加减乘除做加法

题目

image-20210922220122551

题解

  1. 两个数异或:相当于每一位相加,而不考虑进位;
  2. 两个数相与,并左移一位:相当于求得进位;
  3. 将上述两步的结果相加

示例:5->101,7->111

第一步:相加各位的值,不算进位,得到010,二进制每位相加就相当于各位做异或操作,101^111。

第二步:计算进位值,得到1010,相当于各位做与操作得到101,再向左移一位得到1010,(101&111)<<1。

第三步重复上述两步, 各位相加 1000 =010^1010,进位值为100=(010&1010)<<1。

继续重复上述两步:1100 ** =1000^100 ,进位值为0**,跳出循环,1100为最终结果。

class Solution {
public:
    int Add(int num1, int num2) {
        while (num2 != 0) {
            int sum = num1 ^ num2;
            int carray = (num1 & num2) << 1;
            num1 = sum;
            num2 = carray;
        }
        return num1;
    }
};

剑指offer 049、把字符串转换成整数

题目

image-20210923215039560

题解

image-20210923221733131

class Solution {
public:
    int StrToInt(string str) {
        int length = str.size();
        if (length == 0) return 0;
        
        int flag = 1, symbol = 0, i = 0;
        long long num = 0;
        while (i < length && str[i] == ' ') {
            i++;    // 处理首部空格
        }
        
        if (i >= length) return 0;    // 字符串一直为空格
        
        if (str[i] == '-') {    // 负号判断
            flag = -1;
            symbol++;
            i++;
        }
        if (str[i] == '+') {    // 正号判断
            symbol++;
            i++;
        }
        if (symbol > 1) return 0;    // 如果出现两个符号则数字不合法
        
        for (; i < length; ++i) {
            if (str[i] < '0' || str[i] > '9') return 0;    // 判断是否有不合法的数字如1a33
            else {
                num = num * 10 + str[i] - '0';    // 数字拼接
                // 如果输入的是INT_MAX = 2147483647 无影响
                if (num >= INT_MAX && flag == 1) return INT_MAX;
                // 如果输入是INY_MIN = -2147483647-1 = -2147483648,因为num是正数,必须用num > INT_MAX才能正确判断
                if (num > INT_MAX && flag == -1) return INT_MIN;
            }
        }
        return num * flag;
    }
};

剑指offer 050、数组中重复的数字

题目

image-20210923223706813

题解

借助哈希表:

class Solution {
public:
    int duplicate(vector<int>& numbers) {
        unordered_map<int, int> array;
        for (auto x : numbers) {
            array[x]++;
            if (array[x] == 2) return x;
        }
        return -1;
    }
};

剑指offer 051、构建乘积数组

题目

image-20210923225531732

题解

image-20210923233916887

先算下三角中的连乘,即我们先算出B[i]中的一部分,然后倒过来按上三角中的分布规律,把另一部分也乘进去。

class Solution {
public:
    vector<int> multiply(const vector<int>& A) {
        int length = A.size();
        if (length == 1) return vector<int>();
        
        vector<int> B(length, 0);
        B[0] = 1;
        // 计算下三角
        for (int i = 1; i < length; ++i) {
            B[i] = B[i - 1] * A[i - 1];
        }
        // 计算上三角
        int temp = 1;
        for (int i = length - 2; i >= 0; --i) {
            temp *= A[i + 1];
            B[i] *= temp;
        }
        
        return B;
    }
};

剑指offer 052、正则表达式匹配

题目

image-20210924132735721

题解

还是不太懂

力扣大佬三叶姐的方法

image-20210924132701186

image-20210924132724372

class Solution {
public:
    bool match(string s, string p) {
        // 技巧,往原字符头部插入空格,这样得到的string数组是从1开始
        int m = s.size(), n = p.size();
        s.insert(s.begin(), ' ');
        p.insert(p.begin(), ' ');
        // f(i, j)代表s中的1~i字符和p中的1~j字符是否匹配
        vector<vector<bool>> f(m + 1, vector<bool>(n + 1, false));
        f[0][0] = true;
        
        for (int i = 0; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                // 如果下一个字符是 '*',则代表当前字符不能被单独使用
                if (j + 1 <= n && p[j + 1] == '*') continue;
                
                // 对应了p[j] 为普通字符和 '.' 的两种情况
                if (i - 1 >= 0 && p[j] != '*') {
                    f[i][j] = f[i - 1][j - 1] && (s[i] == p[j] || p[j] == '.');
                }
                
                // 对应了p[j] 为 '*' 的情况
                else if (p[j] == '*') {
                    f[i][j] = (j - 2 >= 0 && f[i][j - 2]) 
                        || (i - 1 >= 0 && f[i - 1][j] && (s[i] == p[j - 1] || p[j - 1] == '.'));
                }
            }
        }
        return f[m][n];
    }
};

剑指offer 053、表示数值的字符串

题目

image-20210924155015986

image-20210924155030056

题解

额,直接Ctrl c, Ctrl v。

官方解析:

class Solution {
public:
    enum State {
        STATE_INITIAL,
        STATE_INT_SIGN,
        STATE_INTEGER,
        STATE_POINT,
        STATE_POINT_WITHOUT_INT,
        STATE_FRACTION,
        STATE_EXP,
        STATE_EXP_SIGN,
        STATE_EXP_NUMBER,
        STATE_END
    };

    enum CharType {
        CHAR_NUMBER,
        CHAR_EXP,
        CHAR_POINT,
        CHAR_SIGN,
        CHAR_SPACE,
        CHAR_ILLEGAL
    };

    CharType toCharType(char ch) {
        if (ch >= '0' && ch <= '9') {
            return CHAR_NUMBER;
        } else if (ch == 'e' || ch == 'E') {
            return CHAR_EXP;
        } else if (ch == '.') {
            return CHAR_POINT;
        } else if (ch == '+' || ch == '-') {
            return CHAR_SIGN;
        } else if (ch == ' ') {
            return CHAR_SPACE;
        } else {
            return CHAR_ILLEGAL;
        }
    }

    bool isNumeric(string s) {
        unordered_map<State, unordered_map<CharType, State>> transfer{
            {
                STATE_INITIAL, {
                    {CHAR_SPACE, STATE_INITIAL},
                    {CHAR_NUMBER, STATE_INTEGER},
                    {CHAR_POINT, STATE_POINT_WITHOUT_INT},
                    {CHAR_SIGN, STATE_INT_SIGN}
                }
            }, {
                STATE_INT_SIGN, {
                    {CHAR_NUMBER, STATE_INTEGER},
                    {CHAR_POINT, STATE_POINT_WITHOUT_INT}
                }
            }, {
                STATE_INTEGER, {
                    {CHAR_NUMBER, STATE_INTEGER},
                    {CHAR_EXP, STATE_EXP},
                    {CHAR_POINT, STATE_POINT},
                    {CHAR_SPACE, STATE_END}
                }
            }, {
                STATE_POINT, {
                    {CHAR_NUMBER, STATE_FRACTION},
                    {CHAR_EXP, STATE_EXP},
                    {CHAR_SPACE, STATE_END}
                }
            }, {
                STATE_POINT_WITHOUT_INT, {
                    {CHAR_NUMBER, STATE_FRACTION}
                }
            }, {
                STATE_FRACTION,
                {
                    {CHAR_NUMBER, STATE_FRACTION},
                    {CHAR_EXP, STATE_EXP},
                    {CHAR_SPACE, STATE_END}
                }
            }, {
                STATE_EXP,
                {
                    {CHAR_NUMBER, STATE_EXP_NUMBER},
                    {CHAR_SIGN, STATE_EXP_SIGN}
                }
            }, {
                STATE_EXP_SIGN, {
                    {CHAR_NUMBER, STATE_EXP_NUMBER}
                }
            }, {
                STATE_EXP_NUMBER, {
                    {CHAR_NUMBER, STATE_EXP_NUMBER},
                    {CHAR_SPACE, STATE_END}
                }
            }, {
                STATE_END, {
                    {CHAR_SPACE, STATE_END}
                }
            }
        };

        int len = s.length();
        State st = STATE_INITIAL;

        for (int i = 0; i < len; i++) {
            CharType typ = toCharType(s[i]);
            if (transfer[st].find(typ) == transfer[st].end()) {
                return false;
            } else {
                st = transfer[st][typ];
            }
        }
        return st == STATE_INTEGER || st == STATE_POINT || st == STATE_FRACTION || st == STATE_EXP_NUMBER || st == STATE_END;
    }
};

某位ACM选手的解法

class Solution {
public:
     bool is_digit(char a) { //判断是否为数字
        return a >= '0' && a <= '9';
    }
    bool is_e(char a) {
        return a == 'e' || a == 'E';
    }
    bool check(string st) {
        int count = 0;
        for (char i : st) {
            if (i == '.') count++;
            if (count > 1) return false;
        }
        return true;
    }
    bool isNumeric(string s) {
        if (check(s) == false) return false;//确保最多只有一个小数点
        //前后去空格
        int i = 0;
        while (i < s.size() && s[i] == ' ') {
            i++;
        }
        int end = s.size() - 1;
        while (end >= 0 && s[end] == ' ') {
            s.erase(s.begin() + end);
            end--;
        }
        int len = s.size();
        if (s[i] == '+' || s[i] == '-') {
            i++;
            if (i >= len) return false;
            if (s[i] == '.') {
                i++;
                if (i >= len) return false;
                if (is_digit(s[i])) {
                    while (i < len && is_digit(s[i])) { i++; }
                    if (i >= len) return true;
                    if (is_e(s[i])) {
                        i++;
                        if (i >= len) return false;
                        if (s[i] == '-' || s[i] == '+') i++;
                        if (i >= len) return false;
                        while (i < len && is_digit(s[i])) { i++; }
                        if (i >= len) return true;
                        else return false;
                    }
                    else {
                        return false;
                    }
                }
                else {
                    return false;
                }
            }
            else if (is_digit(s[i])) {
                while (i < len && is_digit(s[i])) { i++; }
                if (i >= len)return true;
                if (is_e(s[i])) {
                    i++;
                    if (i >= len) return false;
                    if (s[i] == '-' || s[i] == '+') i++;
                    if (i >= len) return false;
                    while (i < len && is_digit(s[i])) { i++; }
                    if (i >= len) return true;
                    else return false;
                }
                else if (s[i] == '.' ) {
                    i++;
                    if (i >= len) return true;
                    if (is_digit(s[i])) {
                        while (i < len && is_digit(s[i])) { i++; }
                        if (i >= len) return true;
                        if (is_e(s[i])) {
                            i++;
                            if (i >= len) return false;
                            if (s[i] == '-' || s[i] == '+') i++;
                            if (i >= len) return false;
                            while (i < len && is_digit(s[i])) { i++; }
                            if (i >= len) return true;
                            else return false;
                        }
                        else {
                            return false;
                        }
                    }
                    else if (is_e(s[i])) {
                        i++;
                        if (i >= len) return false;
                        if (s[i] == '-' || s[i] == '+') i++;
                        if (i >= len) return false;
                        while (i < len && is_digit(s[i])) { i++; }
                        if (i >= len) return true;
                        else return false;
                    }
                    else {
                        return false;
                    }
                }
                else {
                    return false;
                }
            }
            else {
                return false;
            }
        }
        else if (s[i] == '.') {
            i++;
            if (i >= len) return false;
            if (is_digit(s[i])) {
                while (i < len && is_digit(s[i])) { i++; }
                if (i >= len) return true;
                if (is_e(s[i])) {
                    i++;
                    if (i >= len) return false;
                    if (s[i] == '-' || s[i] == '+') i++;
                    if (i >= len) return false;
                    while (i < len && is_digit(s[i])) { i++; }
                    if (i >= len) return true;
                    else return false;
                }
                else {
                    return false;
                }
            }
        }
        else if (is_digit(s[i])) {
            while (i < len && is_digit(s[i])) { i++; }
            if (i >= len) return true;
            if (s[i] == '.') {
                i++;
                if (i >= len) return true;
                if (is_digit(s[i])) {
                    while (i < len && is_digit(s[i])) { i++; }
                    if (i >= len) return true;
                    else if (is_e(s[i])) {
                        i++;
                        if (i >= len) return false;
                        if (s[i] == '-' || s[i] == '+') i++;
                        if (i >= len) return false;
                        while (i < len && is_digit(s[i])) { i++; }
                        if (i >= len) return true;
                        else return false;
                    }
                    else {
                        return false;
                    }
                }
                else if (is_e(s[i])) {
                    i++;
                    if (i >= len) return false;
                    if (s[i] == '-' || s[i] == '+') i++;
                    if (i >= len) return false;
                    while (i < len && is_digit(s[i])) { i++; }
                    if (i >= len) return true;
                    else return false;
                }
                else {
                    return false;
                }
            }
            else if (is_e(s[i])) {
                i++;
                if (i >= len) return false;
                if (s[i] == '-' || s[i] == '+') i++;
                if (i >= len) return false;
                while (i < len && is_digit(s[i])) { i++; }
                if (i >= len) return true;
                else return false;
            }
            else {
                return false;
            }
        }
        else {
            return false;
        }
        return false;
    }
};

剑指offer 054、字符流中第一个不重复的字符

题目

image-20210924161828013

题解

借助哈希表,很简单

class Solution
{
public:
  //Insert one char from stringstream
    void Insert(char ch) {
         v.push_back(ch);
        result[ch]++;
    }
  //return the first appearence once char in current stringstream
    char FirstAppearingOnce() {
        for (auto& ch : v) {
            if (result[ch] == 1) return ch;
        }
        return '#';
    }
    
private:
    vector<char> v;
    unordered_map<char, int> result;
};

剑指offer 055、 链表中环的入口结点

题目

image-20210924181650687

image-20210924181700238

题解

利用快慢指针,从头结点开始出发,fast指针每次移动两个节点,slow每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。

image-20210925175114947

相遇时: slow指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z)

因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2: (x + y) * 2 = x + y + n (y + z) ,可得到x = n (y + z) - y

  • 当 n为1的时候,公式就化解为 x = z
  • 当n大于1时,就是fast指针在环形转n圈之后才遇到 slow指针。
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead) {
        ListNode* slow = pHead;
        ListNode* fast = pHead;
        while (fast && fast->next) {
            slow = slow->next;
            fast = fast->next->next;
            // 当快慢指针相遇后,一个指针从头结点开始,一个结点从相遇结点开始,直到再次相遇
            if (slow == fast) {
                ListNode* index1 = pHead;
                ListNode* index2 = fast;
                while (index1 != index2) {
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index1;
            }
        }
        return nullptr;
    }
};

剑指offer 056、删除链表中重复的结点

题目

image-20210925175959663

题解

  1. 首先添加一个头节点,以方便碰到第一个,第二个节点就相同的情况
  2. 设置 pre ,cur指针, pre指针指向当前确定不重复的那个节点,而cur指针相当于工作指针,一直往后面搜索。
class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead) {
        if (!pHead) return pHead;
        
        ListNode* Head = new ListNode(0);
        Head->next = pHead;
        ListNode* pre = Head;
        ListNode* cur = Head->next;
        while (cur) {
            if (cur->next != nullptr && cur->val == cur->next->val) {
                // 寻找最后一个相同的结点
                while (cur->next != nullptr && cur->val == cur->next->val) {
                    cur = cur->next;
                }
                pre->next = cur->next;
                cur = cur->next;
            }
            else {
                pre = pre->next;
                cur = cur->next;
            }
        }
        return Head->next;
    }
};

剑指offer 057、二叉树的下一结点

题目

image-20210925180226908

image-20210925180239481

image-20210925180305150

image-20210925180316973

题解

思路:

  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) {
        // 情况1
        if (!pNode) return nullptr;
        
        TreeLinkNode* node = nullptr;
        // 情况2.如果当前节点有右子树,则右子树最左边的那个节点就是所要寻找的
        if (pNode->right) {
            node = pNode->right;
            while (node->left) {
                node = node->left;
            }
            return node;
        }
        
        while (pNode->next) {    // 该结点的父结点存在时
            TreeLinkNode* root = pNode->next;
            if (root->left == pNode) return root;    // 情况3
            pNode = pNode->next;
        }
        return nullptr;
    }
};

剑指offer 058、对称的二叉树

题目

image-20210926163827820

题解

思路:

  • 终止条件:
    • 当左节点和右节点同时越过叶节点,则此树所有节点对称
    • 当左右节点只有一个越过叶节点,返回false
    • 当左右节点值不相等,此树不对称,返回false
  • 递推过程:
    • 判断两节点lNode->left, rNode->right是否对称,即recur(lNode->left, rNode->right)
    • 判断两节点lNode->right, rNode->left是否对称,即recur(lNode->right, rNode->left);
class Solution {
public:
    bool isSymmetrical(TreeNode* pRoot) {
        if (!pRoot) return true;
        
        return (pRoot == nullptr ? true : recur(pRoot->left, pRoot->right));
    }
    bool recur (TreeNode* lNode, TreeNode* rNode) {
        if (lNode == nullptr && rNode == nullptr) return true;
        if (lNode == nullptr || rNode == nullptr || lNode->val != rNode->val) return false;
        
        return recur(lNode->left, rNode->right) && recur(lNode->right, rNode->left);
    }
};

剑指offer 059、按之字形顺序打印二叉树

题目

image-20210926163843177

image-20210926163854162

题解

非常不错的一道题

利用两个栈来解决(一个栈存奇数行,另一个存偶数行)

class Solution {
public:
    vector<vector<int> > Print(TreeNode* pRoot) {
        if (!pRoot) return vector<vector<int> >();
        
        vector<vector<int> > result;
        stack<TreeNode*> stack1, stack2;
        stack1.push(pRoot);
        while (!stack1.empty() || !stack2.empty()) {
            vector<int> res1, res2;
            TreeNode* cur = nullptr;
            // 将偶数行放到栈2中
            while (!stack1.empty()) {
                cur = stack1.top();
                if (cur->left)
                    stack2.push(cur->left);
                if (cur->right)
                    stack2.push(cur->right);
                res1.push_back(stack1.top()->val);    // 保存奇数行的数据
                stack1.pop();
            }
            if (!res1.empty())
                result.push_back(res1);
            
            // 将奇数行放到栈1中
            while (!stack2.empty()) {
                cur = stack2.top();
                if (cur->right)
                    stack1.push(cur->right);
                if (cur->left)
                    stack1.push(cur->left);
                res2.push_back(stack2.top()->val);    // 保存偶数行的数据
                stack2.pop();
            }
            if (!res2.empty())
                result.push_back(res2);
        }
        return result;
    }
    
};

剑指offer 060、把二叉树打印成多行

题目

image-20210926163905994

image-20210926163927481

题解

借助队列,将该层元素从队列中移出,并将下一层元素移入

class Solution {
public:
        vector<vector<int> > Print(TreeNode* pRoot) {
            if (!pRoot) return vector<vector<int> >();
            
            vector<vector<int> > result;
            queue<TreeNode*> q;    // 借助队列
            q.push(pRoot);
            while (!q.empty()) {
                vector<int> temp;
                // 将队列中原有元素移出,并将队列中元素的子节点添加进来
                for (int i = q.size(); i > 0; --i) {
                    TreeNode* node = q.front();
                    temp.push_back(node->val);
                    if (node->left) q.push(node->left);
                    if (node->right) q.push(node->right);
                    q.pop();
                }
                // 将该层的元素加入result中
                result.push_back(temp);
            }
            return result;
        }
};

剑指offer 061、序列化二叉树

题目

image-20210926215049368

image-20210926215111863

题解

咦~ 困难… 我可以的… 好难啊… 看看解析去… (⊙o⊙)?…

写了两三个小时了… 还是不太会…

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    char* Serialize(TreeNode *root) {    
        if (!root) return nullptr;
        
        string s;
        queue<TreeNode*> q;
        q.push(root);
        while (!q.empty()) {
            if (q.front()) {
                q.push(q.front()->left);
                q.push(q.front()->right);
                s += to_string(q.front()->val) + ' ';
            }
            else {
                s += "# ";
            }
            q.pop();
        }
        // strdup:复制字符串,可以自动分配空间(如果这里使用strcpy,还需手动分配内存)
        char* result = strdup(s.c_str());
        return  result;
    }
    TreeNode* Deserialize(char *str) {
        if (!str) return nullptr;
        
        int k = 0;
        TreeNode* result = nextNode(str, k);
        queue<TreeNode*> q;
        q.push(result);
        while (!q.empty()) {
            q.front()->left = nextNode(str, k);
            q.front()->right = nextNode(str, k);
            if (q.front()->left) 
                q.push(q.front()->left);
            if (q.front()->right) 
                q.push(q.front()->right);
            q.pop();
        }
        return result;
    }
    
private:
    TreeNode* nextNode(char* str, int& k) {
        string s;
        while (str[k] != '\0' && str[k] != ' ') {
            if (str[k] == '#') {
                k += 2;    // #后面还有一个空字符,所以要加2
                return nullptr;
            }
            s += str[k];
            ++k;
        }
        if (str[k] == ' ')
            k++;
        if (!s.empty())
            // stio:将字符串转换为整数
            return new TreeNode(stoi(s));
        return nullptr;
    }
};

剑指offer 062、二叉搜索树的第k个结点

题目

image-20210927012243184

题解

首先想到的是递归

class Solution {
public:
    TreeNode* KthNode(TreeNode* pRoot, int k) {
        if (!pRoot) return nullptr;
        
        TreeNode* result = KthNode(pRoot->left, k);
        if (count >= k) return result;
        if (++count == k) return pRoot;
        return KthNode(pRoot->right, k);
    }
private:
    int count = 0;
};

借助栈来寻找,这种解法还是不太理解

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    TreeNode* KthNode(TreeNode* pRoot, int k) {
        if (!pRoot) return nullptr;
        
        stack<TreeNode*> res;
        TreeNode* p = pRoot;
        while (!res.empty() || p) {
            while (p) {
                res.push(p);
                p = p->left;
            }
            TreeNode* node = res.top();
            res.pop();
            if (--k == 0)     // 计数器
                return node;
            p = node->right;
        }
        return nullptr;
    }
};

剑指offer 063、数据流中的中位数

题目

image-20210927151623655

题解

这道题在读懂题意后,利用优先队列做起来还挺方便

思路:

利用两个优先队列,一个是最大堆(存放最小的一半数字),一个是最小堆(存放最大的一半数字)

由于数据是流式的,所以在这个过程中,最大最小堆中的元素会不断调整:

  • 当两堆的数据个数相同时,在最大堆中加入元素。
    (先将该元素加入最小堆中调整,然后将最小堆中的根节点(最小)会被加入到最大堆中)
  • 当两堆的数据个数不同时(只会出现一种情况:最大堆中元素个数比最小堆多一个),在最小堆中加入元素。
    (先将该元素放在最大堆中调整,然后将最大堆的根节点(最大)加入最小堆中)
class Solution {
public:
    // 最大堆(堆顶最大,存放最小的一半数字)
    priority_queue<int, vector<int>, less<int>> maxHeap;
    // 最小堆(堆顶最小,存放较大的一半数字)
    priority_queue<int, vector<int>, greater<int>> minHeap;
    /*
     * 当两堆的数据个数相同时,在最大堆中加入元素,注意这里不是直接加入最大堆中!
     * 先将该元素加入最小堆中调整后,在最小堆中的根节点(最小)会被加入到最大堆中
     * 这样就保证了最大堆中的元素始终比最小堆中元素小
     *
     * 否则,先将该元素放在最大堆中调整,然后将最大堆的根节点(最大)加入最小堆中
     */
    void Insert(int num) {
        if (maxHeap.size() == minHeap.size()) {
            minHeap.push(num);
            int top = minHeap.top();
            minHeap.pop();
            maxHeap.push(top);
        }
        else {
            maxHeap.push(num);
            int top = maxHeap.top();
            maxHeap.pop();
            minHeap.push(top);
        }
    }

    double GetMedian() { 
        if (maxHeap.size() == minHeap.size()) {
            return (maxHeap.top() + minHeap.top()) * 1.0 / 2;
        }
        else {
            return maxHeap.top() * 1.0;
        }
    }
};

剑指offer 064、滑动窗口的最大值

题目

image-20210927165448613

题解

非常不错的一道题!!!

利用双端队列:滑动窗口不为空时,只要准备进入的元素大于滑动窗口最后一个元素,就删除队列前一个元素,直到队列末尾元素比新数大或队列为空时停止。

看代码注释吧!

class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size) {
        if (num.empty() || size == 0) return vector<int>();
        
        vector<int> result;
        deque<int> window;    // 记录每个窗口的最大下标(队首为最大下标)
        int leftPoint = 0;
        for (unsigned int i = 0; i < num.size(); ++i) {
            // 滑动窗口不为空时,只要准备进入的元素大于滑动窗口最后一个元素,直到队列末尾元素比新数大或队列为空时停止
            while (!window.empty() && num[window.back()] < num[i]) {
                window.pop_back();
            }
            window.push_back(i);
            // 判断队首元素是否过期
            if (window.front() == i - size) {
                window.pop_front();
            }
            // 向result列表中加入元素,要判断一下是否为最开始的size元素,特殊处理
            if (i >= size - 1) {
                result.push_back(num[window.front()]);
            }
        }
        return result;
    }
};

剑指offer 065、矩阵中的路径

题目

image-20210928002418549

题解

看看K神的解析:

image-20210928004822080

image-20210928004841831

class Solution {
public:
    bool hasPath(vector<vector<char> >& matrix, string word) {
        row = matrix.size();
        col = matrix[0].size();
        // 每次调用size()函数也是一笔开销
        for (int i = 0; i < row; ++i) {
            for (int j = 0; j < col; ++j) {
                if (dfs(matrix, word, i, j, 0))
                    return true;
            }
        }
        return false;
    }
private:
    int row, col;
    // k表示string中第k个元素
    bool dfs(vector<vector<char>>& matrix, string word, int i, int j, int k) {
        // 当发生越界,当前矩阵元素与目标字符不匹配时,返回false
        if (i >= row || i < 0 || j >= col || j < 0 || matrix[i][j] != word[k]) return false;
        // 当k的值等于len(word) - 1;说明字符串全部匹配了
        if (k == word.size() - 1) return true;
        // 每次访问过后标记为'\0',避免重复访问
        matrix[i][j] = '\0';
        // 朝当前元素上下左右四个方向都递归
        bool result = dfs(matrix, word, i + 1, j, k + 1) || dfs(matrix, word, i - 1, j, k + 1)
            || dfs(matrix, word, i, j + 1, k + 1) || dfs(matrix, word, i, j - 1, k + 1);
        // 将标记的元素修改为初始值
        matrix[i][j] = word[k];
        
        return result;
    }
};

剑指offer 066、机器人的运动范围

题目

image-20210928010803954

image-20210928010817518

题解

不错不错,我们借助一个矩阵,对矩阵每个元素,当满足条件时标记,不断递归调用即可解决。

class Solution {
public:
    int movingCount(int threshold, int rows, int cols) {
        vector<vector<bool>> matrix(rows, vector<bool>(cols, false));
        return movingCountCore(threshold, rows, 0, cols, 0, matrix);
    }
    int movingCountCore(int threshold, int rows, int i, int cols, int j, vector<vector<bool>>& matrix) {
        // 当边界值不符合 || 不能到达 || 已经走过 时,就返回0
        if (i >= rows || j >= cols || !isArrival(threshold, i, j) || matrix[i][j] == true) return 0;
        
        matrix[i][j] = true;    // 已经走过并且符合要求,标记为true
        // 计算向下 / 向右 递归调用之和
        return movingCountCore(threshold, rows, i + 1, cols, j, matrix) 
            + movingCountCore(threshold, rows, i, cols, j + 1, matrix) + 1;
    }
    bool isArrival(int threshold, int i, int j) {
        // 计算是否满足threshold
        int sum = 0;
        while (i != 0) {
            sum += i % 10;
            i /= 10;
        }
        while (j != 0) {
            sum += j % 10;
            j /= 10;
        }
        
        return sum <= threshold;
    }
};

剑指offer 067、剪绳子

题目

image-20210928100812604

题解

先观察一组数据:

  • 4 : 2*2
  • 5 : 2*3
  • 6 : 3*3
  • 7 : 2*2*3
  • 8 : 2*3*3
  • 9 : 3*3*3
  • 10:2*2*3*3
  • 11:2*3*3*3
  • 12:3*3*3*3
  • 13:2*2*3*3*3
  • 14:2*3*3*3*3

可以看出,对于所有的数字的最大乘积,都是由2和3的乘积组成,因此我们可以定义一个变量来保存3的乘积(每次乘以3,同时给number减一个3),直到number == 4为止。

看代码:

class Solution {
public:
    int cutRope(int number) {
        
        if (number < 2) return 0;        // 题目中限定(2 <= n <= 60)
        if (number < 4) return number - 1;    // 题目中限定 n>1并且m>1
        
        int ThreeMultip = 1;    // 记录3的乘积
        while (number > 4) {
            ThreeMultip *= 3;    // 每乘一个3,就给number减一个3
            number -= 3;
        }
        return ThreeMultip * number;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ClimberCoding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值