剑指Offer_week_2

41.和为S的连续正数序列

image-20220912231341431

使用双指针,形成滑动窗口

class Solution {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        if (0 == sum) return vector<vector<int>>();
        
        vector<vector<int>> ans;
        int left = 1, right = 2;
        
        while (left < right) {
            // 根据等差数列求和公式,n * (a1 + an) / 2
            int sumTmp = (right - left + 1) * (left + right) / 2;
            if (sumTmp == sum) {
                vector<int> tmp;
                for (int i = left; i <= right; i++) {
                    tmp.push_back(i);
                }
                ans.push_back(tmp);
                left++;
            } else if (sumTmp > sum) {
                left++;
            } else {
                right++;
            }
        }
        return ans;
    }
};

42.和为S的两个数字

image-20220912232549792

同样的思路,双指针

class Solution {
public:
    vector<int> FindNumbersWithSum(vector<int> array,int sum) {
        vector<int> ans;
        
        int left = 0, right = array.size() - 1;
        
        while (left < right) {
            int sumTmp = array[left] + array[right];
            
            if (sum == sumTmp) {
                ans.push_back(array[left]);
                ans.push_back(array[right]);
                return ans;
            } else if (sumTmp < sum) {
                left++;
            } else {
                right--;
            }
        }
        return vector<int>();
    }
};

43.左旋转字符串

image-20220912233813996

class Solution {
public:
    string LeftRotateString(string str, int n) {
        int len = str.size();
        if (len == 0) return str;
        
        int index = n % len;
        str += str;
        return str.substr(index, len);
    }
};

44.反转单词序列

image-20220913155748005

class Solution {
public:
    string ReverseSentence(string str) {
        string ans = "", tmp = "";
        for (int i = 0; i < str.size(); i++) {
            if (str[i] == ' ') {
                ans = " " + tmp + ans;
                tmp = "";
            } else {
                tmp += str[i];
            }
        }
        if (tmp.size()) {
            ans = tmp + ans;
        }
        
        return ans;
    }
};

45.扑克牌顺子

image-20220913162242020

class Solution {
public:
    bool IsContinuous( vector<int> numbers ) {
        sort(numbers.begin(), numbers.end());
        
        int zero_num = 0;
                
        for (int i = 0; i < numbers.size() - 1; i++) {
            if (0 == numbers[i]) zero_num++;
            else if (numbers[i] == numbers[i + 1]) return false;
        }
        
        return numbers[4] - numbers[zero_num] < 5;
    }
};

46.圆圈中的数字

image-20220913165354470

约瑟夫环,这老哥讲的很好

约瑟夫环——公式法(递推公式)_陈浅墨的博客-CSDN博客_约瑟夫环公式

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

47.求1+2+3+…+n

image-20220914225653033

利用 短路与 实现递归,直到n = 0时,可以判断递归结束

class Solution {
public:
    int Sum_Solution(int n) {
        (n += Sum_Solution(n - 1)) && n;
        
        return n;
    }
};

48.不用加减乘除做加法

image-20220914232226461

思路,利用加法器的原理实现

异或运算可以得到,两数之和没有进位

逻辑与,可以得到进位

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

49.把字符串转换成整数

image-20220914234958161

主要在于边界处理

class Solution {
public:
    int StrToInt(string str) {
        int len = str.size();
        
        if (0 == len) return 0;
        int i = 0;
        while (i < len && str[i] == ' ') i++;    // 排除字符串前面空格
        if (i == len) return 0;        // 全部为 空格
        if (!isdigit(str[i]) && str[i] != '-' && str[i] != '+') return 0;
        
        bool flag = str[i] == '-' ? true : false;
        i = isdigit(str[i]) ? i : i+1;
        
        long long ans = 0L;
        
        while (i < len && isdigit(str[i])) {
            ans = ans * 10 + (str[i++] - '0');
            
            if (!flag && ans > INT_MAX) {
                ans = INT_MAX;
                break;
            }
            if (flag && ans > 1 + INT_MAX) {
                ans = 1L + INT_MAX;
                break;
            }
        }
        
        if (i != len) return 0;
        
        return !flag ? static_cast<int>(ans) : static_cast<int>(-ans);
    }
};

50.数组中重复的数字

image-20220915095034981

哈希加循环

class Solution {
public:
	bool duplicate(int numbers[], int length, int* duplication) {
        unordered_map<int, int> umap;
         
        for (int i = 0; i < length; i++) {
            umap[numbers[i]]++;
            if (2 == umap[numbers[i]]) {
                *duplication = numbers[i];
                return true;
            }
        }
         
        return false;
    }
};

空间优化

class Solution {
public:
	bool duplicate(int numbers[], int length, int* duplication) {
        vector<bool> vec(length, false);
         
        for (int i = 0; i < length; i++) {
            if (!vec[numbers[i]]) {
                vec[numbers[i]] = true;
            } else {
                *duplication = numbers[i];
                return true;
            }
        }
         
        return false;
    }
};

原地判断

将数组的元素看做数组的下标,将出现过的下标的元素加上数组的长度,使其大于数组长度

class Solution {
public:
    bool duplicate(int numbers[], int length, int* duplication) {
 
        for (int i = 0; i < length; i++) {
            int index = numbers[i] % length;
             
            if (numbers[index] >= length) {
                *duplication = index;
                return true;
            }
            numbers[index] += length;
        }
         
        return false;
    }
};

51.构建乘积数组

image-20220915235145660

emmm… 怎么说

class Solution {
public:
    vector<int> multiply(const vector<int>& A) {
        if (1 == A.size()) return vector<int>();
        
        int num = 1, zero = 0;
        for (int n : A) {
            if (n == 0) {
                zero++;		// 记录 0 出现的个数
                continue;
            }
            num *= n;		// 计算除了0之外,其余所有数的乘积
        }
        
        vector<int> B;
        for (int i = 0; i < A.size(); i++) {
            if (zero == 0) {
                B.push_back(num / A[i]);
            } else if (zero == 1 && A[i] == 0) {
                B.push_back(num);
            } else if (zero > 1){
                B.push_back(0);
            } else {
                B.push_back(0);
            }
        }
        
        return B;
    }
};

这个好

class Solution {
public:
    vector<int> multiply(const vector<int>& A) {
        if (1 == A.size()) return vector<int>();
        
        vector<int> B(A.size(), 1);
        
        for (int i = 1; i < A.size(); i++) {    
            B[i] = B[i - 1] * A[i - 1];    // 从左到右计算 数组元素前面的所有累乘积
        }
        
        int tmp = 1;
        
        for (int i = A.size() - 1; i >= 0; i--) {    
            B[i] *= tmp;
            tmp *= A[i];    // 从右到左计算 数组元素后面的所有累乘积
        }
        
        return B;
    }
};

52.正则表达式匹配

image-20220916092822426

单独看思路是理解不了的,照着敲一遍思路就清晰了

class Solution {
public:
    bool match(string str, string pattern) {
        char a[100], b[100];
        strcpy(a,str.c_str());
        strcpy(b,pattern.c_str());
         
        return match(a, b);
    }
    
    bool match(char* str, char* pattern) {
        // 两种特殊情况
        if (*str == '\0' && *pattern == '\0') return true;
        if (*str != '\0' && *pattern == '\0') return false;
        
        // if the next character in pattern is not '*'
        if (*(pattern + 1) != '*') {	// 只需简单比较,然后继续比较下一个字符
            if (*str == *pattern || (*str != '\0' && *pattern == '.')) {
                return match(str + 1, pattern + 1);
            } else {
                return false;
            }
        } else {    // if the next character in pattern is '*'
            if (*str == *pattern || (*str != '\0' && *pattern == '.')) {
                // 当‘*’比配0个,直接比较pattern的下一个字符
                // 当‘*’匹配1个或多个,str需要移动,pattern不需要移动
                return match(str, pattern + 2) || match(str + 1, pattern);
            } else {
                // 没有匹配到的话,就当做匹配了0个
                return match(str, pattern + 2);
            }
        }
    }
};

53.表示数字的字符串

image-20220916221005753

难,太难了

class Solution {
  public:
    int index = 0;
    // 有符号数判断
    bool integer(string str) {
        if (index < str.length() && str[index] == '-' || str[index] == '+') index++;
        // 然后需要判断后面是否为一个无符号整数
        return unsigned_integers(str);
    }

    // 无符号数判断,直到遇见非数字停止
    bool unsigned_integers(string str) {
        int tmp = index;
        while (index < str.size() && str[index] >= '0' && str[index] <= '9') index++;
        return index > tmp;
    }

    bool isNumeric(string str) {
        int len = str.size();
        if (0 == len) return false;

        // 去除前面的空格
        while (index < len && str[index] == ' ') index++;
        // 去除后面的空格
        while (len >= 1 && str[len - 1] == ' ') len--;
        // 全是空格
        if (len < index) return false;

        // 判断是否为有符号的整数
        bool flag = integer(str);
        // 如果有小数点,需要判断后面是不是一个无符号整数,遇到非数字停止
        if (index < len && str[index] == '.') {
            index++;
            flag = unsigned_integers(str) || flag;
        }

        // 如果有e
        if (index < len && str[index] == 'e' || str[index] == 'E') {
            index++;
            flag = flag && integer(str);
        }
        // 是否字符串遍历结束
        return flag && (index == len);
    }
};

54.字符流中第一个不重复的字符

image-20220916221930622

不要被它的返回值迷惑,就返回第一个不重复的字符即可

使用哈希

class Solution
{
public:
    //Insert one char from stringstream
    void Insert(char ch) {
        vec.push_back(ch);
        umap[ch]++;
    }

    //return the first appearence once char in current stringstream
    char FirstAppearingOnce() {
        if (vec.empty()) return '#';

        for (char c : vec) {
            if (umap[c] == 1) return c;
        }

        return '#';
    }

    vector<int> vec;
    unordered_map<char, int> umap;
};

使用库函数count

class Solution
{
public:
    //Insert one char from stringstream
    void Insert(char ch) {
        vec.push_back(ch);        
    }
  
    //return the first appearence once char in current stringstream
    char FirstAppearingOnce() {
        if (vec.empty()) return '#';
  
        for (char c : vec) {
            if (count(vec.begin(), vec.end(), c) == 1) return c;
        }
  
        return '#';
    }
  
    vector<int> vec;
};

55.链表中环的入口结点

image-20220916223253579

这里不要被示例3迷惑了,即使是示例3这样的,在pHead中是自身形成一个环

所以哈希记录的方法是可以的

class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead) {
        if (!pHead) return nullptr;
        unordered_map<ListNode*, int> umap;

        while (pHead) {
            umap[pHead]++;
            if (umap[pHead] == 2) return pHead;
            pHead = pHead->next;
        }
        
        return nullptr;
    }
};

采用快慢双指针

1.slow指针一次走一步,fast指针一次走两步,两者第一次会在C结点处相遇

⒉.slow指针走的路径为A-B-C,距离为X+Y,则fast指针走的距离为2(X+Y),路径为A-B-C-B-C

3.可以得出C-B的距离为X,也就是A-B的距离,此时快慢指针同时指向环的入口结点

image-20220916225616280

class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead) {
        if (!pHead) return nullptr;
        ListNode* slow = pHead;
        ListNode* fast = pHead;

        while (fast != nullptr && fast->next != nullptr) {
            fast = fast->next->next;
            slow = slow->next;

            // 找到第一次相遇的结点
            if (slow == fast) break;
        }
        
        // 若是快指针指向nullptr,代表没有环
        if (!fast || !fast->next) return nullptr;
        
        // 将快指针指向头结点,快慢指针谁都可以,这里用快指针
        fast = pHead;
        
        // 根据计算出的值,再一次相遇结点为入口结点
        while (fast != slow) {
            fast = fast->next;
            slow = slow->next;
        }

        return fast;
    }
};

56.删除链表中重复的结点

image-20220916231505523

直接遍历比较删除

class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead) {
        if (!pHead) return nullptr;

        ListNode* root = new ListNode(0);
        root->next = pHead;

        ListNode* cur = root;

        while (cur->next && cur->next->next) {
            if (cur->next->val == cur->next->next->val) {
                int tmp = cur->next->val;

                while (cur->next && cur->next->val == tmp) {
                    cur->next = cur->next->next;
                }
            } else {
                cur = cur->next;
            }
        } 

        return root->next;
    }
};

57.二叉树的下一个结点

image-20220917210741738

这个思路很简单,就是通过传入的结点找到根节点,然后中序遍历,找到传入结点的下一个结点即可

class Solution {
public:
    vector<TreeLinkNode*> vec;

    TreeLinkNode* GetNext(TreeLinkNode* pNode) {
        if (!pNode) return nullptr;

        TreeLinkNode* root = pNode;
        while (root->next) root = root->next;

        LDR(root);

        for (int i = 0; i < vec.size() - 1; i++) {
            if (vec[i] == pNode) {
                return vec[i + 1];
            }
        }

        return nullptr;
    }

    void LDR(TreeLinkNode* root) {
        if (!root) return ;

        LDR(root->left);
        vec.push_back(root);
        LDR(root->right);
    }
};

法二:直接分类寻找,画图就很好理解了

class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* pNode) {
        // 有右子树
        if (pNode->right) {
            TreeLinkNode* rchild = pNode->right;
            // 找到该右子树的最左下节点为返回值
            while (rchild->left) rchild = rchild->left;
            return rchild;
        }
		
        // 没有右子树且当前节点是其父结点的左子节点
        if (pNode->next && pNode->next->left == pNode) {
            // 返回其父节点
            return pNode->next;
        }
		
        // 没有右子树且当前节点是其父节点的右子节点
        if (pNode->next) {
            TreeLinkNode* ppar = pNode->next;
			// 沿着左上一直找到当前结点是其父节点的左子节点为止
            while (ppar->next && ppar->next->right == ppar) ppar = ppar->next;
			// 返回其父节点
            return ppar->next;
        }

        return nullptr;
    }
};

58.对称的二叉树

image-20220917213959125

class Solution {
public:
    bool isSymmetrical(TreeNode* pRoot) {
        if (!pRoot) return true;

        return isEqual(pRoot->left, pRoot->right);
    }

    bool isEqual(TreeNode* node1, TreeNode* node2) {
        if (!node1 && !node2) return true;
        if (!node1 || !node2) return false;
        if (node1->val != node2->val) return false;

        return isEqual(node1->left, node2->right) && isEqual(node1->right, node2->left);
    }
};

59.按之字形顺序打印二叉树

image-20220917224215663

利用双向队列实现

通过level代表当前的层高,来判断该层遍历的方向,

​ 默认根节点为第一层,从第二层开始(偶数层),从右往左遍历

​ 然后下一层(奇数层)从左往右遍历,以此类推

刚开始出现一个问题就是如何知道该层需要循环几次,后来发现可以通过上一层添加的子节点数来判断

class Solution {
  public:
    vector<vector<int> > Print(TreeNode* pRoot) {
        if (!pRoot) return vector<vector<int>>();

        vector<vector<int>> ans;
        int level = 1;		// 从第一层开始

        deque<TreeNode*> dq;
        dq.push_back(pRoot);

        while (!dq.empty()) {
            vector<int> tmp;            // 存放每一层的元素
            int rowLen = dq.size();     // 该层有多少个元素,就要循环多少次
            while (rowLen--) {
                if (0 == level % 2) {   // 当为偶数层,从右往左添加
                    TreeNode* node = dq.back();
                    dq.pop_back();

                    tmp.push_back(node->val);

                    // 因为添加到tmp容器的方向不同,所以入队的方向不同
                    if (node->right) dq.push_front(node->right);
                    if (node->left) dq.push_front(node->left);
                } else {   // 当为奇数层,从左往右添加
                    TreeNode* node = dq.front();
                    dq.pop_front();

                    tmp.push_back(node->val);

                    if (node->left) dq.push_back(node->left);
                    if (node->right) dq.push_back(node->right);
                }
            }

            ans.push_back(tmp);
            level++;
        }
        return ans;
    }
};

60.把二叉树打印成多行

image-20220918145609606

class Solution {
public:
    vector<vector<int> > Print(TreeNode* pRoot) {
        if (!pRoot) return vector<vector<int>>();

        vector<vector<int>> ans;
        queue<TreeNode*> q;
        q.push(pRoot);

        while (!q.empty()) {
            vector<int> tmp;
            int rowSize = q.size();
            while (rowSize--) {
                TreeNode* node = q.front();
                q.pop();

                tmp.push_back(node->val);

                if (node->left) q.push(node->left);
                    
                if (node->right) q.push(node->right);        
            }
            ans.push_back(tmp);
        }
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值