• LeetCode刷题记录(二)


    剑指 Offer 17. 打印从1到最大的n位数

    解法一:

    缺点:未考虑到大数的情况,当数字较大时,int不够用

    class Solution {
    public:
        vector<int> printNumbers(int n) {
            //1.先判断n为几位数
            n = pow(10,n);
            vector<int>v;
            for(int i=1;i<n;i++){
                v.push_back(i);
            }
            return v;
        }
    };
    

    解法二:考虑大数的情况

    大数加法的模板

    string Add(string a,string b) {
            string ans;
            int numA[MAX_SIZE]={0},numB[MAX_SIZE]={0};//用来保存数字
            int lenA = a.size();
            int lenB = b.size();
    
            for(int i=0;i<lenA;i++){
                //字符串转数字
                numA[lenA-1-i] = a[i] - '0';//从个位开始转换
            }
            for(int i=0;i<lenB;i++){
                numB[lenB-1-i] = b[i] - '0';
            }
            int lenMax = lenA>lenB ? lenA:lenB;
            // 从个位开始计算
            for(int i=0;i<lenMax;i++){
                numA[i]+=numB[i];
                numA[i+1]+=numA[i]/10;//判断是否有进位
                numA[i]%=10;
            }
            //去除前置0
            if(!numA[lenMax])   lenMax--;	
            for(int i= lenMax;i>=0;i--){
                ans+=numA[i]+'0';	//保存到结果中
            }
            return ans; 
        }
    

    用字符串模拟数字加法:流程为,先判断是否有溢出,没有溢出就加1并保存该值

    class Solution {
    public:
        vector<int> output;
        vector<int> printNumbers(int n) {
            // 以下注释的前提:假设 n = 3
            if(n <= 0) return vector<int>(0);
            string s(n, '0'); // s最大会等于999,即s的长度为n
            while(!overflow(s)) inputNumbers(s);// 当没有溢出999时
            return output;	
        }
        bool overflow(string& s)	//判断溢出情况
        {
            // 本函数用于模拟数字的累加过程,并判断是否越界(即 999 + 1 = 1000,就是越界情况)
            bool isOverFlow = false;
            int carry = 0; // carry表示进位
            for(int i=s.length()-1; i>=0; --i)	//从高到低
            {
                int current = s[i] - '0' + carry; // current表示当前这次的操作
                if(i == s.length() - 1) current ++; // 如果i此时在个位,current执行 +1 操作
                if(current >= 10)	//有进位了
                {
                    // 假如i已经在最大的那一位了,而current++之后>=10,说明循环到头了,即999 + 1 = 1000
                    if(i == 0) isOverFlow = true;
                    else
                    {
                        // 只是普通进位,比如current从9变成10
                        carry = 1;
                        s[i] = current - 10 + '0'; 
                    }
                }
                else
                {
                    // 如果没有进位,更新s[i]的值,然后直接跳出循环,这样就可以回去执行inputNumbers函数了,即往output里添加元素
                    s[i] = current + '0';
                    break;
                }
            }
            return isOverFlow;
        }
        void inputNumbers(string s)
        {
            // 本函数用于循环往output中添加符合传统阅读习惯的元素。比如001,我们会添加1而不是001。
            bool isZero = true; // 判断是否是0,比如001前面的两个0
            string temp = "";
            for(int i=0; i<s.length(); ++i)
            {
                if(isZero && s[i] != '0') isZero = false;
                if(!isZero) temp += s[i];   //去除0后,将该值保存
            }
            output.push_back(stoi(temp));//stoi	string to integer
        }
    };
    

    剑指 Offer 18. 删除链表的节点

    //主要是要考虑到头结点也有可能要被删除这种情况,所有再开辟一个新结点指向头结点,这样循环向后一个判断即可可以方便不少
    class Solution {
    public:
        ListNode* deleteNode(ListNode* head, int val) {
            ListNode *pre = new ListNode(0);
            pre->next = head;
            head = pre;
            while(pre->next!=NULL){
                if(pre->next->val==val){
                    pre->next = pre->next->next;
                    return head->next;
                }
                pre = pre->next;
            }
            return head->next;
        }
    };
    
    

    剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

    第一时间我想到的是快速排序中的类似partition函数这样做分割,但关键地方有点卡住了,就先暴力过了一边

    法一:暴力法

    class Solution {
    public:
        vector<int> exchange(vector<int>& nums) {
            vector<int>res;
            for(auto it=nums.begin();it!=nums.end();++it){
                if( (*it)%2!=0 ){
                    res.push_back((*it));
                }
            }
            for(auto it=nums.begin();it!=nums.end();++it){
                if( (*it)%2==0 ){
                    res.push_back((*it));
                }
            }
            return res;
        }
    };
    

    法二:双指针法

    class Solution {
    public:
        vector<int> exchange(vector<int>& nums) {
            int i=0;
            int j=nums.size()-1;
            while(i < j){
                while(i<j && (nums[i] & 1) == 1) i++;	//奇数
                while(i<j && (nums[j] & 1) == 0) j--;	//偶数
                swap(nums[i],nums[j]);
            }
            return nums;
        }
    };
    

    法三.快慢指针

    思路是让一个先走,而每当快指针指向的是奇数时,将其与low交换(即向左边靠)

    class Solution {
    public:
        vector<int> exchange(vector<int>& nums) {
            int low = 0, fast = 0;
            while (fast < nums.size()) {
                if (nums[fast] & 1) {	//当快指针指向的是奇数时
                    swap(nums[low], nums[fast]);
                    low ++;
                }
                fast ++;
            }
            return nums;
        }
    };
    
    

    剑指 Offer 22. 链表中倒数第k个节点

    没什么难度,直接双指针就出来了,倒数第K个,就让一个先走K步,再两个指针一起走,当先走的到达链尾时,后走的正好到达倒数第K个

    class Solution {
    public:
        ListNode* getKthFromEnd(ListNode* head, int k) {
            ListNode *rear = head;
            for(int i = 0;i<k;++i){
                rear = rear->next;
                if(rear==NULL){//代表K超出范围
                    return head;
                }
            }
            while(rear){
                rear = rear->next;
                head = head->next;
            }
            return head;
        }
    };
    

    剑指 Offer 24. 反转链表

    相当简单,就是画个图,分析一下断链的情况和指针的情况就行了。

    class Solution {
    public:
        ListNode* reverseList(ListNode* head) {
            if(head==NULL)  return head;
                ListNode *p,*q;
                p = head->next;
                head->next = NULL;
                while(p!=NULL){
                    q = p->next;
                    p->next = head;
                    head = p;
                    p = q;
                }
                return head;
        }
    };
    

    方案二:递归实现

    链表的逆序可以自然想到递归,桟

    class Solution {
    public:
        ListNode* reverseList(ListNode* head) {
            return recur(head, nullptr);           // 调用递归并返回
        }
    private:
        ListNode* recur(ListNode* cur, ListNode* pre) {
            if (cur == nullptr) return pre;        // 终止条件,开始回溯
            
            //该语句会一直向下指到5
            ListNode* res = recur(cur->next, cur); // 递归后继节点
            //开始回溯时修改指针指向	5->next = 4  4->next = 3....
            cur->next = pre;                       // 修改节点引用指向
            return res;                            // 返回反转链表的头节点
        }
    };
    

    剑指 Offer 25. 合并两个排序的链表

    解法一
    class Solution {
    public:
        ListNode* mergeTwoLists(ListNode* list_1, ListNode* list_2) {
            ListNode * list = new ListNode(0),*cur =list;
            while(list_1 && list_2){
                //按递增排序
                if(list_1->val<list_2->val){
                    cur->next = list_1;
                    list_1 = list_1->next;
                }else{
                    cur->next = list_2;
                    list_2 = list_2->next;
                }
                cur = cur ->next;
            }
            cur->next = list_1 ? list_1 : list_2 ;//处理剩余的结点,因为前一个while循环一定会让一个链表到尾
            return list->next;//为什么要return list->next呢?因为一开始申请的时候申请了一个0当头结点
        }
    };
    
    解法二

    递归写法,貌似有关链表的题目,都可以用递归来写,但是有一点难理解

    class Solution {
    public:
             ListNode* mergeTwoLists(ListNode *l1, ListNode *l2) {
            
            if(l1 == NULL) return l2;   //l1到头了
            if(l2 == NULL) return l1;   //l2到头了
            if(l1->val <= l2->val) {
                l1->next = mergeTwoLists(l1->next, l2);	//l1向后
                return l1;
            } else {
                l2->next = mergeTwoLists(l1, l2->next);	//l2向后
                return l2;
            }
        }
    
    };
    

    剑指 Offer 27. 二叉树的镜像

    解法一法:

    class Solution {
    public:
        TreeNode* mirrorTree(TreeNode* root) {
            if(root == nullptr) return nullptr;	//这里是边界条件
            TreeNode * temp = root->left;
            root->left=
                mirrorTree(root->right);//根节点的左结点应该向右递归
            root->right=
                mirrorTree(temp); 	//右结点向左递归
            return root;
        }
    };
    
    解法二:

    用栈来处理

    //桟的写法
    class Solution {
    public:
        TreeNode* mirrorTree(TreeNode* root) {
            if(root == nullptr) return nullptr;//一个特殊情况的判断
            stack<TreeNode *>s;
            s.push(root);
            while(!s.empty()){//非空
                TreeNode *node = s.top();//根结点出栈	处理顺序为4,7,9,6,2,3,1
                s.pop();
                if(node->left!=NULL)    s.push(node->left);//左进桟
                if(node->right!=NULL)   s.push(node->right);
                //实现左右指针的一个交换
                TreeNode *temp = node->left;
                node->left = node ->right;
                node->right = temp;
            }
            return root;
        }
    };
    

    总结:这几天写了下这种有类似反转啊,逆置啊,感觉一般首先可以想递归,然后想桟,这种FILO的结构来处理

    剑指 Offer 28. 对称的二叉树

    大佬的写法

    class Solution {
    public:
        bool isSymmetric(TreeNode* root) {
            // 如果是空树
            if(!root)
                return true;
            else
                return isSymmetric(root->left, root->right);	//判断左子树和右子树是否对称
        }
        // 此函数比较二叉树中位置对称的两个节点
        bool isSymmetric(TreeNode* left, TreeNode* right){
            // 结束条件1:如果对称两个节点都为空,则返回true
            if(!left && !right){
                return true;
            }
            // 结束条件2:如果单独一个节点为空,另一个节点不为空,又或者是对称节点间的val值不等,则返回false
            if(!left || !right || left->val != right->val)
                return false;
            // 该层符合对称二叉树的要求,开始比较下一层
            //左结点的左结点,右结点的右结点
            return isSymmetric(left->left, right->right) && isSymmetric(left->right, right->left);      
        }
    };
    

    补一下递归:

    递归,一定有1.递推关系2.递推出口

    • 递归计算数组的前你项和

    • 实现一个字符串的逆序输出
      void reverse_print(int index,string str){
          if(str[index]==NULL){
              return;			//1
          }
          else{
              reverse_print(index+1,str);	//2
              cout<<str[index];		//3
          }
      }
      执行流程是: 2 2 2 2 2..... 1 3 3 3 3 3.....
      
    • 杨辉三角

    最核心的也是找出递推关系:

    class Solution {
    public:
        vector<vector<int>> generate(int numRows) {
            vector<vector<int>> ret(numRows);
            for (int i = 0; i < numRows; ++i) {
                ret[i].resize(i + 1);	//空间的初始化 
                ret[i][0] = ret[i][i] = 1;
                for (int j = 1; j < i; ++j) {
                    ret[i][j] = ret[i - 1][j] + ret[i - 1][j - 1];	//最核心的递推关系
                }
            }
            return ret;
        }
    };
    

    217. 存在重复元素

    这是用集合的特性来做的,集合本身就具有排同性O(nlog2n),还有一种解法就是先排序,之后再判断相邻元素是否相等来判断O(n)

    class Solution {
    public:
        bool containsDuplicate(vector<int>& nums) {
            unordered_set<int>s;
            for(int x:nums){
                if(s.find(x)!=s.end()){
                    return true;
                }
                s.insert(x);
            }
            return false;
        }
    };
    

    类似的287. 寻找重复数

    和上篇文章剑指 Offer 03. 数组中重复的数字的解题思路一样,详情见上篇文章

    class Solution {
    public:
        int findDuplicate(vector<int>& nums) {
            int res;
            int i=0;
            while(i<nums.size()){
                if(nums[i]==i){ //已经归位
                    i++;
                    continue;
                }
                else if(nums[i]==nums[nums[i]])
                    return nums[i];
                    swap(nums[i],nums[nums[i]]);
            }
            return res;
        }
    };
    

    剑指 Offer 29. 顺时针打印矩阵

    模拟这个顺时针的过程,一开始向右走到右边界,走完上边界++,向下走到下边界,走完右边界--,向左走到左边界,走完下边界--,向上走上边界,走完左边界++

    class Solution 
    {
    public:
        vector<int> spiralOrder(vector<vector<int>>& matrix) 
        {
            if (matrix.empty()) return {};
            vector<int> res;
            int l = 0;                      //左边界
            int r = matrix[0].size() - 1;   //右边界
            int t = 0;                      //上边界
            int b = matrix.size() - 1;      //下边界
            while (true)
            {
                //left -> right
                for (int i = l; i <= r; i++) res.push_back(matrix[t][i]);
                if (++t > b) break;
                //top -> bottom
                for (int i = t; i <= b; i++) res.push_back(matrix[i][r]);
                if (--r < l) break;
                //right -> left
                for (int i = r; i >= l; i--) res.push_back(matrix[b][i]);
                if (--b < t) break;
                //bottom -> top
                for (int i = b; i >= t; i--) res.push_back(matrix[i][l]);
                if (++l > r) break;
            }
            return res;
        }
    };
    
  • 相关阅读:
    Cstring 和 const char* , unicode和ANSI编码 的一个具体应用(转)
    引用 CTreeCtrl中用右键选中item并弹出菜单的方法(转)
    Android 开发人员必须掌握的 10 个开发工具
    关于在线程中使用AfxGetMainWnd()出错的问题,终于找到了
    MFC CListCtrl的用法.Style/插入、删除、选中数据及排序问题等(转)
    vc++ 中的匈牙利变量表示法
    Android模拟器安装程序及上传音乐并播放
    关于CString总结(转)
    MSSQL优化之————探索MSSQL执行计划(转)
    抓虫系列(三) 不要轻视web程序中常用的三个"池" 之数据库连接池
  • 原文地址:https://www.cnblogs.com/Truedragon/p/15942080.html
Copyright © 2020-2023  润新知