牛客面试必考算法题刷题

文章目录

      • tips
      • 设计LRU缓存结构
      • 判断链表中是否有环
      • 二分查找
      • 实现二叉树先序、中序、后序遍历
      • 寻找第K大
      • 合并有序链表
      • 求二叉树的层次遍历
      • 括号序列
      • 删除列表的倒数第n个节点
      • 链表中的节点每k个一组翻转
      • 在二叉树中找到两个节点的最近公共祖先节点
      • 合并两个有序的数组
      • 最长公共子串
      • 两数之和
      • 子数组的最大累加和问题
      • 找到字符串的最长无重复子串
      • 最长递增子序列
      • 反转字符串
      • 螺旋矩阵
      • 两个链表生成相加链表
      • 出现次数的TopK问题
      • 大数加法
      • 二叉树的之字形层序遍历
      • 求平方根
      • 数组中相加和为0的三元组
      • 输出二叉树的右视图
      • 岛屿数量(dfs)
      • 二叉树的最大深度
      • 设计getMin功能的栈
      • 最长回文子串
      • 表达式求值
      • 合并k个已排序的链表
      • 容器盛水问题
      • 链表内指定区间反转
      • 在两个长度相等的排序数组中找到中位数
      • 二叉树的最大路径和
      • 买买股票的最好时机(动态规划)
      • 进制转化
      • 二叉树根节点到叶子节点的所有路径和(DFS)
      • 删除有序链表中重复出现的元素
      • 括号生成(同时参见剑指数字全排列)
      • 链表的奇偶重排
      • 判断一个链表是否是回文结构
      • 顺时针旋转矩阵
      • 数组中未出现的最小正整数
      • 矩阵的最小路径和(动态规划)
      • 最小编辑代价(动态规划)
      • 合并区间
      • 有重复项数字的所有排列(回溯)
      • 最长的括号子串(动态规划)
      • 将字符串转化为整数
      • 最长公共子序列
      • 丢棋子(动态规划)
      • 判断一棵树是否为搜索二叉树和完全二叉树
      • 删除有序链表中重复的元素
      • 加起来为目标值的组合(回溯)
      • 排序
      • 数组中的最长连续子序列
      • 大数乘法
      • 判断二叉树是否对称
      • 数字字符串转化成IP地址(回溯)
      • 重排链表
      • 二叉树中是否存在节点和为指定值的路径(回溯)
      • 求路径
      • 换钱的最少货币数(动态规划)
      • 集合的所有子集(回溯)
      • N皇后问题(DFS不需要回溯)
      • LFU缓存结构设计
      • 股票(无限次交易)
      • 合并二叉树
      • 矩阵乘法
      • 旋转字符串
      • 判断t1树中是否有t2树拓扑结构
      • 矩阵最长递增路径(DFS)
      • 股票交易的最大收益(两次交易)
      • 完全二叉树结点数
      • 未排序数组中累加和为给定值的最长子数组
      • 0的个数
      • 拼接所有的字符串产生字典序最小的字符串
      • 比较版本号
      • 划分链表
      • 最大正方形(动态规划)
      • 找到搜索二叉树中两个错误的节点
      • 字典树的实现
      • 验证IP地址
      • 字符串变形
      • 最长重复子串
      • 栈和排序
      • 子数组最大乘积(动态规划)
      • 数独(DFS+回溯)
      • 最小覆盖子串(字符串滑动窗口)
      • 把数字翻译成字符串有多少种翻译法(动态规划)

tips

注意重写比较函数,一般出现在堆和sort函数时,堆的重写是

struct cmp {
     
    bool operator() (const pair<string, int> &a, const pair<string, int> &b) {
     
        if(a.second == b.second) return a.first < b.first;
        return a.second > b.second;
    }
};

sort函数的重写为

static bool cmp(const Interval &int1, const Interval &int2) {
     
        return int1.start < int2.start;
    }

设计LRU缓存结构

设计LRU缓存结构,该结构在构造时确定大小,假设大小为K,并有如下两个功能
set(key, value):将记录(key, value)插入该结构
get(key):返回key对应的value值
[要求]
set和get方法的时间复杂度为O(1)
某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的。
当缓存的大小超过K时,移除最不经常使用的记录,即set或get最久远的。
若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
对于每个操作2,输出一个答案

设置两个vector,分别按常用顺序存储key和value,set方法就是当vector大小小于k时,直接存储,如果大于k,则把容器头部的元素删除。get方法就是查找key,如果没找到返回-1,找到就把原有位置上的key删掉,然后插入新的key,就相当于get方法是在更新操作

class Solution {
     
public:
    /**
     * lru design
     * @param operators int整型vector> the ops
     * @param k int整型 the k
     * @return int整型vector
     */
    vector<int> LRU(vector<vector<int> >& operators, int k) {
     
        // write code here
        vector<int> res;
        vector<int> keys;
        vector<int> values;
        if(operators.size() == 0) return{
     };
        for(int i=0; i<operators.size(); i++) {
     
            if(operators[i][0] == 1) {
     
                int key = operators[i][1];
                int value = operators[i][2];
                set(key, value, keys, values, k);
            }
            else if(operators[i][0] == 2) {
     
                int key = operators[i][1];
                res.push_back(get(keys, values, key));
            }
        }
        return res;
    }
    void set(int key, int value, vector<int>& keys, vector<int>& values, int k) {
     
        if(keys.size() < k) {
     
            keys.push_back(key);
            values.push_back(value);
        }
        else {
     
            keys.erase(keys.begin());
            values.erase(values.begin());
            keys.push_back(key);
            values.push_back(value);
        }
    }
    int get(vector<int>& keys, vector<int>& values, int key) {
     
        int pos = -1;
        for(int i=0; i<keys.size(); i++) {
     
            if(keys[i] == key) {
     
                pos = i;
                break;
            }
        }
        if(pos == -1) return -1;
        int temp = values[pos];
        keys.erase(keys.begin()+pos);
        values.erase(values.begin()+pos);
        keys.push_back(key);
        values.push_back(temp);    //此时values[pos]已经变了
        return temp;
    }
};

判断链表中是否有环

判断给定的链表中是否有环

class Solution {
     
public:
    bool hasCycle(ListNode *head) {
     
        if(head == nullptr) return false;
        ListNode *fast = head, *slow = head;
        while(fast && fast->next && slow) {
     
            slow = slow->next;
            fast = fast->next->next;
            if(slow == fast) return true;
        }
        return false;
    }
};

二分查找

请实现有重复数字的有序数组的二分查找。
输出在数组中第一个大于等于查找值的位置,如果数组中不存在这样的数,则输出数组长度加一。

要注意的是有重复数字,所以要找到mid时要判断一下左边的数是不是也等于mid,还有就是找不到时返回的是大于它的值,所以返回left

class Solution {
     
public:
    /**
     * 二分查找
     * @param n int整型 数组长度
     * @param v int整型 查找值
     * @param a int整型vector 有序数组
     * @return int整型
     */
    int upper_bound_(int n, int v, vector<int>& a) {
     
        if(a.size() == 0) return 1;
        // write code here
        int left = 0, right = n-1, mid;
        while(left <= right) {
     
            mid = (left + right)/2;
            if(a[mid] == v) {
     
                if(mid > 0 && a[mid - 1] == v) right = mid - 1;
                else return mid + 1;
            }
            else if(a[mid] > v) {
     
                right = mid - 1;
            }
            else {
     
                left = mid + 1;
            }
        }
        if(left > right) {
     
            return left + 1;
        }
    }
};

实现二叉树先序、中序、后序遍历

分别按照二叉树先序,中序和后序打印所有的节点。

class Solution {
     
public:
    /**
     * 
     * @param root TreeNode类 the root of binary tree
     * @return int整型vector>
     */
    vector<vector<int> > threeOrders(TreeNode* root) {
     
        // write code here
        vector<vector<int> > res;
        vector<int> v1, v2, v3;
        pre(root, v1);
        res.push_back(v1);
        mid(root, v2);
        res.push_back(v2);
        back(root, v3);
        res.push_back(v3);
        return res;
        
    }
    void pre(TreeNode* root, vector<int>& v) {
     
        if(root == nullptr) return;
        v.push_back(root->val);
        pre(root->left, v);
        pre(root->right, v);
    }
    void mid(TreeNode* root, vector<int>& v) {
     
        if(root == nullptr) return;
        mid(root->left, v);
        v.push_back(root->val);
        mid(root->right, v);
    }
    void back(TreeNode* root, vector<int>& v) {
     
        if(root == nullptr) return;
        back(root->left, v);
        back(root->right, v);
        v.push_back(root->val);
    }
};

寻找第K大

有一个整数数组,请你根据快速排序的思路,找出数组中第K大的数。
给定一个整数数组a,同时给定它的大小n和要找的K(K在1到n之间),请返回第K大的数,保证答案存在。

class Finder {
     
public:
    int findKth(vector<int> a, int n, int K) {
     
        // write code here
        if(a.size() == 0) return 0;
        if(a.size() == 1) return a[0];
        int index, left = 0, right = n - 1;
        index = Partition(a, left, right);
        while(K-1 != index) {
     
            if(index < K-1) {
     
                left = index + 1;
                index = Partition(a, left, right);
            }
            else {
     
                right = index - 1;
                index = Partition(a, left, right);
            }
        }
        return a[index];
    }
    int Partition(vector<int>&a, int i, int j) {
     
        int temp = a[i];
        while(i < j) {
     
            while(temp >= a[j] && i < j) j--;
            a[i] = a[j];
            while(temp <= a[i] && i < j) i++;
            a[j] = a[i];
        }
        a[j] = temp;
        return j;
    }
};

合并有序链表

将两个有序的链表合并为一个新链表,要求新的链表是通过拼接两个链表的节点来生成的。

这道题给的listnode结构里没有构造方法,因此不设置哨兵节点

class Solution {
     
public:
    /**
     * 
     * @param l1 ListNode类 
     * @param l2 ListNode类 
     * @return ListNode类
     */
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
     
        // write code here
        ListNode *newhead, *p;
        if(l1 == nullptr && l2 == nullptr) return nullptr;
        if(l1 == nullptr) return l2;
        if(l2 == nullptr) return l1;
        if(l1->val < l2->val) {
     newhead = l1; l1 = l1->next;}
        else {
     newhead = l2; l2 = l2->next;}
        p = newhead;
        while(l1 && l2) {
     
            if(l1->val < l2->val) {
     
                p->next = l1;
                p = p->next;
                l1 = l1->next;
            } else {
     
                p->next = l2;
                p = p->next;
                l2 = l2->next;
            }
        }
        if(l1) p->next = l1;
        if(l2) p->next = l2;
        return newhead;
    }
};

求二叉树的层次遍历

要按照一层一层返回

class Solution {
     
public:
    /**
     * 
     * @param root TreeNode类 
     * @return int整型vector>
     */
    vector<vector<int> > levelOrder(TreeNode* root) {
     
        // write code here
        if(root == nullptr) return {
     };
        vector<vector<int> > res;
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty()) {
     
            int size = que.size();
            vector<int> temp;
            while(size--) {
     
                TreeNode *p = que.front();
                temp.push_back(p->val);
                que.pop();
                if(p->left) que.push(p->left);
                if(p->right) que.push(p->right);
            }
            res.push_back(temp);
        }
        return res;
    }
};

括号序列

给出一个仅包含字符’(’,’)’,’{’,’}’,’[‘和’]’,的字符串,判断给出的字符串是否是合法的括号序列
括号必须以正确的顺序关闭,"()“和”()[]{}“都是合法的括号序列,但”(]“和”([)]"不合法。

用栈实现,左括号进栈,遇到右括号就出栈

class Solution {
     
public:
    /**
     * 
     * @param s string字符串 
     * @return bool布尔型
     */
    bool isValid(string s) {
     
        // write code here
        stack<char> stk;
        for(int i=0; i<s.length(); i++) {
     
            if(s[i] == '[' || s[i] == '(' || s[i] == '{') {
     
                stk.push(s[i]);
            } else if(s[i] == ']' || s[i] == ')' || s[i] == '}') {
     
                if(stk.empty()) return false;
                char c = stk.top();
                if(match(c, s[i])) stk.pop();
                else return false;
            }
        }
        if(!stk.empty()) return false;
        return true;
    }
    bool match(char c1, char c2) {
     
        if(c1 == '(' && c2 == ')') return true;
        if(c1 == '[' && c2 == ']') return true;
        if(c1 == '{' && c2 == '}') return true;
        return false;
    }
};

删除列表的倒数第n个节点

给定一个链表,删除链表的倒数第n个节点并返回链表的头指针

双指针

class Solution {
     
public:
    /**
     * 
     * @param head ListNode类 
     * @param n int整型 
     * @return ListNode类
     */
    ListNode* removeNthFromEnd(ListNode* head, int n) {
     
        // write code here
        if(head == nullptr || n == 0) return nullptr;
         ListNode *p = head, *r, *q = head;
         for(int i=0; i<n-1; i++) {
     
             if(q == nullptr) return nullptr; 	//n大于链表长度
             q = q->next;
         }
         if(q == nullptr) return nullptr;	//n大于链表长度
         while(q->next) {
     
             r = p;
             p = p->next;
             q = q->next;
         }
        if(p == head) return head->next;	//删除的节点是头结点
        r->next = p->next;
        return head;
    }
};

链表中的节点每k个一组翻转

将给出的链表中的节点每k 个一组翻转,返回翻转后的链表
如果链表中的节点数不是k 的倍数,将最后剩下的节点保持原样
你不能更改节点中的值,只能更改节点本身。
空间复杂度O(1)

class Solution {
     
public:
    /**
     * 
     * @param head ListNode类 
     * @param k int整型 
     * @return ListNode类
     */
    ListNode* reverseKGroup(ListNode* head, int k) {
     
        // write code here
        if(head == nullptr || k == 1) return head;
        int sum = 0;
        ListNode *p = head, *r = nullptr, *hhead, *temp, *tail, *temp1;	//hhead就是新链表的头,temp1是每一组的第一个节点,记录下来为了下一组翻转时挂在该节点后面
        tail = new ListNode(0);	//tail是每前一组翻转链表的尾部
        while(p) {
     
            p = p->next;
            sum++;
        }
        p = head;
        int flag = 0;
        while(sum) {
     
            if(sum >= k) {
     
                for(int i=0; i<k; i++) {
     
                    if(i == 0) temp1 = p;
                    temp = p->next;
                    tail->next = p;
                    p->next = r;
                    r = p;
                    p = temp;
                }
                if(flag == 0) {
     hhead = tail; flag = 1;}	//给头结点赋值
                tail = temp1;
                sum -= k;
            }
            else {
     
                tail->next = p;
                if(flag == 0) {
     hhead = tail; flag = 1;}
                break;
            }
        }
        return hhead->next;
    }
};

在二叉树中找到两个节点的最近公共祖先节点

给定一棵二叉树以及这棵树上的两个节点 o1 和 o2,请找到 o1 和 o2 的最近公共祖先节点。

用递归实现,判断两个节点是否在同一棵树上,如果在同一棵树上就继续递归寻找,如果不在同一棵树上就返回根节点,如果一个节点是另一个节点的父节点,就返回那个节点

class Solution {
     
public:
    /**
     * 
     * @param root TreeNode类 
     * @param o1 int整型 
     * @param o2 int整型 
     * @return int整型
     */
    int lowestCommonAncestor(TreeNode* root, int o1, int o2) {
     
        // write code here
        if(root == nullptr) return 0;
        if(root->val == o1 || root->val == o2) return root->val;
        if(search(root->left, o1) && search(root->right, o2)) return root->val;
        if(search(root->right, o1) && search(root->left, o2)) return root->val;
        if(search(root->left, o1) && search(root->left, o2)) return lowestCommonAncestor(root->left, o1, o2);
        if(search(root->right, o1) && search(root->right, o2)) return lowestCommonAncestor(root->right, o1, o2);
    }
    bool search(TreeNode* root, int o) {
     
        if(root == nullptr) return false;
        if(root->val == o) return true;
        if(search(root->left, o)) return true;
        if(search(root->right, o)) return true;
        return false;
    }
};

合并两个有序的数组

给出两个有序的整数数组A和B,请将数组A合并到数组B中,变成一个有序的数组
注意:
可以假设A数组有足够的空间存放B数组的元素,A和B中初始的元素数目分别为m和n

class Solution {
     
public:
    void merge(int A[], int m, int B[], int n) {
     
        int p1 = m-1, p2 = n-1, p = m+n-1;
        while(p1 >=0 && p2 >= 0) {
     
            if(A[p1] > B[p2]) {
     
                A[p--] = A[p1--];
            } else {
     
                A[p--] = B[p2--];
            }
        }
        while(p1 >= 0) {
     
            A[p--] = A[p1--];
        }
        while(p2 >= 0) {
     
            A[p--] = B[p2--];
        }
        return;
    }
};

最长公共子串

给定两个字符串str1和str2,输出两个字符串的最长公共子串,如果最长公共子串为空,输出-1。

动态规划,但是这里用的是二维数组,后续可以改成一维数组

class Solution {
     
public:
    /**
     * longest common substring
     * @param str1 string字符串 the string
     * @param str2 string字符串 the string
     * @return string字符串
     */
    string LCS(string str1, string str2) {
     
        // write code here
        int len1 = str1.length(), len2 = str2.length(), maxlength = 5005, maxlen = 0, start;
        if(len1 == 0 || len2 == 0) return "-1";
        int dp[maxlength][maxlength];
        memset(dp, 0, sizeof(dp));
        for(int i=1; i<=len1; i++) {
     
            for(int j=1; j<=len2; j++) {
     
                if(str1[i-1] == str2[j-1]) {
         // 因为i j都是从1开始 所以要-1
                    dp[i][j] = dp[i-1][j-1] + 1;
                }
                if(dp[i][j] > maxlen) {
     
                    maxlen = dp[i][j];
                    start = i;
                }
            }
        }
        if(maxlen == 0) return "-1";
        return str1.substr(start-maxlen, maxlen);
    }
};

两数之和

给出一个整数数组,请在数组中找出两个加起来等于目标值的数,
你给出的函数twoSum 需要返回这两个数字的下标(index1,index2),需要满足 index1 小于index2.。注意:下标是从1开始的
假设给出的数组中只存在唯一解

用哈希表

class Solution {
     
public:
    /**
     * 
     * @param numbers int整型vector 
     * @param target int整型 
     * @return int整型vector
     */
    vector<int> twoSum(vector<int>& numbers, int target) {
     
        // write code here
        map<int, int> m;
        vector<int> res;
        for(int i=1; i<=numbers.size(); i++) {
     
            m[numbers[i-1]] = i;
        }
        for(int i=0; i<numbers.size(); i++) {
     
            int temp = target - numbers[i];
            if(m.count(temp) != 0 && (i+1) != m[temp]) {
     
                res.push_back(i+1);
                res.push_back(m[temp]);
                break;
            }
        }
        return res;
    }
};

子数组的最大累加和问题

给定一个数组arr,返回子数组的最大累加和

class Solution {
     
public:
    /**
     * max sum of the subarray
     * @param arr int整型vector the array
     * @return int整型
     */
    int maxsumofSubarray(vector<int>& arr) {
     
        // write code here
        int sum = 0;
        for(int i=0; i<arr.size(); i++) {
     
            if(sum < 0) {
     
                int temp = max(sum + arr[i], arr[i]);
                sum = temp;
            }
            else {
     
                sum += arr[i];
            }
        }
        return sum;
    }
};

找到字符串的最长无重复子串

示例:
输入[2, 3, 4, 5] 输出4
输入[2, 2, 3, 4, 3] 输出3

用双指针和哈希表,双指针记录的是无重复子串的起始和结束位置,哈希表记录是否已经存在这个元素,如果发现起始和结束位置重复了,就更新起始位置

class Solution {
     
public:
    /**
     * 
     * @param arr int整型vector the array
     * @return int整型
     */
    int maxLength(vector<int>& arr) {
     
        // write code here
        if(arr.size() == 0) return 0;
        vector<int> v(100005);
        int i = 0, j = 0, res = 0;
        while(j < arr.size()) {
     
            if(v[arr[j]] == 0) {
     
                v[arr[j]] = 1;
                res = max(res, j-i+1);
                j++;
            } else {
     
                v[arr[i]] = 0;    //一定是j与i这两个位置上的元素重复了,因此把这个位置重置为0, 并且更新起始位置
                i++;
            }
        }
        return res;
    }
};

最长递增子序列

给定数组arr,设长度为n,输出arr的最长递增子序列。(如果有多个答案,请输出其中字典序最小的)

牛客面试必考算法题刷题_第1张图片
用动态规划是O(n^2)的时间复杂度,会运行超时,所以用贪心法加二分查找

class Solution {
     
public:
    /**
     * retrun the longest increasing subsequence
     * @param arr int整型vector the array
     * @return int整型vector
     */
    vector<int> LIS(vector<int>& arr) {
     
        // write code here
        vector<int> dp(arr.size(), 0);
        vector<int> end;
        dp[0] = 1;
        end.push_back(arr[0]);
        for(int i=1; i<arr.size(); i++) {
     
            if(arr[i] > end.back()) {
     
                end.push_back(arr[i]);
                dp[i] = end.size();
            }
            else {
     
                int index = search(0, end.size()-1, end, arr[i]);
                end[index] = arr[i];
                dp[i] = index + 1;
            }
        }
        vector<int> res(end.size(), INT_MAX);
        int len = end.size();
        for(int i=arr.size()-1; i>=0; i--) {
     
            if(dp[i] == len) {
     
                if(arr[i] < res[len-1]) {
     
                    res[len-1] = arr[i];
                }
                len--;
            }
        }
        return res;
    }
    
    int search(int left, int right, vector<int>& vec, int target) {
     
        while(left < right) {
     
            int mid = (left + right) / 2;
            if(vec[mid] < target) left = mid + 1;
            else right = mid; 
        }
        return left;
    }
};

反转字符串

写出一个程序,接受一个字符串,然后输出该字符串反转后的字符串。(字符串长度不超过1000

用双指针,从前从后遍历字符串然后交换

class Solution {
     
public:
    /**
     * 反转字符串
     * @param str string字符串 
     * @return string字符串
     */
    string solve(string str) {
     
        // write code here
        int i = 0, j = str.length() - 1;
        while(i < j) {
     
            char temp = str[i];
            str[i] = str[j];
            str[j] = temp;
            i++;
            j--;
        }
        return str;
    }
};

螺旋矩阵

给定一个m x n大小的矩阵(m行,n列),按螺旋的顺序返回矩阵中的所有元素。

class Solution {
     
public:
    vector<int> spiralOrder(vector<vector<int> > &matrix) {
     
        if(matrix.size() == 0) return {
     };
        int rows = matrix.size(), cols = matrix[0].size();
        int left = 0, right = cols - 1, up = 0, down = rows - 1;
        vector<int> res;
        while(left <= right && up <= down) {
     
            for(int j=left; j<=right; j++) {
     
                res.push_back(matrix[up][j]);
            }
            up++;
            if(up > down) break;
            for(int i=up; i<=down; i++) {
     
                res.push_back(matrix[i][right]);
            }
            right--;
            if(left > right) break;
            for(int j=right; j>=left; j--) {
     
                res.push_back(matrix[down][j]);
            }
            down--;
            if(up > down) break;
            for(int i=down; i>=up; i--) {
     
                res.push_back(matrix[i][left]);
            }
            left++;
            if(left > right) break;
        }
        return res;
    }
};

两个链表生成相加链表

假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。
给定两个这种链表,请生成代表两个整数相加值的结果链表。

将两个链表先进行反转,从末位开始相加,并且计算进位。注意,反转链表的时候不需要用头插法,这道题如果用头插法会内存不够,所以要free

class Solution {
     
public:
    /**
     * 
     * @param head1 ListNode类 
     * @param head2 ListNode类 
     * @return ListNode类
     */
    ListNode* Reverse(ListNode* head) {
     
        ListNode *newhead = new ListNode(0);
        ListNode *p = head, *temp, *r = nullptr;
        while(p) {
     
            temp = p->next;
            free(newhead);
            newhead->next = p;
            p->next = r;
            r = p;
            p = temp;
        }
        return r;
    }
    /*ListNode *Reverse(ListNode* head) {
        ListNode *cur = nullptr;
        while (head) {
            ListNode *tmp = head->next;
            head->next = cur;
            cur = head;
            head = tmp;
        }
        return cur;
    }*/
    ListNode* addInList(ListNode* head1, ListNode* head2) {
     
        // write code here
        ListNode *nhead1 = Reverse(head1);
        ListNode *nhead2 = Reverse(head2);
        ListNode *p = nhead1, *q = nhead2, *head = new ListNode(0), *r = head;
        int carry = 0;
        while(p || q) {
     
            int sum = 0;
            if(p) {
     sum += p->val; p = p->next;}
            if(q) {
     sum += q->val; q = q->next;}
            sum += carry;
            r->next = new ListNode(sum%10);
            r = r->next;
            carry = sum / 10;
        }
        if(carry) {
     
            r->next = new ListNode(carry);
        }
        return Reverse(head->next);
    }
};

出现次数的TopK问题

给定String类型的数组strArr,再给定整数k,请严格按照排名顺序打印 出次数前k名的字符串。
你需要按照出现出现次数由大到小输出,若出现次数相同时字符串字典序较小的优先输出.

先用哈希表存储每个字符和每个字符出现的次数,然后用最小堆寻找出现次数TOPK的数,这里要注意使用pair,将堆中的每个元素用pair存储,并且要重写cmp方法

struct cmp {
     
    bool operator() (const pair<string, int> &a, const pair<string, int> &b) {
     
        if(a.second == b.second) return a.first < b.first;
        return a.second > b.second;
    }
};
class Solution {
     
public:
    /**
     * return topK string
     * @param strings string字符串vector strings
     * @param k int整型 the k
     * @return string字符串vector>
     */
    vector<vector<string> > topKstrings(vector<string>& strings, int k) {
     
        // write code here
        map<string, int> m;
        for(int i=0; i<strings.size(); i++) {
     
            m[strings[i]]++;
        }
        priority_queue<pair<string, int>, vector<pair<string, int> >, cmp> que;
        for(map<string, int>::iterator it = m.begin(); it != m.end(); it++) {
     
            pair<string, int> temp(it->first, it->second);
            if(que.size() < k) {
     
                que.push(temp);
            }
            else if(temp.second > que.top().second){
     
                que.pop();
                que.push(temp);
            }
            else if(temp.second == que.top().second) {
     
                if(temp.first < que.top().first) {
     
                    que.pop();
                    que.push(temp);
                }
            }
        }
        vector<vector<string> > res;
        while(!que.empty()) {
     
            res.push_back({
     que.top().first, to_string(que.top().second)});
            que.pop();
        }
        reverse(res.begin(), res.end());
        return res;
    }
};

大数加法

以字符串的形式读入两个数字,编写一个函数计算它们的和,以字符串形式返回。

从末位开始相加,然后计算进位

class Solution {
     
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 计算两个数之和
     * @param s string字符串 表示第一个整数
     * @param t string字符串 表示第二个整数
     * @return string字符串
     */
    void reverse(string& str) {
     
        int i = 0, j = str.length() - 1;
        while(i < j) {
     
            char temp = str[i];
            str[i] = str[j];
            str[j] = temp;
            i++; j--;
        }
    }
    string solve(string s, string t) {
     
        // write code here
        string res = "";
        int carry = 0, len1 = s.length() - 1, len2 = t.length() - 1;
        while(len1>=0 || len2>=0) {
     
            int sum = 0;
            if(len1 >= 0) {
     
                sum += (s[len1--]-'0');
            }
            if(len2 >= 0) {
     
                sum += (t[len2--]-'0');
            }
            sum += carry;
            res += to_string(sum%10);    //如果不用to_string,直接用+‘0’会内存不够
            carry = sum / 10;
        }
        if(carry) {
     
            res += to_string(carry);
        }
        reverse(res);
        return res;
    }
};

二叉树的之字形层序遍历

给定一个二叉树,返回该二叉树的之字形层序遍历,(第一层从左向右,下一层从右向左,一直这样交替)

class Solution {
     
public:
    /**
     * 
     * @param root TreeNode类 
     * @return int整型vector>
     */
    vector<vector<int> > zigzagLevelOrder(TreeNode* root) {
     
        // write code here
        if(root == nullptr) return {
     };
        queue<TreeNode*> que;
        que.push(root);
        TreeNode* p = root;
        vector<vector<int>> res;
        int flag = 0;
        while(!que.empty()) {
     
            int size = que.size();
            vector<int> temp;
            while(size) {
     
                p = que.front();
                que.pop();
                temp.push_back(p->val);
                if(p->left) que.push(p->left);
                if(p->right) que.push(p->right);
                size--;
                
            }
            if(flag == 0) {
     res.push_back(temp); flag = 1;}
            else {
     reverse(temp.begin(), temp.end()); res.push_back(temp); flag = 0;}
        }
        return res;
    }
};

求平方根

实现函数 int sqrt(int x).

用二分法计算,但要注意不用写成mid*mid=x,而要写成mid = x/mid

class Solution {
     
public:
    /**
     * 
     * @param x int整型 
     * @return int整型
     */
    int sqrt(int x) {
     
        // write code here
        if(x == 0) return 0;
        if(x == 1) return 1;
        int left = 1, right = x/2;
        while(left <= right) {
     
            int mid = (left + right) / 2;
            if(mid == x/mid) return mid;
            else if(mid < x/mid) left = mid + 1;
            else right = mid - 1;
        }
        if(left >= right) return right;
    }
};

数组中相加和为0的三元组

先排序,然后固定一个数,然后用快慢指针遍历。但是一定要注意重复

class Solution {
     
public:
    vector<vector<int> > threeSum(vector<int> &num) {
     
        int left, right, target;
        if(num.size() < 3) return {
     };
        sort(num.begin(), num.end());
        //if(num.front() > 0 || num.back() < 0) return {};
        vector<vector<int> > res;
        for(int i=0; i<num.size() - 2; i++) {
     
            //if(num[i] > 0) break;
            if(i > 0 && num[i] == num[i-1]) continue;
            target = 0-num[i];
            left = i + 1;
            right = num.size() - 1;
            while(left < right) {
     
                if(num[left] + num[right] < target) left++;
                else if(num[left] + num[right] > target) right--;
                else {
     
                    res.push_back({
     num[i], num[left], num[right]});
                    while(left < right && num[left] == num[left+1]) left++;
                    while(left < right && num[right] == num[right-1]) right--;
                    left++; right--;
                }
            }
        }
        return res;
    }
};

输出二叉树的右视图

请根据二叉树的前序遍历,中序遍历恢复二叉树,并打印出二叉树的右视图

二叉树的右视图就是每一层的最右边的节点

class Solution {
     
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 求二叉树的右视图
     * @param xianxu int整型vector 先序遍历
     * @param zhongxu int整型vector 中序遍历
     * @return int整型vector
     */
    vector<int> solve(vector<int>& xianxu, vector<int>& zhongxu) {
     
        // write code here
        TreeNode *root;
        root = resolve(xianxu, zhongxu);
        if(root == nullptr) return {
     };
        vector<int> res;
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty()) {
     
            int size = que.size();
            while(size--) {
     
                TreeNode *p = que.front();
                que.pop();
                if(p->left) que.push(p->left);
                if(p->right) que.push(p->right);
                if(size == 0) res.push_back(p->val);
            }
        }
        return res;
    }
    TreeNode* resolve(vector<int>& xianxu, vector<int>& zhongxu) {
     
        if(xianxu.size() == 0 || zhongxu.size() == 0) return nullptr;
        TreeNode* root = new TreeNode(xianxu[0]);
        vector<int> left_xian, left_zhong, right_xian, right_zhong;
        int sum = 0, i = 0;
        while(zhongxu[i] != xianxu[0]) {
     
            left_zhong.push_back(zhongxu[i]);
            left_xian.push_back(xianxu[i+1]);
            i++;
        }
        i++;
        while(i < zhongxu.size()) {
     
            right_zhong.push_back(zhongxu[i]);
            right_xian.push_back(xianxu[i]);
            i++;
        }
        root->left = resolve(left_xian, left_zhong);
        root->right = resolve(right_xian, right_zhong);
        return root;
    }
};

岛屿数量(dfs)

给一个01矩阵,1代表是陆地,0代表海洋, 如果两个1相邻,那么这两个1属于同一个岛。我们只考虑上下左右为相邻。
岛屿: 相邻陆地可以组成一个岛屿(相邻:上下左右) 判断岛屿个数。

DFS,DFS的题目首先要注意是不是要回溯,如果需要回溯就需要在DFS之后再更改回来。如果不需要回溯,那么需要考虑的是这个DFS的返回值是什么,是没有返回值,还是返回true false。这道题目的目的就是在DFS的过程中,把为1的点都设为访问,不需要返回值

class Solution {
     
public:
    /**
     * 判断岛屿数量
     * @param grid char字符型vector> 
     * @return int整型
     */
    int dis[4][2] = {
     {
     0,1}, {
     0,-1}, {
     1,0}, {
     -1,0}};
    int visited[205][205];
    int solve(vector<vector<char> >& grid) {
     
        // write code here
        if(grid.size() == 0) return 0;
        int rows = grid.size();
        int cols = grid[0].size();
        int sum = 0;
        memset(visited, 0, sizeof(visited));
        for(int i=0; i<rows; i++) {
     
            for(int j=0; j<cols; j++) {
     
                if(grid[i][j] == '1' && visited[i][j] == 0) {
     
                    dfs(i, j, grid); 
                    sum++;
                }
            }
        }
        return sum;
    }
    void dfs(int x, int y, vector<vector<char> >& grid) {
     
        if(visited[x][y] == 1) return;
        visited[x][y] = 1;
        int rows = grid.size();
        int cols = grid[0].size();
        for(int i=0; i<4; i++) {
     
            int next_x = x + dis[i][0];
            int next_y = y + dis[i][1];
            if(next_x >= 0 && next_x < rows && next_y >=0 && next_y < cols) {
     
                if(grid[next_x][next_y] == '1') {
     
                    dfs(next_x, next_y, grid);
                }
            }
        }
        return;
    }
};

二叉树的最大深度

求给定二叉树的最大深度

class Solution {
     
public:
    /**
     * 
     * @param root TreeNode类 
     * @return int整型
     */
    int maxDepth(TreeNode* root) {
     
        // write code here
        if(root == nullptr) return 0;
        int h1 = maxDepth(root->left) + 1;
        int h2 = maxDepth(root->right) + 1;
        return h1>h2?h1:h2;
    }
};

设计getMin功能的栈

实现一个特殊功能的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作。
有三种操作种类,op1表示push,op2表示pop,op3表示getMin。你需要返回和op3出现次数一样多的数组,表示每次getMin的答案

用两个栈实现,一个栈就是普通栈,另一个栈存储当前的最小值

class Solution {
     
public:
    /**
     * return a array which include all ans for op3
     * @param op int整型vector> operator
     * @return int整型vector
     */
    vector<int> getMinStack(vector<vector<int> >& op) {
     
        // write code here
        stack<int> s1, s2;
        vector<int> res;
        for(int i=0; i<op.size(); i++) {
     
            if(op[i][0] == 1) {
     
                int num = op[i][1];
                s1.push(num);
                if(s2.empty() || num < s2.top()) {
     
                    s2.push(num);
                }
                else {
     
                    s2.push(s2.top());
                }
            }
            else if(op[i][0] == 2) {
     
                s1.pop();
                s2.pop();
            }
            else if(op[i][0] == 3) {
     
                res.push_back(s2.top());
            }
        }
        return res;
    }
};

最长回文子串

给定字符串A以及它的长度n,请返回最长回文子串的长度。

动态规划,dp[i][j]用来判断从i到j是否是回文子串,dp[i][j] = dp[i+1][j-1],因此i要递减遍历,j要递增遍历

class Palindrome {
     
public:
    int getLongestPalindrome(string A, int n) {
     
        // write code here
        int dp[n][n];
        int len = 0, maxlen = 0;
        memset(dp, 0, sizeof(dp));
        for(int i=n-2; i>=0; i--) {
     
            for(int j=i+1; j<n; j++) {
     
                if(A[i] == A[j]) {
     
                    if(j - i > 2) dp[i][j] = dp[i+1][j-1];
                    else dp[i][j] = 1;
                    if(dp[i][j] == 1) {
     
                        len = j - i + 1;
                        if(maxlen < len) maxlen = len;
                    }
                    
                }
            }
        }
        return maxlen;
    }
};

表达式求值

请写一个整数计算器,支持加减乘三种运算和括号。

两个栈,符号栈和数字栈,如果碰到(就直接入栈,如果碰到加减就退栈计算,直到遇到左括号或者栈空,如果碰到乘就退栈计算,知道所有的乘都计算完

class Solution {
     
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 返回表达式的值
     * @param s string字符串 待计算的表达式
     * @return int整型
     */
    int solve(string s) {
     
        // write code here
        if(s.length() == 0) return 0;
        stack<char> ops;
        stack<int> nums;
        for(int i=0; i<s.length(); i++) {
     
            if(isOp(s[i]) == true) {
     
                if(s[i] == '(') ops.push(s[i]);
                else if(s[i] == '*') {
     
                    while(!ops.empty() && ops.top() == '*') {
     
                        cal(nums, ops.top());
                        ops.pop();
                    }
                    ops.push(s[i]);
                }
                else if(s[i] == '+' || s[i] == '-') {
     
                    while(!ops.empty() && ops.top() != '(') {
     
                        cal(nums, ops.top());
                        ops.pop();
                    }
                    ops.push(s[i]);
                }
                else if(s[i] == ')') {
     
                    while(ops.top() != '(') {
     
                        cal(nums, ops.top());
                        ops.pop();
                    }
                    ops.pop();
                }
            } else {
     
                int num = 0;
                while(i < s.length() && isOp(s[i]) == false) {
     
                    num = num*10 + (s[i++] - '0');
                }
                nums.push(num);
                i--;
            }
        }
        while(!ops.empty()) {
     
            cal(nums, ops.top());
            ops.pop();
        }
        return nums.top();
    }
    bool isOp(char c) {
     
        if(c == '+' || c == '-' || c == '*' || c == '(' || c == ')') return true;
        return false;
    }
    void cal(stack<int>& nums, char op) {
     
        int num1 = nums.top();
        nums.pop(); 
        int num2 = nums.top();
        nums.pop();
        if(op == '*') {
     
            nums.push(num1 * num2);
        }
        if(op == '+') {
     
            nums.push(num1 + num2);
        }
        if(op == '-') {
     
            nums.push(num2 - num1);
        }
    }
};

合并k个已排序的链表

合并 k个已排序的链表并将其作为一个已排序的链表返回。分析并描述其复杂度。

用最小堆,先将每个链表的表头加入到堆中,然后最小值继续移动

struct cmp {
     
    bool operator()(const ListNode* node1, const ListNode* node2) {
     
        return node1->val > node2->val;
    }
    
};
class Solution {
     
public:
    ListNode *mergeKLists(vector<ListNode *> &lists) {
     
        priority_queue<ListNode*, vector<ListNode*>, cmp> que;
        for(auto l:lists) {
     
            if(l) que.push(l);
        }
        ListNode *head = new ListNode(0);
        ListNode *p = head;
        while(!que.empty()) {
     
            ListNode *temp = que.top();
            p->next = temp;
            que.pop();
            if(temp->next) que.push(temp->next);
            p = p->next;
        }
        return head->next;
    }
};

容器盛水问题

给定一个整形数组arr,已知其中所有的值都是非负的,将这个数组看作一个容器,请返回容器能装多少水。

双指针问题,先比较容器的最左边和最右边,选取最小值作为边界,然后左边的值小于右边的,左边就+1,此时判断水量是否小于最小值,如果小于则算到总容量中,如果大于,则要重新设置一下边界

class Solution {
     
public:
    /**
     * max water
     * @param arr int整型vector the array
     * @return long长整型
     */
    long long maxWater(vector<int>& arr) {
     
        // write code here
        if(arr.size() <= 2) return 0;
        long long res = 0;
        int left = 0, right = arr.size() - 1;
        int minvalue = min(arr[left], arr[right]);
        while(left < right) {
     
            if(arr[left] < arr[right]) {
     
                left++;
                if(arr[left] < minvalue) {
     
                    res += (minvalue - arr[left]);
                } else {
     
                    minvalue = min(arr[left], arr[right]);
                }
            } else {
     
                right--;
                if(arr[right] < minvalue) {
     
                    res += (minvalue - arr[right]);
                } else {
     
                    minvalue = min(arr[left], arr[right]);
                }
            }
        }
        return res;
    }
};

链表内指定区间反转

将一个链表m 位置到n位置之间的区间反转,要求时间复杂度O(n) ,空间复杂度O(1)。
例如:给出的链表为1->2->3->4->5->NULL, m=2, n=4,反转后1->4->3->2->5->NULL

注意这种反转链表的题目,反转之后要接到原有的链表上,要注意记录头结点和尾结点,头结点就是每次都插入到该结点后面,尾结点就是要反转的第一个结点

class Solution {
     
public:
    /**
     * 
     * @param head ListNode类 
     * @param m int整型 
     * @param n int整型 
     * @return ListNode类
     */
    ListNode* reverseBetween(ListNode* head, int m, int n) {
     
        // write code here
        ListNode *p = head, *r, *tail;	//tail就是
        if(head == nullptr) return nullptr;
        if(m == n) return head;
        int pos = 1;
        ListNode *hhead = new ListNode(0);	// hhead就是要头插的那个点
        while(pos <= n) {
     
            if(pos <= m) {
     
                r = p;
                p = p->next;
                if(pos == m-1) hhead = r;
                if(pos == m) tail = r;
            } else {
     
                ListNode *temp = p->next;
                hhead->next = p;	
                p->next = r;
                //tail->next = temp;	
                r = p;
                p = temp;
            }
            pos++;
        }
        tail->next = p;
        if(m == 1) head = hhead->next;
        return head;
    }
};

在两个长度相等的排序数组中找到中位数

给定两个有序数组arr1和arr2,已知两个数组的长度都为N,求两个数组中所有数的上中位数。

设置双指针

class Solution {
     
public:
    /**
     * find median in two sorted array
     * @param arr1 int整型vector the array1
     * @param arr2 int整型vector the array2
     * @return int整型
     */
    int findMedianinTwoSortedAray(vector<int>& arr1, vector<int>& arr2) {
     
        // write code here
        int i = 0, j = 0, n = arr1.size(), num = 0;
        while(i<n && j<n) {
     
            if(arr1[i] <= arr2[j]) {
     
                num++;
                if(num == n) return arr1[i];
                i++;
            } else {
     
                num++;
                if(num == n) return arr2[j];
                j++;
            }
        }
        return 0;
    }
};

二叉树的最大路径和

给定一个二叉树,请计算节点值之和最大的路径的节点值之和是多少。
这个路径的开始节点和结束节点可以是二叉树中的任意节点

整体思路和动态规划中的求子序列和很像,但是用了树中可以用的递归方法,用后序遍历,分别计算左右两边的最大和,然后更新最大值

class Solution {
     
public:
    /**
     * 
     * @param root TreeNode类 
     * @return int整型
     */
    int maxvalue = INT_MIN;
    int maxPathSum(TreeNode* root) {
     
        // write code here
        if(root == nullptr) return 0;
        getMaxPathSum(root);
        return maxvalue;
    }
    int getMaxPathSum(TreeNode* root) {
     	//返回的是包含这个结点的最大值
        if(root == nullptr) return 0;
        int leftMax = max(0, getMaxPathSum(root->left));
        int rightMax = max(0, getMaxPathSum(root->right));
        maxvalue = max(maxvalue, leftMax+rightMax+root->val);
        return max(0, root->val + max(leftMax, rightMax));    //返回的时候只能返回一条路径,因为返回之后还要继续走别的点,但是在算最大值的时候就算整个路径都走完的
    }
};

买买股票的最好时机(动态规划)

假设你有一个数组,其中第 i 个元素是股票在第 i 天的价格。
你有一次买入和卖出的机会。(只有买入了股票以后才能卖出)。请你设计一个算法来计算可以获得的最大收益。

class Solution {
     
public:
    /**
     * 
     * @param prices int整型vector 
     * @return int整型
     */
    int maxProfit(vector<int>& prices) {
     
        // write code here
        int n = prices.size(), maxvalue = 0;
        for(int i=0; i<n-1; i++) {
     
            int temp = 0;
            for(int j=i+1; j<n; j++) {
     
                if(prices[j] > prices[i]) {
     
                    temp = max(temp, prices[j]-prices[i]);
                }
            }
            if(temp > maxvalue) maxvalue = temp;
        }
        return maxvalue;
    }
};

进制转化

给定一个十进制数M,以及需要转换的进制数N。将十进制数M转化为N进制数

负数的转化直接在前面添加负号

class Solution {
     
public:
    /**
     * 进制转换
     * @param M int整型 给定整数
     * @param N int整型 转换到的进制
     * @return string字符串
     */
    string solve(int M, int N) {
     
        // write code here
        if(M == 0) return "0";
        int flag = 0;
        if(M < 0) {
     
            flag = 1;
            M = -M;
        }
        string res = "";
        string str = "0123456789ABCDEF";
        while(M != 0) {
     
            res += str[M%N];
            M = M/N;
        }
        if(flag == 1) res += "-";
        reverse(res.begin(), res.end());
        return res;
    }
};

二叉树根节点到叶子节点的所有路径和(DFS)

给定一个仅包含数字0-9的二叉树,每一条从根节点到叶子节点的路径都可以用一个数字表示。例如根节点到叶子节点的一条路径是1->2->3,那么这条路径就用123来代替,找出根节点到叶子节点的所有路径表示的数字之和

DFS

class Solution {
     
public:
    /**
     * 
     * @param root TreeNode类 
     * @return int整型
     */
    int sumNumbers(TreeNode* root) {
     
        // write code here
        int res = 0;
        vector<int> path;
        dfs(root, path, res);
        return res;
    }
    void dfs(TreeNode* root, vector<int>& path, int& res) {
     
        if(root == nullptr) return;
        path.push_back(root->val);
        if(root->left == nullptr && root->right == nullptr) {
     
            res += cal(path);
        }
        dfs(root->left, path, res);
        dfs(root->right, path, res);
        path.pop_back();
    }
    int cal(vector<int>& path) {
     
        int sum = 0;
        for(int i=0; i<path.size(); i++) {
     
            sum = sum*10 + path[i];
        }
        return sum;
    }
};

删除有序链表中重复出现的元素

给出一个升序排序的链表,删除链表中的所有重复出现的元素,只保留原链表中只出现一次的元素

class Solution {
     
public:
    /**
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    ListNode* deleteDuplicates(ListNode* head) {
     
        // write code here
        ListNode *p = head, *hhead, *r;
        hhead = new ListNode(0);
        hhead->next = head;
        r = hhead;
        while(p) {
     
            if(p->next && p->val == p->next->val){
     
                while(p->next && p->val == p->next->val) {
     
                    p = p->next;
                }
                r->next = p->next;
                p = p->next;
            } else {
     
                r = p;
                p = p->next;
            }
        }
        return hhead->next;
    }
};

括号生成(同时参见剑指数字全排列)

给出n对括号,请编写一个函数来生成所有的由n对括号组成的合法组合。
例如,给出n=3,解集为:
“((()))”, “(()())”, “(())()”, “()()()”, “()(())”,

用递归进行回溯

class Solution {
     
public:
    /**
     * 
     * @param n int整型 
     * @return string字符串vector
     */
    vector<string> generateParenthesis(int n) {
     
        // write code here
        vector<string> res;
        string s = "";
        generate(0, 0, res, s, n);
        return res;
    }
    void generate(int usedleft, int usedright, vector<string>& res, string s, int n) {
     
        if(usedleft == n && usedright == n) {
     
            res.push_back(s);
            return;
        }
        if(usedleft < n) {
     
            generate(usedleft+1, usedright, res, s+"(", n);
        }
        if(usedright < n && usedleft > usedright) {
     
            generate(usedleft, usedright+1, res, s+")", n);
        }
    }
};

链表的奇偶重排

给定一个单链表,请设定一个函数,讲链表的奇数位节点和偶数位节点分别放在一起,重排后输出。

class Solution {
     
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    ListNode* oddEvenList(ListNode* head) {
     
        // write code here
        if(head == nullptr) return nullptr;
        ListNode *p = head, *head1 = head, *head2 = head->next, *r = head;
        int sum = 0;
        while(p->next) {
     
            ListNode *temp = p->next;
            p->next = p->next->next;
            r = p;
            p = temp;
            sum++;
        }	//遍历完之后p就是链表的最后一个结点
        //两条链表的拼接需要根据链表中节点的总数的奇偶性决定
        if(sum%2 == 0) p->next = head2;
        else r->next = head2;
        return head1;
    }
};

判断一个链表是否是回文结构

class Solution {
     
public:
    /**
     * 
     * @param head ListNode类 the head
     * @return bool布尔型
     */
    bool isPail(ListNode* head) {
     
        // write code here
        if(head == nullptr) return false;
        vector<int> nums;
        getNum(nums, head);
        int i = 0, j = nums.size() - 1;
        while(i < j) {
     
            if(nums[i] != nums[j]) return false;
            i++; j--;
        }
        return true;
    }
    void getNum(vector<int>& nums, ListNode* head) {
     
        ListNode *p = head;
        while(p) {
     
            nums.push_back(p->val);
            p = p->next;
        }
    }
};

顺时针旋转矩阵

有一个NxN整数矩阵,请编写一个算法,将矩阵顺时针旋转90度。
给定一个NxN的矩阵,和矩阵的阶数N,请返回旋转后的NxN矩阵,保证N小于等于300

class Rotate {
     
public:
    vector<vector<int> > rotateMatrix(vector<vector<int> > mat, int n) {
     
        // write code here
        for(int i=0; i<n/2; i++) {
     
            for(int j=i; j<n-i-1; j++) {
         //是一条对角线
                int temp = mat[i][j];
                mat[i][j] = mat[n-j-1][i];
                mat[n-j-1][i] = mat[n-i-1][n-j-1];
                mat[n-i-1][n-j-1] = mat[j][n-i-1];
                mat[j][n-i-1] = temp;
            }
        }
        return mat;
    }
};

数组中未出现的最小正整数

给定一个无序数组arr,找到数组中未出现的最小正整数
例如arr = [-1, 2, 3, 4]。返回1
arr = [1, 2, 3, 4]。返回5

class Solution {
     
public:
    /**
     * return the min number
     * @param arr int整型vector the array
     * @return int整型
     */
    int minNumberdisappered(vector<int>& arr) {
     
        // write code here
        int res = 1;
        for(int i=0; i<arr.size(); i++) {
     
            if(arr[i] <= 0) continue;
            if(arr[i] > 0) {
     
                if(arr[i] == res) res++;
            }
        }
        return res;
    }
};

矩阵的最小路径和(动态规划)

给定一个 n * m 的矩阵 a,从左上角开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,输出所有的路径中最小的路径和。

用动态规划,dp[i][j] = arr[i][j]+min(dp[i-1][j], dp[i][j-1])

class Solution {
     
public:
    /**
     * 
     * @param matrix int整型vector> the matrix
     * @return int整型
     */
    int minPathSum(vector<vector<int> >& matrix) {
     
        // write code here
        if(matrix.size() == 0) return 0;
        int rows = matrix.size();
        int cols = matrix[0].size();
        vector<vector<int> > dp(rows, vector<int>(cols));
        dp[0][0] = matrix[0][0];
        for(int i=1; i<cols; i++) {
     
            dp[0][i] = dp[0][i-1] + matrix[0][i];
        }
        for(int i=1; i<rows; i++) {
     
            dp[i][0] = dp[i-1][0] + matrix[i][0];
        }
        for(int i=1; i<rows; i++) {
     
            for(int j=1; j<cols; j++) {
     
                dp[i][j] = matrix[i][j] + min(dp[i-1][j], dp[i][j-1]);
            }
        }
        return dp[rows-1][cols-1];
    }
};

最小编辑代价(动态规划)

给定两个字符串str1和str2,再给定三个整数ic,dc和rc,分别代表插入、删除和替换一个字符的代价,请输出将str1编辑成str2的最小代价。

用动态规划,dp[i][j]表示str1的前i个字符编辑成str2的前j个字符需要的最小操作数
初始状态,dp[i][0]=idc,i次删除,dp[0][i]=iic,i次插入,dp[0][i]=i*ic,i次插入
当i字符等于j字符时:dp[i][j] = dp[i-1][j-1],不需要额外操作
当i字符不等于j字符时:dp[i][j] = Math.min(insert, delete, replace)
int insert = dp[i][j-1] + 1; i个编辑成j-1个字符,再插入一个j
int delete = dp[i-1][j] + 1; i-1个编辑成j个字母,再删除一个i
int replace = dp[i-1][j-1] + 1; i-1个编辑成j-1个字母,再将i替换成j
之后用一维数组节省空间

class Solution {
     
public:
    /**
     * min edit cost
     * @param str1 string字符串 the string
     * @param str2 string字符串 the string
     * @param ic int整型 insert cost
     * @param dc int整型 delete cost
     * @param rc int整型 replace cost
     * @return int整型
     */
    int minEditCost(string str1, string str2, int ic, int dc, int rc) {
     
        // write code here
        int size1 = str1.length(), size2 = str2.length();
        vector<int> dp;
        for(int i=0; i<=size2; i++) {
     
            dp.push_back(i*ic);
        }
        int prev;
        for(int i=1; i<=size1; i++) {
     
            prev = dp[0];
            dp[0] = i*dc;
            for(int j=1; j<=size2; j++) {
     
                int temp = dp[j];    //dp[i-1][j]
                if(str1[i-1] == str2[j-1]) dp[j] = prev;
                else {
     
                    int insert = dp[j-1] + ic; // dp[i][j-1]+ic
                    int del = dp[j] + dc; // dp[i-1][j] + dc
                    int rep = prev + rc; // dp[i-1][j-1]+1
                    dp[j] = min(min(insert, del), rep);
                }
                prev = temp;
            }
        }
        return dp[size2];
    }
};

合并区间

给出一组区间,请合并所有重叠的区间。

先对区间进行排序,记录当前区间的边界值,然后和下一个区间比较,如果在该范围内,则更新边界值

class Solution {
     
public:
    vector<Interval> merge(vector<Interval> &intervals) {
     
        if(intervals.size() <= 1) return intervals;
        sort(intervals.begin(), intervals.end(), cmp);
        int left = intervals[0].start, right = intervals[0].end;
        vector<Interval> res;
        for(int i=1; i<intervals.size(); i++) {
     
            if(intervals[i].start >= left && intervals[i].start <= right) {
     
                right = max(intervals[i].end, right);
            }
            else {
         //如果当前的区间不在之前的区间范围内,就把之前的区间范围放入到res中
                Interval temp(left, right);
                res.push_back(temp);
                left = intervals[i].start;
                right = intervals[i].end;
            }
        }
        Interval temp(left, right);
        res.push_back(temp);
        return res;
    }
    static bool cmp(const Interval &int1, const Interval &int2) {
     
        return int1.start < int2.start;
    }
};

有重复项数字的所有排列(回溯)

给出一组可能包含重复项的数字,返回该组数字的所有排列

在剑指中有一道类似的题,是对字符串进行全排列,然后对vector进行去重。这道题目如果用类似的回溯法,就比较难进行去重,因为unique是对相邻的重复元素进行去重,所以如果用回溯法就需要用set来进行去重。另一个方法是不进行回溯,也不对数组进行修改,

class Solution {
     
public:
    vector<vector<int> > permuteUnique(vector<int> &num) {
     
        vector<vector<int> > res;
        set<vector<int>> st;    //如果不去重 [1,2,2]就会有问题,因为只判断begin和i有没有重复,不判断后面的i有没有重复
        dfs(st, num, 0);
        for (auto vec : st) res.push_back(vec);
        return res;
    }
 
    void dfs(set<vector<int> > &st, vector<int> &num, int idx) {
     
        if (idx == num.size()-1) {
      st.insert(num); return; }
        for (int i = idx; i < num.size(); ++i) {
     
            if (i != idx && num[i] == num[idx]) continue; // 剪枝
            swap(num[i], num[idx]);
            dfs(st, num, idx+1);
            swap(num[i], num[idx]);
        }
    }
};
class Solution {
     
public:
    void permute(vector<vector<int>> &res,vector<int> num,int index,int n)	//num传进去的不是引用
    {
     
        if(index==n-1)
        {
     
            res.push_back(num);
            return;
        }
        for(int i=index;i<n;i++)
        {
     
            if(i==index||(i!=index&&num[i]!=num[index]))
            {
     
                swap(num[index],num[i]);    
                permute(res,num,index+1,n);
                //swap(num[index],num[i]);
            }
            
        }
    }
    vector<vector<int> > permuteUnique(vector<int> &num)
    {
     
        sort(num.begin(),num.end());
        vector<vector<int>> res;
        permute(res,num,0,num.size());
        return res;
    }
};

最长的括号子串(动态规划)

给出一个仅包含字符’(‘和’)'的字符串,计算最长的格式正确的括号子串的长度。
对于字符串"(()“来说,最长的格式正确的子串是”()",长度为2.
再举一个例子:对于字符串")()())",来说,最长的格式正确的子串是"()()",长度为4

动态规划,dp[i]是以i为结尾的最长括号子串长度,如果str[i]为左括号则为0,如果为有括号则找到与它相匹配的那个括号prev

class Solution {
     
public:
    /**
     * 
     * @param s string字符串 
     * @return int整型
     */
    int longestValidParentheses(string s) {
     
        // write code here
        vector<int> dp(s.size(), 0);
        int maxlen = 0;
        for(int i=1; i<s.size(); i++) {
     
            if(s[i] == ')') {
     
                int prev = i-1-dp[i-1];    //prev是与i相匹配的括号
                if(prev >= 0 && s[prev] == '(') {
     
                    dp[i] = dp[i-1] + 2;    //先加上现在配对的这一对
                    if(prev >= 1) dp[i] += dp[prev-1];    //再加上之前的
                }
            }
            if(dp[i] > maxlen) maxlen = dp[i];
        }
        return maxlen;
    }
};

将字符串转化为整数

实现函数 atoi 。函数的功能为将字符串转化为整数

这道题目的参数是字符串指针,要注意字符串指针在遍历的时候要while(*str)而不是while(str),因为字符串末尾遇到\0,*str会为0,但是字符串末尾并不是空指针,而是会指到分配的内存空间之外

class Solution {
     
public:
    int atoi(const char *str) {
     
        long long sum = 0;
        int flag = 0, num = 0;
        while(*str) {
     
            if(num == 0 && *str == '-') {
     flag = 1; str++; num++; continue;}
            else if(num == 0 && *str == '+') {
     str++; num++; continue;}
            else if(*str == ' ') {
     str++; continue;}
            else if(!(*str >= '0' && *str <='9')) break;
            sum = sum*10 + (*str - '0');
            if(num == 0) num++;
            if(flag == 0 && sum > INT_MAX) return INT_MAX;
            if(flag == 1 && sum > INT_MAX) return INT_MIN;
            str++;
        }
        int res = sum;
        return flag == 0? res: -res;
    }
};

最长公共子序列

给定两个字符串str1和str2,输出连个字符串的最长公共子序列。如过最长公共子序列为空,则输出-1

先求最长公共子序列的长度,然后再根据dp求子序列。动态规划求长度,dp[i][j]表示到i和到j的公共长度,
if s1[I]=s2[j] dp[i][j] = dp[i-1][j-1]+1
else dp[I][j] = max{dp[I-1[j], dp[i]j-1]}

class Solution {
     
public:
    /**
     * longest common subsequence
     * @param s1 string字符串 the string
     * @param s2 string字符串 the string
     * @return string字符串
     */
    string LCS(string s1, string s2) {
     
        // write code here
        int dp[5005][5005];
        memset(dp, 0, sizeof(dp));
        int maxlen = 0;
        for(int i=1; i<=s1.size(); i++) {
     
            for(int j=1; j<=s2.size(); j++) {
     
                if(s1[i-1] == s2[j-1]) {
     
                    dp[i][j] = dp[i-1][j-1] + 1;
                } else {
     
                    dp[i][j] = max(dp[i][j-1], dp[i-1][j]);
                }
            }
        }
        int j = s2.size() - 1, i = s1.size() - 1;
        string res = "";
        while(i>=0 && j>=0) {
     
            if(s1[i] == s2[j]) {
     
                res = s1[i] + res;
                i--;
                j--;
            } else {
     
                if(dp[i+1][j] > dp[i][j+1]) j--;
                else i--;
            }
        }
        return res==""? "-1": res;
    }
};

丢棋子(动态规划)

一座大楼有0~N层,地面算作第0层,最高的一次为第N层,已知棋子从第0层掉落肯定不会摔碎,从第i层掉落可能会摔碎,也可能不会摔碎,给定整数N作为楼层数,再给定K作为棋子数,返回如果想找到棋子不会摔碎的最高层数,即使在最差的情况下扔的最小次数

见丢棋子问题
最后的一维数组的解是这样的,dp[i][j]为i个棋子扔j次可以到达的最大楼层数,dp[i][j] = dp[i][j-1]+dp[i-1][j-1]+1,就是第一次在某层先扔一次,如果碎了,就向下扔用i-1个扔j-1次,如果没碎就向上扔,最终的总楼层就是这一层加上向下扔的楼层加上向上扔的楼层

class Solution {
     
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 返回最差情况下扔棋子的最小次数
     * @param n int整型 楼层数
     * @param k int整型 棋子数
     * @return int整型
     */
    int solve(int n, int k) {
     
        // write code here
        if(n < 1) return 0;
        if(k == 1) return n;
        int logtimes = log2(n) + 1;
        if(k >= logtimes) return logtimes;
        vector<int> dp(k+1, 0);
        int cnt = 0;
        while(dp[k] < n) {
     
            cnt++;
            for(int i=k; i>0; i--) {
     
                dp[i] += dp[i-1] + 1;
            }
        }
        return cnt;
    }
};

判断一棵树是否为搜索二叉树和完全二叉树

给定一棵二叉树,已经其中没有重复值的节点,请判断该二叉树是否为搜索二叉树和完全二叉树。

判断是否为搜索二叉树,用中序遍历的方式,要记录下每次遍历的前一个点,然后去和当前节点值进行比较。判断是否为完全二叉树,就记录下每个节点的编号,空节点也进行编号,然后判断编号是否连续

class Solution {
     
public:
    /**
     * 
     * @param root TreeNode类 the root
     * @return bool布尔型vector
     */
    vector<bool> judgeIt(TreeNode* root) {
     
        // write code here
        vector<bool> res;
        int pre = INT_MIN;
        res.push_back(isSearch(root, pre));
        res.push_back(isTotal(root));
        return res;
    }
    bool isSearch(TreeNode* root, int pre) {
         //要注意不仅要判断根节点和左右子节点的大小关系,左子树的右孩子也需要小于根节点,所以用中序遍历,记录下遍历的之前的一个结点
        if(root == nullptr) return true;
        if(!isSearch(root->left, pre)) return false;
        if(root->val < pre) return false;
        pre = root->val;
        if(!isSearch(root->right, pre)) return false;
        return true;
    }
    bool isTotal(TreeNode* root) {
     
        if(root == nullptr) return true;
        queue<TreeNode*> que;
        vector<int> nums;
        int sum = 0;
        que.push(root);
        nums.push_back(sum++);
        while(!que.empty()) {
     
            int size = que.size();
            while(size--) {
     
                TreeNode *temp = que.front();
                que.pop();
                if(temp->left) {
     que.push(temp->left); nums.push_back(sum++);}
                else sum++;
                if(temp->right) {
     que.push(temp->right); nums.push_back(sum++);}
                else sum++;
            }
        }
        for(int i=1; i<nums.size(); i++) {
     
            if(nums[i] != (nums[i-1]+1)) return false;
        }
        return true;
    }
};

删除有序链表中重复的元素

删除给出链表中的重复元素(链表中元素从小到大有序),使链表中的所有元素都只出现一次
例如:
给出的链表为1->1->2,返回1->2

class Solution {
     
public:
    /**
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    ListNode* deleteDuplicates(ListNode* head) {
     
        // write code here
        ListNode *p = head, *r, *temp;
        while(p) {
     
            if(p->next && p->val == p->next->val) {
     
                r = p;
                while(p->next && p->val == p->next->val) {
     
                    p = p->next;
                }
                r->next = p->next;
                p = p->next;
            } else {
     
                p = p->next;
            }
        }
        return head;
    }
};

加起来为目标值的组合(回溯)

给出一组候选数 C 和一个目标数 T,找出候选数中起来和等于 T 的所有组合。 C 中的每个数字在一个组合中只能使用一次。

回溯法,要注意的有两点,第一是这里和排列组合不一样,排列组合是所有的字母都要出现,所以去重要比较num[i]和num[begin],这个是判断num[i]和num[i-1]。第二点要注意的是这里用加法可能会溢出,所以要用减法

class Solution {
     
public:
    vector<vector<int> > combinationSum2(vector<int> &num, int target) {
     
        vector<int> path;
        vector<vector<int> > res;
        if(num.empty()) return res;
        sort(num.begin(), num.end());
        combineSum(path, res, target, 0, num);
        return res;
    }
    void combineSum(vector<int>& path, vector<vector<int> >& res, int target, int begin, vector<int>& num) {
     
        if(target == 0) {
     
            res.push_back(path);
            return;
        }
        int n = num.size();
        if(begin >= n) return;
        for(int i=begin; i<n; i++) {
     
            if(i!=begin && num[i] == num[i-1]) continue;
            if(num[i] > target) return;
            path.push_back(num[i]);
            combineSum(path, res, target-num[i], i+1, num);    //一开始用加法会超时是因为加法会溢出,所以用减法比较好
            path.pop_back();
        }
    }
};

排序

给定一个数组,请你编写一个函数,返回该数组排序后的形式。

用快排

class Solution {
     
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 将给定数组排序
     * @param arr int整型vector 待排序的数组
     * @return int整型vector
     */
    vector<int> MySort(vector<int>& arr) {
     
        // write code here
        quicksort(arr, 0, arr.size() - 1);
        return arr;
    }
    int partition(vector<int>& arr, int i, int j) {
     
        int temp = arr[i];
        while(i < j) {
     
            while(i < j && arr[j] >= temp) j--;
            arr[i] = arr[j];
            while(i < j && arr[i] <= temp) i++;
            arr[j] = arr[i];
        }
        arr[j] = temp;
        return j;
    }
    void quicksort(vector<int>& arr, int left, int right) {
     
        if(left >= right) return;
        int index = partition(arr, left, right);
        quicksort(arr, left, index-1);
        quicksort(arr, index+1, right);
    }
};

数组中的最长连续子序列

给定无序数组arr,返回其中最长的连续序列的长度(要求值连续,位置可以不连续,例如 3,4,5,6为连续的自然数)
输入:[100, 4, 200, 1, 3, 2]
输出:4

先排序然后去重,然后统计数组中的连续最大长度

class Solution {
     
public:
    /**
     * max increasing subsequence
     * @param arr int整型vector the array
     * @return int整型
     */
    int MLS(vector<int>& arr) {
     
        // write code here
        sort(arr.begin(), arr.end());
        auto it = unique(arr.begin(), arr.end());
        arr.erase(it, arr.end());
        int sum = 1, maxlen = 0;
        if(arr.size() == 1) return 1;
        for(int i=1; i<arr.size(); i++) {
     
            if(arr[i] == (arr[i-1] + 1)) {
     
                sum++;
                if(sum > maxlen) maxlen = sum;
            }
            else {
     
                sum = 1;
            }
        }
        return maxlen;
    }
};

大数乘法

以字符串的形式读入两个数字,编写一个函数计算它们的乘积,以字符串形式返回。

第i位和第j位的字符相乘,得到的结果存储道res[i+j+1]中,为了给进位腾出位置

class Solution {
     
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 
     * @param s string字符串 第一个整数
     * @param t string字符串 第二个整数
     * @return string字符串
     */
    string solve(string s, string t) {
     
        // write code here
        int len1 = s.length();
        int len2 = t.length();
        string res(len1+len2, '0');
        for(int i=len1-1; i>=0; i--) {
     
            for(int j=len2-1; j>=0; j--) {
     
                int temp = (s[i]-'0')*(t[j]-'0') + (res[i+j+1]-'0');    //这个没有逆序,所以为了给进位留出位置,res要i+j+1
                res[i+j+1] = temp % 10 + '0';
                res[i+j] = res[i+j] + temp/10;
            }
        }
        for(int i=0; i<res.size(); i++) {
     
            if(res[i] != '0') {
     
                return res.substr(i);
            }
        }
        return "0";
    }
};

判断二叉树是否对称

class Solution {
     
public:
    /**
     * 
     * @param root TreeNode类 
     * @return bool布尔型
     */
    bool isSymmetric(TreeNode* root) {
     
        // write code here
        if(root == nullptr) return true;
        return isSym(root->left, root->right);
    }
    bool isSym(TreeNode* root1, TreeNode* root2) {
     
        if(root1 == nullptr && root2 == nullptr) return true;
        if(root1 == nullptr || root2 == nullptr) return false;
        if(root1->val != root2->val) return false;
        return isSym(root1->left, root2->right) && isSym(root1->right, root2->left);
    }
};

数字字符串转化成IP地址(回溯)

现在有一个只包含数字的字符串,将该字符串转化成IP地址的形式,返回所有可能的情况。

要注意考虑特殊情况,字符串必须分成4组,每一组的数字不得超出255,每一个数字不能出现010的情况,也不能出现000的情况

class Solution {
     
public:
    /**
     * 
     * @param s string字符串 
     * @return string字符串vector
     */
    vector<string> restoreIpAddresses(string s) {
     
        // write code here
        vector<string> res;
        vector<string> nums;
        IpAddress(s, nums, res, 0);
        return res;
    }
    void IpAddress(string s, vector<string> &nums, vector<string>& res, int begin) {
     
        if(begin == s.size()) {
     
            if(nums.size() != 4) return;
            string str = "";
            for(int i=0; i<nums.size(); i++) {
     
                int temp_num = to_int(nums[i]);
                if(temp_num > 255) return;
                if(nums[i].size()>1 && temp_num == 0) return;
                if(nums[i][0] == '0' && temp_num != 0) return;
                if(i != 0) str += ".";
                string temp = nums[i];
                str += temp;
            }
            res.push_back(str);
            return;
        }
        string num = "";
        for(int i=begin; i<s.size() && i<begin+3; i++) {
     
            num += s[i];
            nums.push_back(num);
            IpAddress(s, nums, res, i+1);
            nums.pop_back();
        }
        
    }
    int to_int(string str) {
     
        int sum = 0;
        for(int i=0; i<str.size(); i++) {
     
            sum = sum*10 + str[i] - '0';
        }
        return sum;
    }
};

重排链表

将给定的单链表L:L0->L1->L2……->Ln
重新排序位L0->Ln->L1->Ln-1……
要求使用原地算法,不能改变节点内部的值,需要对实际的节点进行交换

例如1->2->3->4,先变成1->2->4->3,然后拆分成1->2和4->3,然后将两个链表进行合并

class Solution {
     
public:
    void reorderList(ListNode *head) {
     
        int sum = 0;
        ListNode *p = head, *r = nullptr, *tail;
        while(p) {
     
            sum++;
            p = p->next;
        }
        if(sum <= 2) return;
        int num = sum%2==0? sum/2: (sum/2+1);
        p = head;
        //对链表节点进行计数
        while(num--) {
     
            tail = p;
            p = p->next;
        }
        //将后半段进行翻转
        while(p) {
     
            ListNode *temp = p->next;
            tail->next = p;
            p->next = r;
            r = p;
            p = temp;
        }
        r = nullptr;
        ListNode *p1 = head, *p2 = tail->next;
        tail->next = nullptr;    //彻底将两个链表分开
        //合并两个链表
        while(p1 && p2) {
     
            ListNode *temp1 = p1->next;
            ListNode *temp2 = p2->next;
            p1->next = p2;
            p1 = temp1;
            p2->next = p1;
            p2 = temp2;
        }
    }
};

二叉树中是否存在节点和为指定值的路径(回溯)

给定一个二叉树和一个值 sum,判断是否有从根节点到叶子节点的节点值之和等于 sum 的路径,若存在返回true

回溯

class Solution {
     
public:
    /**
     * 
     * @param root TreeNode类 
     * @param sum int整型 
     * @return bool布尔型
     */
    bool hasPathSum(TreeNode* root, int sum) {
     
        // write code here
        if(root == nullptr) return false;
        return hasPath(root, sum);
    }
    bool hasPath(TreeNode* root, int sum) {
     
        if(root == nullptr) return false;
        sum -= root->val;
        if(root->left == nullptr && root->right == nullptr) {
     
            if(sum == 0) return true;
            else return false;
        }
        bool flag1 = false, flag2 = false;
        if(root->left) flag1 = hasPath(root->left, sum);
        if(root->right) flag2 = hasPath(root->right, sum);
        return flag1 || flag2;
    }
};

求路径

一个机器人在mxn大小的地图上的左上角,机器人每次向下或向右移动。机器人要到达地图的右上角。可以有多少种不同的路径从起点走到终点?

动态规划,dp[i][j]代表到达i,j有多少种路径,dp[i][j] = dp[i-1][j]+dp[i][j-1]

class Solution {
     
public:
    /**
     * 
     * @param m int整型 
     * @param n int整型 
     * @return int整型
     */
    int uniquePaths(int m, int n) {
     
        // write code here
        int dp[105][105];
        for(int i=0; i<n; i++) {
     
            dp[0][i] = 1;
        }
        for(int i=0; i<m; i++) {
     
            dp[i][0] = 1;
        }
        for(int i=1; i<m; i++) {
     
            for(int j=1; j<n; j++) {
     
                dp[i][j] = dp[i-1][j] + dp[i][j-1];
            }
        }
        return dp[m-1][n-1];
    }
};

换钱的最少货币数(动态规划)

给定数组arr,arr中所有的值都为正整数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个aim,代表要找的钱数,求组成aim的最少货币数。
如果无解,请返回-1.

动态规划,dp[i]表示的是组成总面值为i的货币需要的张数,dp[i]=min(dp[i], dp[i-arr[j]]+1)

class Solution {
     
public:
    /**
     * 最少货币数
     * @param arr int整型vector the array
     * @param aim int整型 the target
     * @return int整型
     */
    int minMoney(vector<int>& arr, int aim) {
     
        // write code here
        vector<int> dp(aim+1, 6000);    //这里如果是INT_MAX 会溢出
        dp[0] = 0;
        for(int i=1; i<aim+1; i++) {
     
            for(int j=0; j<arr.size(); j++) {
     
                if(i < arr[j]) continue;
                dp[i] = min(dp[i], dp[i-arr[j]]+1);
            }
        }
        return dp[aim]==6000? -1: dp[aim];
    }
};

集合的所有子集(回溯)

现在有一个没有重复元素的整数集合S,求S的所有子集

回溯

class Solution {
     
public:
    vector<vector<int> > subsets(vector<int> &S) {
     
        vector<vector<int> > res;
        vector<int> nums;
        for(int len=0; len<=S.size(); len++) {
     
            subset(S, res, nums, 0, len);
        }
        return res;
    }
    void subset(vector<int> S, vector<vector<int> >& res, vector<int>& nums, int begin, int len) {
     
        if(len == 0) {
     
            res.push_back(nums);
        }
        for(int i=begin; i<S.size(); i++) {
     
            nums.push_back(S[i]);
            subset(S, res, nums, i+1, len-1);    //注意这里是i+1而不是begin+1,在全排列那里是begin+1,是因为那个交换了数字,所以每次都要begin+1这个位置开始
            nums.pop_back();
        }
    }
};

N皇后问题(DFS不需要回溯)

N皇后问题是指在N*N的棋盘上摆N个皇后,要求,任何两个皇后不同行,不同列,也不能在同一条斜线上,返回N皇后的摆法数

用DFS,先摆放第一行的棋子,在第一行的每一列进行摆放,然后继续搜索下一行。同时要判断不在同一行同一列也不在同一条斜线上
为什么不需要回溯,是因为每次判断可不可以放棋子都是在和之前的行进行比较,所以后面设置的不重要,而回溯是为了把后面设置的清空

class Solution {
     
public:
    /**
     * 
     * @param n int整型 the n
     * @return int整型
     */
    int sum = 0;
    int Nqueen(int n) {
     
        // write code here
        vector<int> visited(n);
        dfs(visited, 0, n);
        return sum;
    }
    void dfs(vector<int>& visited, int row, int n) {
     
        if(row == n) {
     
            sum++;
        }
        else for(int i=0; i<n; i++) {
         //每一列
            int flag = 1;
            visited[row] = i;    //第row行置为i
            for(int j=0; j<row; j++) {
     
                if(visited[j] == visited[row] || row-i==j-visited[j] || row+i==j+visited[j]) {
     
                    flag = 0;
                    break;
                }
            }
            if(flag == 1) dfs(visited, row+1, n);
        }
    }
};

LFU缓存结构设计

题目要求与LRU类似,LRU是最近最久未使用,LFU是最少使用

与LRU不同的是要记录每一条记录的时间与频次,而且要注意每一个key应该只有一条记录,如果set时这个key已经存在,那么就更新这个key对应的记录。还要注意的是把这些记录都加入到set中,因为set可以自动排序,这里要修改set的排序方式

#include 
struct Node {
     
    int key;
    int value;
    int cnt;
    int time;
    Node(int _key, int _value, int _cnt, int _time):
        key(_key), value(_value), cnt(_cnt), time(_time){
     
    }
    bool operator< (const Node& othernode) const {
     
        return cnt==othernode.cnt? time<othernode.time: cnt<othernode.cnt;
    }
};
class Solution {
     
public:
    /**
     * lfu design
     * @param operators int整型vector> ops
     * @param k int整型 the k
     * @return int整型vector
     */
    unordered_map<int, Node> cache;
    set<Node> s;
    int time = 0;
    vector<int> LFU(vector<vector<int> >& operators, int k) {
     
        // write code here
        vector<int> res;
        for(int i=0; i<operators.size(); i++) {
     
            if(operators[i][0] == 1) {
     
                int key = operators[i][1];
                int value = operators[i][2];
                set(key, value, k);
            } else if(operators[i][0] == 2) {
     
                int key = operators[i][1];
                res.push_back(get(key, k));
            }
        }
        return res;
    }
    void set(int key, int value, int k) {
     
        if(k == 0) return;
        auto iter = cache.find(key);
        if(iter == cache.end()) {
     
            if(s.size() == k) {
     
                cache.erase(s.begin()->key);
                s.erase(s.begin());
            }
            Node tempnode(key, value, 1, ++time);
            cache.insert({
     key, tempnode});
            s.insert(tempnode);
        } else {
     
            Node tempnode = iter->second;
            s.erase(tempnode);
            tempnode.cnt++;
            tempnode.time = ++time;
            tempnode.value = value;
            iter->second = tempnode;
            s.insert(tempnode);
        }
    }
    int get(int key, int k) {
     
        if(k == 0) return -1;
        auto iter = cache.find(key);
        if(iter == cache.end()) {
     
            return -1;
        }
        Node tempnode = iter->second;
        s.erase(tempnode);
        tempnode.cnt++;
        tempnode.time = ++time;
        s.insert(tempnode);
        iter->second = tempnode;
        return tempnode.value;
    }
};

股票(无限次交易)

假定你知道某只股票每一天价格的变动。
你最多可以同时持有一只股票。但你可以无限次的交易(买进和卖出均无手续费)。
请设计一个函数,计算你所能获得的最大收益。

用贪心法,如果某天的价格比前一天的高,那么就买入卖出再买入

class Solution {
     
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 计算最大收益
     * @param prices int整型vector 股票每一天的价格
     * @return int整型
     */
    int maxProfit(vector<int>& prices) {
     
        // write code here
        if(prices.size() == 0) return 0;
        int profit = 0;
        for(int i=1; i<prices.size(); i++) {
     
            if(prices[i] > prices[i-1]) {
     
                profit += (prices[i] - prices[i-1]);
            }
        }
        return profit;
    }
};

合并二叉树

已知两颗二叉树,将它们合并成一颗二叉树。合并规则是:都存在的结点,就将结点值加起来,否则空的位置就由另一个树的结点来代替。
输入:{1,3,2,5},{2,1,3,#,4,#,7}
输出:{3,4,5,5,4,#,7}

class Solution {
     
public:
    /**
     * 
     * @param t1 TreeNode类 
     * @param t2 TreeNode类 
     * @return TreeNode类
     */
    TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
     
        // write code here
        if(t1!=nullptr && t2!=nullptr) t1->val = t1->val + t2->val;
        else if(t1==nullptr && t2!=nullptr) {
     
            t1 = t2; 
            t2 = nullptr; 
            return t1;
        }
        else if(t1!=nullptr && t2==nullptr) return t1;
        else if(t1==nullptr && t2==nullptr) return nullptr;
        t1->left = mergeTrees(t1->left, t2->left);
        t1->right = mergeTrees(t1->right, t2->right);
        return t1;
    }
};

矩阵乘法

给定两个nn的矩阵A和B,求AB。

class Solution {
     
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 
     * @param a int整型vector> 第一个矩阵
     * @param b int整型vector> 第二个矩阵
     * @return int整型vector>
     */
    vector<vector<int> > solve(vector<vector<int> >& a, vector<vector<int> >& b) {
     
        // write code here
        if(a.size() == 0 || b.size() == 0) return {
     };
        int rows_a = a.size(), cols_a = a[0].size(), rows_b = b.size(), cols_b = b[0].size();
        vector<vector<int> > res(rows_a, vector<int>(cols_b));
        for(int i=0; i<rows_a; i++) {
     
            for(int j=0; j<cols_b; j++) {
     
                res[i][j] = 0;
                for(int m=0; m<cols_a; m++) {
     
                    res[i][j] += (a[i][m] * b[m][j]);
                }
            }
        }
        return res;
    }
};

旋转字符串

给定两字符串A和B,如果能将A从中间某个位置分割为左右两部分字符串(都不为空串),并将左边的字符串移动到右边字符串后面组成新的字符串可以变为字符串B时返回true。
例如:如果A=‘youzan’,B=‘zanyou’,A按‘you’‘zan’切割换位后得到‘zanyou’和B相同返回true。

将两个A拼接起来,然后检索里面是否有B

class Solution {
     
public:
    /**
     * 旋转字符串
     * @param A string字符串 
     * @param B string字符串 
     * @return bool布尔型
     */
    bool solve(string A, string B) {
     
        // write code here
        if(A.length() != B.length()) return false;
        for(int i=0; i<A.length(); i++) {
     
            if((A+A).substr(i, A.size()) == B) return true;
        }
        return false;
    }
};

判断t1树中是否有t2树拓扑结构

class Solution {
     
public:
    /**
     * 
     * @param root1 TreeNode类 
     * @param root2 TreeNode类 
     * @return bool布尔型
     */
    bool isContains(TreeNode* root1, TreeNode* root2) {
     
        // write code here
        if(root1 == nullptr && root2 == nullptr) return true;
        if(root1 == nullptr || root2 == nullptr) return false;
        if(root1->val == root2->val) return isSame(root1, root2);
        return isContains(root1->left, root2) || isContains(root1->right, root2);
    }
    bool isSame(TreeNode* root1, TreeNode* root2) {
     
        if(root1 == nullptr && root2 == nullptr) return true;
        if(root1 == nullptr || root2 == nullptr) return false;
        if(root1->val == root2->val) {
     
            return isSame(root1->left, root2->left) && isSame(root1->right, root2->right);
        }
        return false;
    }
};

矩阵最长递增路径(DFS)

给定一个矩阵,矩阵内所有数均为非负整数。
求一条路径,该路径上所有数是递增的。
这个路径必须满足以下条件:
1、对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外。
2、你不能走重复的单元格。即每个格子最多只能走一次。

DFS

class Solution {
     
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 递增路径的最大长度
     * @param matrix int整型vector> 描述矩阵的每个数
     * @return int整型
     */
    int maxlen = 0;
    int dis[4][2] = {
     {
     0, 1}, {
     0, -1}, {
     1, 0}, {
     -1, 0}};
    int rows, cols;
    int solve(vector<vector<int> >& matrix) {
     
        // write code here
        if(matrix.size() == 0) return 0;
        rows = matrix.size();
        cols = matrix[0].size();
        vector<vector<int> > visited(rows, vector<int>(cols));
        for(int i=0; i<rows; i++) {
     
            for(int j=0; j<cols; j++) {
     
                dfs(matrix, i, j, visited, 1);
            }
        }
        return maxlen;
    }
    void dfs(vector<vector<int> >& matrix, int x, int y, vector<vector<int> >& visited, int len) {
         //matrix没有用引用会运行超时
        visited[x][y] = 1;
        if(len > maxlen) maxlen = len;
        for(int i=0; i<4; i++) {
     
            int next_x = x + dis[i][0];
            int next_y = y + dis[i][1];
            if(next_x >= 0 && next_x < rows && next_y >=0 && next_y < cols && visited[next_x][next_y] == 0) {
     
                if(matrix[next_x][next_y] > matrix[x][y]) {
     
                    dfs(matrix, next_x, next_y, visited, len+1);
                }
            }
        }
        visited[x][y] = 0;
    }
};

股票交易的最大收益(两次交易)

假定你知道某只股票每一天价格的变动。
你最多可以同时持有一只股票。但你最多只能进行两次交易(一次买进和一次卖出记为一次交易。买进和卖出均无手续费)。
请设计一个函数,计算你所能获得的最大收益。

分两次计算,一次是从前到后,一次是从后到前

class Solution {
     
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 两次交易所能获得的最大收益
     * @param prices int整型vector 股票每一天的价格
     * @return int整型
     */
    int maxProfit(vector<int>& prices) {
     
        // write code here
        if(prices.size() == 0) return 0;
        int n = prices.size(), price = prices[0];
        vector<int> profit1(n);    //profit1[i]表示从开始到i的一次最大收益
        vector<int> profit2(n);    //profit2[i]表示从i到最后的一次最大收益
        profit1[0] = 0;
        for(int i=1; i<n; i++) {
     
            if(prices[i] < price) price = prices[i];
            profit1[i] = max(profit1[i-1], prices[i]-price);
        }
        profit2[n-1] = 0;
        price = prices[n-1];
        for(int i=n-2; i>=0; i--) {
     
            if(prices[i] > price) price = prices[i];
            profit2[i] = max(profit2[i+1], price-prices[i]);
        }
        int res = 0;
        for(int i=0; i<n; i++) {
     
            res = max(res, profit1[i]+profit2[i]);
        }
        return res;
    }
};

完全二叉树结点数

给定一棵完全二叉树的头节点head,返回这棵树的节点个数。如果完全二叉树的节点数为N,请实现时间复杂度低于O(N)的解法。

先判断左子树和右子树高度,如果子数高度相等,则左子树是满二叉树,否则右子树是满二叉树,结点总数=满二叉子树结点个数+root+另一棵子树结点数

class Solution {
     
public:
    int nodeNum(struct TreeNode* head) {
     
        if(head == nullptr) return 0;
        int leftheight = getHeight(head->left);
        int rightheight = getHeight(head->right);
        if(leftheight == rightheight + 1) {
         //右子树是满二叉树
            return pow(2, rightheight) - 1 + 1 + nodeNum(head->left);
        } else {
     
            return pow(2, leftheight) - 1 + 1 + nodeNum(head->right);
        }
    }
    int getHeight(TreeNode *head) {
     
        int h = 0;
        TreeNode *p = head;
        while(p) {
     
            h++;
            p = p->left;
        }
        return h;
    }
};

未排序数组中累加和为给定值的最长子数组

给定一个无序数组arr, 其中元素可正、可负、可0。给定一个整数k,求arr所有子数组中累加和为k的最长子数组长度

假设s(i)是数组arr[0……i]的累加和,s(j)是arr[0……j]的累积和,那么可以求得arr[j+1……i]=s(i)-s(j)
用哈希表来记录(sum, i),遍历i,然后看哈希表中是否存在s(i)-k。若s(i)不在哈希表中,则更新,若存在不需要更新,因为是最长长度
相关问题

#include 
class Solution {
     
public:
    /**
     * max length of the subarray sum = k
     * @param arr int整型vector the array
     * @param k int整型 target
     * @return int整型
     */
    int maxlenEqualK(vector<int>& arr, int k) {
     
        // write code here
        int sum = 0, maxlen = 0;
        unordered_map<int, int> m;
        m[0] = -1;
        for(int i=0; i<arr.size(); i++) {
     
            sum += arr[i];
            if(m.count(sum - k)) {
     
                maxlen = max(maxlen, i-m[sum-k]);
            } 
            if(m.count(sum) == 0) {
     
                m[sum] = i;
            }
        }
        return maxlen;
    }
};

0的个数

给定一个非负整数N,返回N!结果的末尾为0的数量

ret = n/5 + n/25 + n/125

class Solution {
     
public:
    /**
     * the number of 0
     * @param n long长整型 the number
     * @return long长整型
     */
    long long thenumberof0(long long n) {
     
        // write code here
        // ret = n/5 + n/25 + n/125
        long long res = 0;
        long long i = 5;
        while(i <= n) {
     
            res += n/i;
            i *= 5;
        }
        return res;
    }
};

拼接所有的字符串产生字典序最小的字符串

给定一个字符串的数组strs,请找到一种拼接顺序,使得所有的字符串拼接起来组成的字符串是所有可能性中字典序最小的,并返回这个字符串。

修改sort函数

class Solution {
     
public:
    /**
     * 
     * @param strs string字符串vector the strings
     * @return string字符串
     */
    static bool cmp(const string &str1, const string &str2) {
     
        return str1+str2 < str2+str1;
    }
    string minString(vector<string>& strs) {
     
        // write code here
        sort(strs.begin(), strs.end(), cmp);
        string res = "";
        for(int i=0; i<strs.size(); i++) {
     
            res += strs[i];
        }
        return res;
    }
};

比较版本号

如果version1 > version2 返回1,如果 version1 < version2 返回-1,不然返回0.
输入的version字符串非空,只包含数字和字符.。.字符不代表通常意义上的小数点,只是用来区分数字序列。例如字符串2.5并不代表二点五,只是代表版本是第一级版本号是2,第二级版本号是5.

注意可能有多个.

class Solution {
     
public:
    /**
     * 比较版本号
     * @param version1 string字符串 
     * @param version2 string字符串 
     * @return int整型
     */
    int compare(string version1, string version2) {
     
        // write code here
        int v1_num = 0, v2_num = 0;
        int i1 = 0, i2 = 0;
        while(i1 < version1.size() && i2 < version2.size()) {
     
            v1_num = 0;
            v2_num = 0;
            while(i1<version1.size() && version1[i1] != '.') {
     
                v1_num = v1_num*10 + version1[i1++] - '0';
            }
            i1++;
            while(i2<version2.size() && version2[i2] != '.') {
     
                v2_num = v2_num*10 + version2[i2++] - '0';
            }
            i2++;
            if(v1_num < v2_num) return -1;
            else if(v1_num > v2_num) return 1;
        }
        if(i1 < version1.size()) return 1;
        else if(i2 < version2.size()) return -1;
        return 0;
    }
};

划分链表

给出一个链表和一个值 x,以 为参照将链表划分成两部分,使所有小于x 的节点都位于大于或等于x 的节点之前。

定义两个新链表,然后小于x的节点链接到一条链表上,大于x的在另一条链表,之后把这两条链表连接起来

class Solution {
     
public:
    /**
     * 
     * @param head ListNode类 
     * @param x int整型 
     * @return ListNode类
     */
    ListNode* partition(ListNode* head, int x) {
     
        // write code here
        if(head == nullptr) return nullptr;
        ListNode *head1 = new ListNode(0);
        ListNode *head2 = new ListNode(0);
        ListNode *p1 = head1, *p2 = head2, *p = head;
        while(p) {
     
            if(p->val < x) {
     
                p1->next = p;
                p1 = p1->next;
            } else {
     
                p2->next = p;
                p2 = p2->next;
            }
            p = p->next;
        }
        p1->next = head2->next;
        p2->next = nullptr;
        return head1->next;
    }
};

最大正方形(动态规划)

给定一个由0和1组成的2维矩阵,返回该矩阵中最大的由1组成的正方形的面积

动态规划,dp[i][j]表示以(i,j)为右下角的正方形的边长,若matrix[i][j]=0,则dp[i][j]=0,否则dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])+1

class Solution {
     
public:
    /**
     * 最大正方形
     * @param matrix char字符型vector> 
     * @return int整型
     */
    int solve(vector<vector<char> >& matrix) {
     
        // write code here
        int rows = matrix.size(), cols = matrix[0].size();
        vector<vector<int> > dp(rows+1, vector<int>(cols+1));
        int maxlen = 0;
        for(int i=0; i<rows; i++) {
     
            for(int j=0; j<cols; j++) {
     
                if(matrix[i][j] == '0') dp[i+1][j+1] = 0;
                else {
     
                    dp[i+1][j+1] = min(min(dp[i][j+1], dp[i+1][j]), dp[i][j]) + 1;
                }
                if(dp[i+1][j+1] > maxlen) maxlen = dp[i+1][j+1];
            }
        }
        return maxlen*maxlen;
    }
};

找到搜索二叉树中两个错误的节点

一棵二叉树原本是搜索二叉树,但是其中有两个节点调换了位置,使得这棵二叉树不再是搜索二叉树,请按升序输出这两个错误节点的值。(每个节点的值各不相同)

对树进行中序遍历,在遍历的过程中记录cur,如果cur比当前节点大,那么就给res赋值,如果比当前节点小,就更新cur的值

class Solution {
     
public:
    /**
     * 
     * @param root TreeNode类 the root
     * @return int整型vector
     */
    int cur = INT_MIN;
    vector<int> findError(TreeNode* root) {
     
        vector<int> res(2);
        find(root, res);
        return res;
    }
    void find(TreeNode* root, vector<int>& res) {
     
        if(root == nullptr) return;
        find(root->left, res);
        if(cur > root->val) {
     
            res[0] = root->val;
            res[1] = cur;
        } else {
     
            cur = root->val;
        }
        find(root->right, res);
    }
};

字典树的实现

请实现字典树的结构,并包含以下四个主要的功能。void insert(String word):添加word,可重复添加;void delete(String word):删除word,如果word添加过多次,仅删除一次;boolean search(String word):查询word是否在字典树中出现过(完整的出现过,前缀式不算);int prefixNumber(String pre):返回以字符串pre作为前缀的单词数量。

注意类的写法,注意区分class和struct用法

class Trie {
     
    bool isString = false;
    Trie* next[26] = {
     nullptr};
    int cnt = 0;
public:
    void insert(string word) {
     
        Trie *root = this;    //this自身就是个指针,指向实例本身
        for(char w:word) {
     
            if(!root->next[w-'a']) root->next[w-'a'] = new Trie();
            root = root->next[w-'a'];
            root->cnt++;
        }
        root->isString = true;
    }
    void remove(string word) {
     
        Trie *root = this;
        for(char w:word) {
     
            if(!root->next[w-'a']) return;
            root = root->next[w-'a'];
            root->cnt--;
        }
        if(root->cnt == 0) root->isString = false;
    }
    bool search(string word) {
     
        Trie *root = this;
        for(char w:word) {
     
            if(!root->next[w-'a']) return false;
            root = root->next[w-'a'];
        }
        return root->isString;
    }
    int startswith(string prefix) {
     
        Trie *root = this;
        for(char w:prefix) {
     
            if(!root->next[w-'a']) return 0;
            root = root->next[w-'a'];
        }
        return root->cnt;
    }
};
class Solution {
     
public:
    /**
     * 
     * @param operators string字符串vector> the ops
     * @return string字符串vector
     */
    vector<string> trieU(vector<vector<string> >& operators) {
     
        // write code here
        Trie trie;	//注意这里的声明
        vector<string> res;
        for(auto op: operators) {
     
            if(op[0] == "1") trie.insert(op[1]);
            else if(op[0] == "2") trie.remove(op[1]);
            else if(op[0] == "3") res.push_back(trie.search(op[1])?"YES":"NO");
            else if(op[0] == "4") res.push_back(to_string(trie.startswith(op[1])));
        }
        return res;
    }
};

验证IP地址

编写一个函数来验证输入的字符串是否是有效的 IPv4 或 IPv6 地址

class Solution {
     
public:
    /**
     * 验证IP地址
     * @param IP string字符串 一个IP地址字符串
     * @return string字符串
     */
    string solve(string IP) {
     
        // write code here
        if(IP.size() == 0) return "Neither";
        if(IP.find('.') != IP.npos) {
     
            return isIPv4(IP);
        } else if(IP.find(':') != IP.npos) {
     
            return isIPv6(IP);
        }
        return "Neither";
    }
    string isIPv4(string IP) {
     
        int i = 0, cnt = 0;
        while(i < IP.size()) {
     
            int num = 0;
            if(IP[i] == '0') {
     
                if(i==IP.size()-1 || IP[i+1] == '.') {
         //考虑0开头后面有数字或有多个0的情况
                    cnt++;
                    i = i+2;
                    continue;
                } else return "Neither";
            }
            while(i<IP.size() && IP[i] != '.') {
     
                if(!(IP[i]>='0' && IP[i]<='9')) return "Neither";
                num = num* 10 + (IP[i]-'0');
                i++;
            }
            if(num > 255) return "Neither";
            cnt++;
            i++;
        }
        if(cnt != 4) return "Neither";
        return "IPv4";
    }
    string isIPv6(string IP) {
     
        int i = 0, cnt = 0;    //cnt是分组个数
        while(i < IP.size()) {
     
            int zero = 0;
            if(IP[i] == '0') {
     
                while(i<IP.size() && IP[i]=='0') {
     i++; zero++;}    //考虑一个分组全部为0且有多个0的情况
            }
            if(i == IP.size() || IP[i] == ':') {
     
                if(zero > 1) return "Neither";
            } 
            while(i<IP.size() && IP[i] != ':') {
     
                if(!((IP[i]>='0' && IP[i]<='9') || (IP[i]>='a' && IP[i]<='z') || (IP[i]>='A' && IP[i]<='Z'))) return "Neither";
                i++;
            }
            cnt++;
            i++;
        }
        if(cnt != 8) return "Neither";
        return "IPv6";
    }
};

字符串变形

对于一个给定的字符串,我们需要在线性(也就是O(n))的时间里对它做一些变形。首先这个字符串中包含着一些空格,就像"Hello World"一样,然后我们要做的是把着个字符串中由空格隔开的单词反序,同时反转每个字符的大小写。比如"Hello World"变形后就变成了"wORLD hELLO"。

遍历每一个字符,如果没有碰到空格,就顺序添加,遇到空格就将该单词逆序添加到之前的单词

class Transform {
     
public:
    string trans(string s, int n) {
     
        // write code here
        string res = "", word = "";
        for(char w:s) {
     
            if(w != ' ') {
     
                if(w >= 'a' && w <= 'z') {
     
                    word += ('A' + w - 'a');
                }
                else if(w >= 'A' && w <= 'Z') {
     
                    word += ('a' + w - 'A');
                }
            } else {
     
                res = ' ' + word + res;
                word = "";
            }
        }
        res = word + res;    //最后一个单词要单独处理
        return res;
    }
};

最长重复子串

一个重复字符串是由两个相同的字符串首尾拼接而成,例如abcabc便是长度为6的一个重复字符串,而abcba则不存在重复字符串。
给定一个字符串,请编写一个函数,返回其最长的重复字符子串。
若不存在任何重复字符子串,则返回0。

设置窗口,窗口大小逐渐递减,然后窗口向前滑动,判断两个挨着的字符串是否相等

class Solution {
     
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 
     * @param a string字符串 待计算字符串
     * @return int整型
     */
    int solve(string a) {
     
        // write code here
        int n = a.size();
        for(int len = n/2; len>=0; len--) {
     
            for(int i=0; i<=n-len*2; i++) {
     
                int j;
                for(j=i; j<len+i; j++) {
     
                    if(a[j] != a[j+len]) {
     
                        break;
                    }
                }
                if(j == len+i) return len*2;
            }
        }
        return 0;
    }
};

栈和排序

给你一个1->n的排列和一个栈,入栈顺序给定
你要在不打乱入栈顺序的情况下,对数组进行从大到小排序
当无法完全排序时,请输出字典序最大的出栈序列

记录一个数组dp,dp[i]表示从i到最后的最大值,然后元素依次入栈,如果栈顶元素比后面的元素的最大值都大,那么就出栈

class Solution {
     
public:
    /**
     * 栈排序
     * @param a int整型一维数组 描述入栈顺序
     * @param aLen int a数组长度
     * @return int整型vector
     */
    vector<int> solve(int* a, int aLen) {
     
        // write code here
        if(aLen == 0) return {
     };
        vector<int> maxnum(aLen+1, INT_MIN);
        stack<int> s;
        vector<int> res;
        for(int i=aLen-1; i>=0; i--) {
     
            maxnum[i] = max(maxnum[i+1], a[i]);
        }
        for(int i=0; i<aLen; i++) {
     
            while(!s.empty() && s.top() >= maxnum[i]) {
     
                res.push_back(s.top());
                s.pop();
            }
            s.push(a[i]);
        }
        while(!s.empty()) {
     
            res.push_back(s.top());
            s.pop();
        }
        return res;
    }
};

子数组最大乘积(动态规划)

给定一个double类型的数组arr,其中的元素可正可负可0,返回子数组累乘的最大乘积。

动态规划,维护两个数组,一个数组记录包括元素i的最大乘积,一个数组记录包括元素i的最小乘积,记录最小是防止负负得正的情况

class Solution {
     
public:
    double maxProduct(vector<double> arr) {
     
        int n = arr.size();
        double res = INT_MIN;
        vector<double> max_dp(n+1, 1);
        vector<double> min_dp(n+1, 1);
        for(int i=0; i<n; i++) {
     
            max_dp[i+1] = max(max(max_dp[i]*arr[i], min_dp[i]*arr[i]), arr[i]);
            min_dp[i+1] = min(min(max_dp[i]*arr[i], min_dp[i]*arr[i]), arr[i]);
            res = max(max_dp[i+1], res);
        }
        return res;
    }
};

数独(DFS+回溯)

请编写一个程序,给数独中的剩余的空格填写上数字(9*数独)
空格用字符’.'表示
假设给定的数独只有唯一的解法

回溯,从第一行开始,先遍历每一行,如果这一行填完了,就继续填下一行,然后判断同一行同一列九宫格内有没有相同数字,之后回溯

class Solution {
     
int N = 9;
public:
    
    void solveSudoku(vector<vector<char> > &board) {
     
        if(board.size() == 0) return;
        dfs(board, 0, 0);
    }
    bool dfs(vector<vector<char> > &board, int x, int y) {
     
        if(board[x][y] != '.') {
     
            if(x == N-1 && y == N-1) return true;
            if(y < N-1) {
     
                return dfs(board, x, y+1);
            } else if(x < N-1) {
     
                return dfs(board, x+1, 0);
            }
        } else {
     
            for(int i=1; i<=9; i++) {
     
                bool flag = false;
                board[x][y] = i+'0';
                if(check(board, x, y)) {
     
                    if(x == N-1 && y == N-1) return true;
                    else if(y < N-1) {
     
                        flag = dfs(board, x, y+1);
                    }
                    else if(x < N-1) {
     
                        flag = dfs(board, x+1, 0);
                    }
                }
                if(flag) return true;
            }
            board[x][y] = '.';
        }
        return false;
    }
    bool check(vector<vector<char> > &board, int x, int y) {
     
        for(int i=0; i<N; i++) {
         //检查每一行
            if(i!=x && board[i][y]==board[x][y]) return false;
            if(i!=y && board[x][i]==board[x][y]) return false;
        }
        int begin_x = x/3*3, begin_y = y/3*3;
        for(int i=0; i<3; i++) {
     
            for(int j=0; j<3; j++) {
     
                if((begin_x + i == x) && (begin_y + j == y)) continue; 
                if(board[begin_x+i][begin_y+j] == board[x][y]) return false;
            }
        }
        return true;
    }
};

最小覆盖子串(字符串滑动窗口)

给出两个字符串S和T,要求在O(n)的时间复杂度内在S中找出最短的包含T中所有字符的子串

滑动窗口基本思想:用两个字典分别维护窗口中字符的统计数量,用双指针遍历主字符串,在遍历过程中,不断增大、缩小窗口

class Solution {
     
public:
    /**
     * 
     * @param S string字符串 
     * @param T string字符串 
     * @return string字符串
     */
    string minWindow(string S, string T) {
     
        // write code here
        int T_len = T.size(), S_len = S.size();
        int mp[128] = {
     0};    //mp记录每个字符出现的次数
        int begin = 0, end = 0, counter = T_len, minlen = INT_MAX, pos;
        for(int i=0; i<T_len; i++) {
     
            mp[T[i]]++;
        }
        while(end < S_len) {
     
            if(mp[S[end++]]-- > 0) {
     
                counter--;
            }
            while(counter == 0) {
     
                if((end-begin) < minlen) {
     
                    minlen = end - begin;
                    pos = begin;
                }
                if(mp[S[begin++]]++ == 0) {
     
                    counter++;
                }
            }
        }
        return minlen==INT_MAX? "": S.substr(pos, minlen);
    }
};

把数字翻译成字符串有多少种翻译法(动态规划)

动态规划,主要注意对0的处理

class Solution {
     
public:
    /**
     * 解码
     * @param nums string字符串 数字串
     * @return int整型
     */
    int solve(string nums) {
     
        // write code here
        int len = nums.size();
        if(len == 0) return 0;
        if(nums[0] == '0') return 0;
        vector<int> dp(len+1, 1);
        dp[1] = 1;
        for(int i=1; i<len; i++) {
     
            string temp = "";
            if(nums[i] == '0' && nums[i-1] == '0') {
         //100
                return 0;
            }
            if(nums[i] == '0') {
     
                dp[i+1] = dp[i];
            }
            else if(nums[i-1] == '1' || (nums[i-1] == '2' && nums[i] <= '6')) {
     
                dp[i+1] = dp[i] + dp[i-1];
            }
            else {
     
                dp[i+1] = dp[i];
            }
        }
        return dp[len];
    }
};

你可能感兴趣的:(算法,算法)