剑指offer(1-20)

本文解析了面试中常见的数组问题(查找重复数字、区间计数、排序数组等)及其高效算法,如哈希表、替换法、二分查找等,并介绍了链表操作(打印尾到头、合并、删除节点)。还包括了如何用O(1)空间复杂度解决JZ系列问题的实例。
摘要由CSDN通过智能技术生成

1.面试题三 数组中重复的数字

题目一

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任一一个重复的数字。 例如,如果输入长度为7的数组[2,3,1,0,2,5,3],那么对应的输出是2或者3。存在不合法的输入的话输出-1

法一、利用hashmap,bitset尤其适用于这种形式
/*(O(n),O(n))
 * 每个数字以O(1)的时间来判断哈希表
 * 需要一个大小为O(n)的哈希表
*/
class Solution {
public:
    /** 
     * @param numbers int整型vector 
     * @return int整型
     */
    int duplicate(vector<int>& numbers) {
        bitset<10000> hashmap;
        for(int i = 0; i < numbers.size(); i++){
            if(hashmap[numbers[i]]){
                return numbers[i];
            }
            hashmap[numbers[i]] = 1;
        }
        return -1;        
    }
};
法二、
 /**替换法(O(n),O(1))
  *数组存放原则:numbers[i] = i
    遍历数组所有元素,交换不符合数组存放原则的元素:
        例如[2,3,1,0,2]
        遍历0位元素2:(交换0位元素2和2位元素1)->[1,3,2,0,2]
        遍历0位元素1:(交换0位元素1和1位元素3)->[3,1,2,0,2]
        遍历0位元素3:(交换0位元素3和3位元素0)->[0,1,2,3,2]
        依次遍历0、1、2、3位置元素,都符合存放原则numbers[i] = i,不做任何操作
        遍历末位元素2,此时末位元素2和2位元素2相等,出现了两次,即数字2位重复元素
 */
int duplicate(vector<int>& numbers) {
        for(int i = 0; i < numbers.size(); i++)
        {
            if(numbers[i] == i){
                continue;
            }else{
                if(numbers[i] == numbers[numbers[i]]){
                    return numbers[i];
                }else{
                    /**
                    *int temp = numbers[i];
                    *numbers[i] = numbers[numbers[i]];
                    *numbers[temp] = temp;
                    *注意这里交换时
                    *第三行不能写为 numbers[numbers[i]] = temp
                    *因为numbers[i]的值已经发生改变
                    **/
                    int temp = numbers[numbers[i]];
                    numbers[numbers[i]] = numbers[i];
                    numbers[i] = temp;
                    i--;
                }
            }
        }
        return -1;
}

题目二

bool count(vector<int>& numbers, int start, int end){
    int length = numbers.size();
    int count = 0;
    for(int i = 0; i < length; i++){
        if((numbers[i] >= start) && (numbers[i] <= end)){
            count++;
        }
    }
    if(count > (end - start + 1)){
        return 1;
    }else{
        return 0;
    }
}
int duplicate(vector<int>& numbers) {
    int length = numbers.size();
    int start = 1;
    int end = length - 1;
    while(start < end){
        int middle = start + (end - start)/2;
        if(count(numbers, start, middle)){
            end = middle;
        }else{
            start = middle + 1;
        }
    }
    if(count(numbers, start, end)){
            return start;
    }else{
            return -1;
    }
}

2.面试题四 JZ4 二维数组中的查找

描述
在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
[
[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]
]
给定 target = 7,返回 true。
给定 target = 3,返回 false。
数据范围:矩阵的长宽满足 0 \le n,m \le 5000≤n,m≤500
进阶:空间复杂度 O(1)O(1) ,时间复杂度 O(n+m)O(n+m)

法一、
bool Find(int target, vector<vector<int>> array)
{
    int rows = array.size();
    int cols = array[0].size();
    if (cols == 0)
    {
        return false;
    }
    if (target > array[rows - 1][cols - 1])
    {
        return false;
    }
    int hang = 0;
    int lie = 0;
    for (int i = 0; i < max(rows, cols); i++)
    {
        if (rows == cols)
        {
            if (array[i][i] >= target)
            {
                hang = i;
                lie = i;
                break;
            }
        }
        else
        {
            if (i < min(rows, cols))
            {
                if (array[i][i] >= target)
                {
                    hang = i;
                    lie = i;
                    break;
                }
            }
            else
            {
                if (rows > cols)
                {
                    if (array[i][cols - 1] >= target)
                    {
                        hang = i;
                        lie = cols - 1;
                        break;
                    }
                }
                else
                {
                    if (array[rows - 1][i] >= target)
                    {
                        hang = rows - 1;
                        lie = i;
                        break;
                    }
                }
            }
        }
    }
    for (int j = lie; j < cols; j++)
    {
        for (int i = 0; i <= hang; i++)
        {
            if (array[i][j] == target)
            {
                return true;
            }
        }
    }
    for (int j = hang; j < rows; j++)
    {
        for (int i = 0; i <= lie; i++)
        {
            if (array[j][i] == target)
            {
                return true;
            }
        }
    }

    return false;
}
法二、
class Solution {
public:
    bool Find(int target, vector<vector<int> > array) {
        bool found = false;
        int rows = array.size();
        int cols = array[0].size();
        int row = 0;
        int col = cols - 1;
            while(row<rows && col>=0){
                if(array[row][col] == target){
                    return true;
                }else if(array[row][col] > target){
                    col--;
                }else{
                    row++;
                }
            }
   
        return found;
    }
};

3. JZ5 替换空格

题目一

法一
string replaceSpace(string s) {
        // write code here
        int length = s.size();
        for(int i = 0; i < s.size(); i++){
            if(s[i] == ' '){
                s[i] = '%';
                s.insert(i+1,"20");
            }
        }     
        return s;      
    }
//c++可以直接对字符串进行遍历
string replaceSpace(string s) {
        // write code here
        string ans="";
        // 直接对字符串进行遍历
        for(auto x:s){
            // 对空白字符进行替换
            if(x==' '){
                ans+="%20";
            // 否则不进行处理
            }else{
                ans+=x;
            }
        }
        return ans;
    }
法二
/*
* 双指针从后向前遍历
* 若以s[]的形式越界访问或者修改数组,超出s的长度范围
* 不会被输出,所以实现要resize一下。
*/
string replaceSpace(string s)
{
    // write code here
    int i = 0;
    int length = 0;
    int needlength = 0;
    char a[3] = {'%', '2', '0'};
    while (s[i] != '\0')
    {
        if (s[i] == ' ')
        {
            length++;
            needlength += 3;
        }
        else
        {
            length++;
            needlength++;
        }
        i++;
    }
    cout << length << " " << needlength << endl;
    s.resize(needlength+1);
    while (length >= 0)
    {
        //cout << s[length] << " ";
        if (s[length] == ' ')
        {
            for (int i = 0; i < 3; i++)
            {
                s[needlength --] = a[2 - i];
            }
        }
        else
        {
            s[needlength] = s[length];
            needlength--;
        }
        length--;
    }
    return s;
}

题目二

合并两个有序的数组
描述
给出一个有序的整数数组 A 和有序的整数数组 B ,请将数组 B 合并到数组 A 中,变成一个有序的升序数组

注意:

  1. 保证 A 数组有足够的空间存放 B 数组的元素, A 和 B 中初始的元素数目分别为 m 和 n,A的数组空间大小为 m+n
  2. 不要返回合并的数组,将数组 B 的数据合并到 A 里面就好了
  3. A 数组在[0,m-1]的范围也是有序的
法一、
void merge(int A[], int m, int B[], int n) {
        int k = m+n-1;
        m--;
        n--;
        while(min(m,n) >= 0){
            if(A[m] > B[n]){
                A[k] = A[m];
                m--;
            }else{
                A[k] = B[n];
                n--;
            }
            k--;  
        }
        if(m < 0){
            for(int i = 0; i <= n; i++){
                A[i] = B[i];
            }
        }
    }
//细节操作
void merge(int A[], int m, int B[], int n) {
        int i=m-1,j=n-1,p=m+n-1;//i,j指针指向原末尾,p指针指向合并后的A末尾
        while(~i&&~j) {// ~i  <==> i>=0,因为-1取反过后返回的是0,位运算一般情况比其他运算符速度要快
            A[p--]=A[i]>B[j]?A[i--]:B[j--];//合并A、B数组, 比较i,j指针指向的元素,将大的那个丢进去
        }
        while(~j){//如果B数组还有剩余,则直接全部移到A数组中
            A[p--]=B[j--];
        }
        /*此处如果出现A数组有剩余的情况,理论上应该执行while(i>=0)A[p--]=A[i--]
        但是如果A有剩余必然有p==i(出现剩余的情况,只有可能一个出现剩余,不可能出现AB数组都剩余的情况)
        (如果出现AB都剩余,就跟第一个for循环矛盾了)
        既然对于指针p==i,那么此时这个while循环就会显得很多余                    
        */   
    }

面试题六 JZ6 从尾到头打印链表

法一
/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        vector<int> v;
        if(head == nullptr){
            return v;
        }
        cout<< head->val;
        printdigui(v, head);
        return v;
        
    }
    void printdigui(vector<int>& v, ListNode*  head){
        if((head -> next) == nullptr){
            v.push_back(head->val);
        }else{
            printdigui(v, head->next);
            v.push_back(head->val);
        }
    }
};
//对比别人的题解,相当的干练
class Solution {
public:
    vector<int> ans;
    void dfs(ListNode* now){
        // 递归的出口为当前的指针为空的情况
        if(!now){
            return;
        }
        // 向后面进行递归
        dfs(now->next);
        // 递归之后收集权值
        ans.push_back(now->val);
    }
    vector<int> printListFromTailToHead(ListNode* head) {
        dfs(head);
        return ans;
    }
};
法二
//利用std提供的栈实现
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        vector<int> v;
        std::stack<ListNode*> nodes;
        ListNode* nodetemp = head;
        while(nodetemp != nullptr){
            nodes.push(nodetemp);
            nodetemp = nodetemp->next;
        }
        while(!nodes.empty()){
            nodetemp = nodes.top();
            v.push_back(nodetemp->val);
            nodes.pop();
        }
        return v;
    }
};

面试题七 JZ7 重建二叉树

法一
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        if(pre.size() == 0){
            return nullptr;
        }
        TreeNode* it = new TreeNode(pre[0]);
        int i = 0;
        while(pre[0] != vin[i]){
            i++;
        }
        vector<int> pre1,vin1,pre2,vin2;
        for(int j = 1; j <= i; j++){
            pre1.push_back(pre[j]);
        }
        for(int j = 0; j < i; j++){
            vin1.push_back(vin[j]);
        }
        for(int j = i+1; j < pre.size(); j++){
            pre2.push_back(pre[j]);
        }
        for(int j = i+1; j < vin.size(); j++){
            vin2.push_back(vin[j]);
        }
        it->left = reConstructBinaryTree(pre1, vin1);
        it->right = reConstructBinaryTree(pre2, vin2);
        return it;    
        /*对于vector的处理可以更巧妙
         *int leftSize = posi - vin.begin();
         *int rightSize = vin.end() - posi - 1;
          node->left = reConstructBinaryTree(vector(pre.begin() + 1, pre.begin() + 1 + leftSize),
          vector(vin.begin(), vin.begin() + leftSize));
          node->right = reConstructBinaryTree(vector(pre.begin() + 1 + leftSize, pre.end()),
          vector(vin.begin() + leftSize + 1, vin.end()))
        */    
}

面试题8 JZ8 二叉树的下一个结点

法一
/*
struct TreeLinkNode {
    int val;
    struct TreeLinkNode *left;
    struct TreeLinkNode *right;
    struct TreeLinkNode *next;
    TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL) {
        
    }
};
*/
//先得到中序遍历,再去寻找下一个值
//时间复杂度也是线性的,第一步:最坏为O(N), N为整棵树结点的个数。第二步:O(N), //第三步:最坏为O(N),所以整的时间复杂度:3*O(N)
class Solution {
public:
    vector<TreeLinkNode*> v;
    void tree2mid(TreeLinkNode* pNode){
        if(pNode == nullptr){
            return;  //void 不能返回NULL,也不能不写return
        }
        tree2mid(pNode->left);
        v.push_back(pNode);
        tree2mid(pNode->right);
    }
    TreeLinkNode* GetNext(TreeLinkNode* pNode) {
        if(pNode == nullptr){
            return nullptr;
        }
        TreeLinkNode* root = pNode;
        TreeLinkNode* tmp = pNode;
        while(tmp){
            root = tmp;
            tmp = tmp -> next;            
        } 
        tree2mid(root);
        for(int i = 0; i < v.size(); i++)
        {
            if (v[i] == pNode && i + 1 != v.size()) {
                  return v[i+1];
              }
        }
        return nullptr;
    }
};
法二
class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* pNode) {
        if(pNode == nullptr){
            return nullptr;
        }
        TreeLinkNode* tmp1 = pNode->right;
        if(tmp1 == nullptr){
            TreeLinkNode* tmp = pNode;
            while(tmp != tmp->next->left && tmp->next!=nullptr){
                tmp = tmp->next;
            }
            return tmp->next;
        }
        while(tmp1 -> left != nullptr){
            tmp1 = tmp1->left;
        }
        return tmp1;         
    }
};

面试题九 JZ9 用两个栈实现队列

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

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

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

相关题目

用两个队列构建一个栈

class Solution
{
public:
    void push(int node) {
        if(!queue1.empty()){
            queue1.push(node);
        }else{
            queue2.push(node);
        }
    }

    int pop() {
        if(queue2.empty()){
            while(queue1.size() != 1){
                int i = queue1.front();
                queue1.pop();
                queue2.push(i);
            }
            int i = queue1.front();
            queue1.pop();
            return i;
        }else{
            while (queue2.size() != 1)
            {
                int i = queue2.front();
                queue2.pop();
                queue1.push(i);
            }
            int i = queue2.front();
            queue2.pop();
            return i;
        }
    }

private:
    queue<int> queue1;
    queue<int> queue2;
};
/*
  思路:由于队列和栈进出相反.  后进的需要先出,  每次需要把队尾的元素出队,必然要把除了队尾的元素之外的元素,先找地方(d2)保存起来.  下一次出队的时候,同样道理,.
  进队时,两个队列d1,d2的状态必然是  :  一个为空  ,  另一个装元素. 将需要进队的元素直接插入非空那个队列队尾.
  出队时,  将  非空队列的    除去最后一个元素外的所以元素   都放到另一个空队列中,在将最后一个出队...整个过程就是倒腾过来  ,  倒腾过去.  
  但是要知道  ,   两个队列是交替做临时队列的 ,  而不是说每次都用d2做临时存储,然后出队完还要把d2中的东西还给d1,这是不需要的!  因为下次你用d1做存储区不就行了么..不用还回去!!!这就是优化点!
*/

面试题十 JZ10 斐波那契数列

法一
class Solution {
public:
    int Fibonacci(int n) {
        if(n <= 1){
            return 1;
        }
        int tmp1 = 1;
        int tmp2 = 1;
        for(int i = 0; i < n-2; i++){
            int temp = tmp2;
            tmp2 = tmp1 + tmp2;
            tmp1 = temp;
        }
        return tmp2;
    }
};
//递归的程序,函数不断进出栈,时间复杂度过高
法二
//避免重复计算,空间换时间
class Solution {
public:
    int Fibonacci(int n) {
        if(n <= 1){
            return 1;
        }
        int tmp1 = 1;
        int tmp2 = 1;
        for(int i = 0; i < n-2; i++){
            int temp = tmp2;
            tmp2 = tmp1 + tmp2;
            tmp1 = temp;
        }
        return tmp2;
    }
};

面试题十一 JZ11 旋转数组的最小数字

描述
有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。
数据范围:1 \le n \le 100001≤n≤10000,数组中任意元素的值: 0 \le val \le 100000≤val≤10000
要求:空间复杂度:O(1)O(1) ,时间复杂度:O(logn)O(logn)

法一
class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        if(rotateArray.size() == 0){
            return 0;
        }
        int i = 0;
        int temp = rotateArray[0];
        while(temp <= rotateArray[i] && i <= rotateArray.size()-1 ){
            i++;            
        }
        if(i > rotateArray.size()-1){
            return temp;
        }else{
            return rotateArray[i];
        }
    }
};
法二
//二分查找的应用
class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        int p1 = 0;
        int p2 = rotateArray.size() - 1;
        
        while(p1 != p2){
            int mid = p1 + ((p2-p1) >> 1);
            if(rotateArray[mid] > rotateArray[p2]){
                p1 = mid + 1;
            }else if(rotateArray[mid] < rotateArray[p2]){
                p2 = mid;
            }else{
                p2--;
            }         
        }
        return rotateArray[p1];
        
    }
};

面试题十二 JZ12 矩阵中的路径

深度优先遍历 回溯

法一
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param matrix char字符型vector<vector<>> 
     * @param word string字符串 
     * @return bool布尔型
     */
    int vis[205][205] ,n,m;
    const int dxy[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; 
    string word1;
    bool hasPath(vector<vector<char> >& matrix, string word) {
        n = matrix.size(), m = matrix[0].size();
        memset(vis, 0, sizeof(vis));
        // write code here
        word1 = word;
        if(matrix.size() == 0 || matrix[0].size() == 0){
            return false;
        }
        if(word.size() == 0){
            return true;
        }
        string::iterator it = word1.begin()+1;
        for(int i = 0; i < n; i++){
            for(int j = 0; j < m; j++){
                if(matrix[i][j] == word[0]){
                    if(find(matrix, it, i, j)){
                        return true;
                    }
                    vis[i][j] = false;
                }
            }
        }

        return false;
        
    }
    bool dfs(vector<vector<char>>& matrix, string::iterator a,int x, int y){
        if(a == word1.end()){
            return true;
        }    // 找到一条合法路径
        vis[x][y] = true;    // 标记当前位置
        for(int i = 0; i < 4; i++){
            int dx = x + dxy[i][0], dy = y + dxy[i][1];
            if(dx >= 0 && dx < n && dy >= 0 && dy < m && !vis[dx][dy] && matrix[dx][dy] == *a){
                if(dfs(matrix,a+1, dx, dy)) return true;
                vis[dx][dy] = false;    // 回溯
            }
        }
        return false;
    }
    bool find(vector<vector<char>>& matrix, string::iterator a, int row, int col){
        if(a == word1.end()){
            return true;
        }
        vis[row][col] = true;
        for(int i = 0; i < 4; i++){
            int dx = row + dxy[i][0];
            int dy = col + dxy[i][1];
            if(dx >= 0 && dx < n && dy >= 0 && dy < m && !vis[dx][dy] && matrix[dx][dy] == *a){
                if(find(matrix,a+1,dx,dy)){ //这个地方写成++a就会出问题,还没搞清楚
                    return true;
                }
                
                vis[dx][dy] = false;
            }
        }

        return false;   
    }
    
};
对比一下别人答案找找差距
class Solution {
private:
    static constexpr int maxn = 205;    // 根据题目给出的信息,矩阵最大为200 * 200
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param matrix char字符型vector<vector<>> 
     * @param word string字符串 
     * @return bool布尔型
     */
    int vis[maxn][maxn], n, m;    // n为矩阵行数, m为矩阵列数

    const int dxy[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};    // 向四个方向移动的偏移量

    bool hasPath(vector<vector<char>>& matrix, string word) {
        n = matrix.size(), m = matrix[0].size();
        memset(vis, 0, sizeof(vis));    // 初始化
        for(int i = 0; i < n; i++){
            for(int j = 0; j < m; j++){
                if(matrix[i][j] == word[0]){    // 找到入口点
                    if(dfs(matrix, i, j, 1, word)){
                        return true;
                    }
                    vis[i][j] = false;    // 记得恢复状态
                }
            }
        }
        return false;
    }

    bool dfs(vector<vector<char>>& matrix, int x, int y, int p, string& word){
        if(p == word.size()) return true;    // 找到一条合法路径
        vis[x][y] = true;    // 标记当前位置
        for(int i = 0; i < 4; i++){
            int dx = x + dxy[i][0], dy = y + dxy[i][1];
            if(dx >= 0 && dx < n && dy >= 0 && dy < m && !vis[dx][dy] && matrix[dx][dy] == word[p]){
                if(dfs(matrix, dx, dy, p + 1, word)) return true;
                vis[dx][dy] = false;    // 回溯
            }
        }
        return false;
    }
};

JZ13 机器人的运动范围

法一

深度优先的遍历 dfs

class Solution {
public:
    int vis[100][100],m,n;
    int nums;
    int mv[4][2] = {{-1,0}, {1,0}, {0,-1}, {0,1}};
    int movingCount(int threshold, int rows, int cols) {
        m = rows;
        n = cols;
        
        memset(vis, 0, sizeof(vis));
        find(threshold, 0, 0);
        return nums;     
    }
    void find(int threshold, int row, int col){
        if(vis[row][col] == 0){
            if((row/10+row%10 + col/10+col%10) <= threshold){
                vis[row][col] = 1;
                nums++;
                for(int i = 0; i < 4; i++){
                    int dx = row + mv[i][0];
                    int dy = col + mv[i][1];
                    if(dx >= 0 && dy >= 0 && dx < m && dy < n && vis[dx][dy] == 0){
                        find(threshold, dx, dy);
                    }
                }           
            }else{
                vis[row][col] = -1;
            }
        }else{
            return;
        }
        return;
    }
};
法二

广度优先 bfs (通常配合队列完成)

class Solution {
public:
    int vis[100][100],m,n;
    int mv[4][2] = {{-1,0}, {1,0}, {0,-1}, {0,1}};
    int nums = 1;
    
    int movingCount(int threshold, int rows, int cols) {
        vector<pair<int, int>> v;
        vector<pair<int, int>> v_temp;
        v.push_back(make_pair(0,0));
        memset(vis, 0, sizeof(vis));
        vis[0][0] = 1;
        while(!v.empty()){
            for(int i = 0; i < v.size(); i++){
                int x = v[i].first;
                int y = v[i].second;
                for(int j = 0; j < 4; j++){
                    int dx = x + mv[j][0];
                    int dy = y + mv[j][1];
                    if(dx >= 0 && dy >= 0 && dx < rows && dy < cols && dx/10 + dx%10 + dy/10 + dy%10 <= threshold && vis[dx][dy] == 0){
                        vis[dx][dy] = 1;
                        nums++;
                        v_temp.push_back(make_pair(dx, dy));
                    }else if(dx >= 0 && dy >= 0 && dx < rows && dy < cols && dx/10 + dx%10 + dy/10 + dy%10 > threshold && vis[dx][dy] == 0){
                        vis[dx][dy] = -1;
                    }else{
                        continue;
                    }         
                }
            }
            v.clear();
            v.assign(v_temp.begin(), v_temp.end());
            v_temp.clear();
        }
        return nums;
    }
};

//用队列改进后的代码
class Solution {
public:
    int vis[100][100],m,n;
    int mv[4][2] = {{-1,0}, {1,0}, {0,-1}, {0,1}};
    int nums = 1;
    
    int movingCount(int threshold, int rows, int cols) {
        queue<pair<int, int>> q;
        q.push(make_pair(0,0));
        memset(vis, 0, sizeof(vis));
        vis[0][0] = 1;
        while(!q.empty()){
            int x = q.front().first;
            int y = q.front().second;
            q.pop();
            for(int j = 0; j < 4; j++){
                int dx = x + mv[j][0];
                int dy = y + mv[j][1];
                if(dx >= 0 && dy >= 0 && dx < rows && dy < cols && dx/10 + dx%10 + dy/10 + dy%10 <= threshold && vis[dx][dy] == 0){
                    vis[dx][dy] = 1;
                    nums++;
                    q.push(make_pair(dx, dy));
                }else if(dx >= 0 && dy >= 0 && dx < rows && dy < cols && dx/10 + dx%10 + dy/10 + dy%10 > threshold && vis[dx][dy] == 0){
                        vis[dx][dy] = -1;
                }else{
                    continue;
                }         
            }
        }     
        return nums;
    }
};

JZ14 剪绳子

动态规划

class Solution {
public:
    int f[60];
    int cutRope(int number) {
        if (number == 2) {
            return 1;
        }
        else if (number == 3) {
            return 2;
        }
        //n为2,3的时候特殊处理,因为这时候不分的是最大,3之后不分的就都比不分的大了,因为
        //最少得分一刀
        memset(f, 0, sizeof(f));
        f[0] = 1;
        int i = 1;
        while(i <= number){
            f[i] = i;
            for(int j = 1; j <= i/2; j++){
                if(f[j]*f[i-j] > f[i]){
                    f[i] = f[j]*f[i-j];
                }   
            }
            i++;
        }
        return f[number];
    }
};

JZ15 二进制中1的个数

位运算 (非常不熟悉) 位运算比乘除快得多

class Solution {
public:
     int  NumberOf1(int n) {
         int count = 0;
         unsigned int flag = 1;
         while(n){
            if(n & 1){
                count++;
            }
            n = n >> 1;
         }
         return count;
     }
};
//这种情况在n为负数的情况下,右移n会补1,会造成死循环
//所以下面采用左移一个flag的方式,避免了n的移动
class Solution {
public:
     int  NumberOf1(int n) {
         int count = 0;
         unsigned int flag = 1;
         while(n){
            if(n & 1){
                count++;
            }
            n = n >> 1;
         }
         return count;
     }
};
//上面方式循环次数等于位数,下面的方法有几个1就循环几次
//利用 n 和 n-1 按位与后 消除最右边一个1的原理
class Solution {
public:
     int  NumberOf1(int n) {
         int count = 0;
         while(n){
            ++count;
            n = (n-1) & n;
         }
         return count;
     }
};

JZ16 数值的整数次方

二分的递归 不要傻傻的递归

class Solution {
public:
    double Power(double base, int exponent) {
        if(exponent == 0){
            return 1;
        }
        if(exponent == 1){
            return base;
        }
        double result;
        //一定要注意,不要习惯性的设置为int类型
        result = Power(base, abs(exponent) >> 1);
        result *= result;
        if(exponent & 1 == 1){
            result *= base;
        }
        if(exponent < 0){
            double a = 1;
            return a/result;
        }
        return result;
    }
};

JZ17 打印从1到最大的n位数

对于这种看起来简单的问题,更应该小心

题目一

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param n int整型 最大位数
     * @return int整型vector
     */
    vector<int> printNumbers(int n) {
        // write code here
        int numbers = 1;
        int i = 0;
        while(i++ < n){
            numbers *= 10;
        }
        vector<int> v;
        for(int i = 1; i < numbers; i++){
            v.push_back(i);
        }
        return v;
    }
};
//若要考虑大数情况,常用方法是用字符串或者数组代表大数
````c++
bool Increment(char* number){
    bool isOverflow = false; //判断是否超过
    int nTakeOver = 0; //进位判断
    int nLength = strlen(number); 
    for(int i = nLength - 1; i >= 0; --i){ //从最后一位开始
        int nSum = number[i] - '0' + nTakeOver; //记录当前位的数值(包括低位进位)
        if(i == nLength - 1){ //最后一位直接++
            nSum ++; 
        }
        if(nSum >= 10){ //若需要进位
            if(i == 0){ //为第1位时,溢出
                isOverflow = true;
            }else{ //进位,nSum归零,进位标志设为1
                nSum -= 10;
                nTakeOver = 1;
                number[i] = '0' + nSum;
            }
        }else{ //不需要进位
            number[i] = '0' + nSum;
            break;
        }
    }
    return isOverflow ;
}
//打印数字,前面为0时不能打印出来
void PrintNumber(const char* number){
    int n = strlen(number);
    bool isnotzero = false;
    for(int i = 0; i < n; i++){
        if(number[i] != '0'){
            isnotzero = true;
        }
        if(isnotzero){
            cout << number[i];
        }
    }
    cout << endl;

}
void PrintToMaxOfDigits(int n){
    if(n <= 0){
        return;
    }
    char* number = new char[n+1];
    memset(number, '0', n);
    number[n] = '\0'; //右斜零 
    while(!Increment(number)){
        PrintNumber(number);
    }
    delete[] number; //一定记得释放空间
}

扩展题目二

实现大数的加法,给定两个字符串形式的非负整数 num1和num2 ,计算它们的和。

string bigsum(string s,string m){
    int takeover = 0;
    int nlength = max(s.size(),m.size());

    string output;
    int i = s.size()-1;
    int j = m.size()-1; 
    //此处的条件判断非常巧妙
    while(i>=0 || j>=0 || takeover){
        int num1 = i >= 0 ? s[i] - '0' : 0;
        int num2 = j >= 0 ? m[j] - '0' : 0;
        int sum = num1 + num2 + takeover;
        takeover = sum /10;
        sum = sum %10;
        output.push_back(sum + '0');
        i--;j--;
    }
    reverse(output.begin(),output.end());
    return output;
}
//若要考虑负数呢?

JZ18 删除链表的节点

法一

遍历前一个节点

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param head ListNode类 
     * @param val int整型 
     * @return ListNode类
     */
    ListNode* deleteNode(ListNode* head, int val) {
        // write code here
        ListNode* temp = new ListNode(-1); //切记要用new的形式给指针赋值,否则会导致错误
        temp->next = head;
        ListNode* p = temp;
        //避免了当相同节点为头节点时,返回头节点导致的错误
        while(temp->next != nullptr && temp != nullptr){
            if(temp->next->val == val){
                temp->next = temp->next->next;
                break;
            }
            temp = temp->next;
        }
//         if(head->val == val){
//             return head->next;
//         }
        return p->next;
    }
};
法二

把下一个节点的内容复制到需要删除的节点上

class Solution {
public
    ListNode* deleteNode(ListNode* head, int val) {
        // write code here
        ListNode* temp = head;
        while(temp->val != val && temp != nullptr){
            temp = temp->next;
        }
        if(temp == nullptr){
            return head;
        }
        temp -> val = temp->next->val;
        temp -> next = temp->next->next;
        return head;
    }
};

扩展题目 JZ76 删除链表中重复的结点

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
//保存重复元素出现的位置
class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead) {
        int a[1000];
        memset(a,-1,sizeof(a));
         
        ListNode* temp = pHead;
        int num = 0;
        vector<int> v;
         
        while(temp != nullptr){
            if(a[temp->val] != -1){
                v.push_back(a[temp->val]);
                v.push_back(num);
                num++;
                temp = temp -> next;
                continue;
            }
            a[temp->val] = num;
            num++;
            temp = temp -> next;           
        }
        ListNode* temp1 = new ListNode(-1);
        ListNode* temp2 = temp1;
         
        temp1->next = pHead;
        int n = 0;
        while(temp1->next != nullptr && temp1 != nullptr){
            if(count(v.begin(), v.end(), n)){
                temp1->next = temp1->next->next;
                n++;
                continue;
            }
            n++;
            temp1 = temp1->next;
        }
        return temp2->next;
 
    }
};
//双指针遍历
class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead) {
        ListNode* p1 = new ListNode(-1);
        ListNode* p2 = new ListNode(-1);
        p1->next = pHead;
        
        ListNode* p3 = p1;
        int a = 0;
        while(p1->next != nullptr){
            p2 = p1->next;
            while(p2->next != nullptr){
                if(p1->next->val == p2->next->val){
                    p2->next = p2->next->next;
                    a = 1;
                }
                else{
                    p2 = p2->next;
                }
            }
            if(a == 1){
                p1->next = p1->next->next;
                a = 0;
            }else{
                p1 = p1->next;
            }
            
        }
        return p3->next;
    }
};
//递归解法
public class Solution {
    public ListNode deleteDuplication(ListNode pHead) {
        // 递归出口:当「输入节点为空」或者「不存在下一节点」,直接返回
        if (pHead == null || pHead.next == null) return pHead;

        if (pHead.val != pHead.next.val) {
            // 若「当前节点」与「下一节点」值不同,则当前节点可以被保留
            pHead.next = deleteDuplication(pHead.next);
            return pHead;
        } else {
            // 若「当前节点」与「下一节点」相同,需要跳过「值相同的连续一段」
            ListNode tmp = pHead;
            while (tmp != null && tmp.val == pHead.val) tmp = tmp.next;
            return deleteDuplication(tmp);
        }
    }
}

JZ19 正则表达式匹配

重点在于判断匹配的递归性质的掌握
https://blog.nowcoder.net/n/1aaa31fb6db943bbb92e9ca3f6bfb982

//递归的解法
class Solution {
public:
    bool match(string str, string pattern) {
        if(pattern.empty()) return str.empty();
        if(pattern.size() >= 2 && pattern[1] == '*') {
            if(!str.empty() && (str[0] == pattern[0] || pattern[0] == '.')){
                return match(str.substr(1,str.length()), pattern) ||
                       match(str, pattern.substr(2,pattern.length()));
            }else{
                return match(str, pattern.substr(2,pattern.length()));
            }
        }
        if(!str.empty() && (str[0] == pattern[0] || pattern[0] == '.')){
            return match(str.substr(1,str.length()), pattern.substr(1,pattern.length()));
        }
        return false;
    }
};
//动态规划
class Solution {
public:
    bool match(string str, string pattern) {
        bool dp[20][30];
        memset(dp,false,sizeof(dp));
        
        int m = str.length();
        int n = pattern.length();
        dp[0][0] = true;
        for(int i=1;i<=n;i++){
            dp[i][0]=false;//非空文本串与空模式串一定不匹配    
        }       
        
        for(int i = 0; i <= m; i++){
            for(int j = 0; j <= n; j++){
                if (j <= n-1 && pattern[j] == '*') continue;
                if(pattern[j-1] == '*'){
                    dp[i][j] = (j>=2 && dp[i][j-2]) || ((i >= 1 && dp[i-1][j])
                               && (str[i-1] == pattern[j-2] || pattern[j-2] == '.'));
                }
                if (i - 1 >= 0 && pattern[j-1] != '*') {
                    dp[i][j] = dp[i - 1][j - 1] && (str[i-1] == pattern[j-1] || pattern[j-1] == '.');
                }
            }
        }
        return dp[m][n];
    }
};

JZ20 表示数值的字符串

https://leetcode-cn.com/problems/biao-shi-shu-zhi-de-zi-fu-chuan-lcof/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值