牛客网与leetcode刷题(高频题中简单or中等的)


感觉所有算法里面掌握最好的还是回溯,其他的都是半吊子。

1、反转链表

反转链表
双指针法

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

2、排序

排序
这里我们选择使用快速排序来求解:

class Solution {
    
    
public:
    void Quicksort(vector<int>& a, int s, int t)
    {
    
    
        int i, j;
        if (s < t)
        {
    
    
            //【1】设置两个变量i、j.分别指向首元素和尾元素,设定i指向的首元素为基准元素
            i = s;
            j = t + 1;
            while (1)
            {
    
    
                do i++;
                while (!(a[s] <= a[i] || i == t));               //【2】重复i++操作,直到i指向的元素>=基准元素,或者i指向尾部
                do j--;
                while (!(a[s] >= a[j] || j == s));              //【3】反复执行j--,直到指向的元素<基准元素,或者j指向头部
                if (i < j)                                  //【5】若此时i<j,将i和j指向的元素进行交换。(大的元素在后面)
                {
    
    
                    swap(a[j], a[i]);
                }
                else break;                                  //【5】完成第一次交换后,重复执行步骤1、2,直到i>=j位置
            }
            //【6】此时i>=j,然后将基准元素与j指向的元素交换位置,至此完成了原序列的第一次划分
            swap(a[s], a[j]);
            //【7】接下来分别对基准元素前后的子序列中长度大于1的子序列重复执行上述操作。
            Quicksort(a, s, j - 1);                             //前半序列
            Quicksort(a, j + 1, t);                             //后半序列
        }
    }
    vector<int> MySort(vector<int>& arr) {
    
    
        // write code here
        Quicksort(arr,0,arr.size()-1);
        return arr;
    }
};

3、先序中序后序遍历

递归法:

class Solution {
    
    
public:
    /**
     * 
     * @param root TreeNode类 the root of binary tree
     * @return int整型vector<vector<>>
     */
    vector<int> pre;
    vector<int> mid;
    vector<int> post;
    void preorder(TreeNode* root)
    {
    
    
        if(root == nullptr) return;
        pre.push_back(root->val);
        preorder(root->left);
        preorder(root->right);
    }
    void midorder(TreeNode* root)
    {
    
    
        if(root == nullptr) return;
        midorder(root->left);
        mid.push_back(root->val);
        midorder(root->right);
    }
    void postorder(TreeNode* root)
    {
    
    
        if(root == nullptr) return;
        postorder(root->left);
        postorder(root->right);
        post.push_back(root->val);
    }
    vector<vector<int> > threeOrders(TreeNode* root) {
    
    
        // write code here
        pre.clear();
        mid.clear();
        post.clear();
        preorder(root);
        midorder(root);
        postorder(root);
        return {
    
    pre,mid,post};
    }
};

4、最小的k个数

sort + 取值

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

方法二:堆
我们用一个大根堆实时维护数组的前 k 小值。首先将前 k个数插入大根堆中,随后从第 k+1 个数开始遍历,如果当前遍历到的数比大根堆的堆顶的数要小,就把堆顶的数弹出,再插入当前遍历到的数。最后将大根堆里的数存入数组返回即可。在下面的代码中,由于 C++ 语言中的堆(即优先队列)为大根堆,我们可以这么做。

class Solution {
    
    
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
    
    
        if(k > input.size()) return {
    
    };
        if(k == 0) return {
    
    };
        priority_queue<int> que;
        for(int i = 0; i < k; i++)
        {
    
    
            que.push(input[i]);
        }
        int size = (int)input.size();
        for(int i = k; i < size; i++)
        {
    
    
            if(input[i] < que.top())
            {
    
    
                que.pop();
                que.push(input[i]);
            }
        }
        vector<int> res(k,0);
        for(int i = 0; i < k; i++)
        {
    
    
            res[i] = que.top();
            que.pop();
        }
        return res;
    }
};

如果是要求最大的k个数,就用小根堆
在这里插入图片描述

5、子数组的最大累加和

https://leetcode-cn.com/problems/lian-xu-zi-shu-zu-de-zui-da-he-lcof/

class Solution {
    
    
public:
    int maxSubArray(vector<int>& nums) {
    
    
        if(nums.empty()) return 0;
        int res =INT_MIN;
        int sum = 0;
        for(int i = 0; i < nums.size(); i++)
        {
    
    
            sum += nums[i];
            if(sum > res) res = sum;
            if(sum < 0) sum = 0;
        }
        return res;
    }
};

6、 用两个栈实现队列

用两个栈实现队列
一个输入栈,一个输出栈。
push数据的时候,只要把数据放进输入栈即可。
pop的时候:
1、输出栈如果为空,就将进栈的数据全部导入,再出栈弹出数据。
2、如果栈不为空,直接从出栈弹出数据

class Solution
{
    
    
public:
    void push(int x) {
    
    
        stIn.push(x);
    }

    int pop() {
    
    
        if(stOut.empty())
        {
    
    
            while(!stIn.empty())
            {
    
    
                stOut.push(stIn.top());
                stIn.pop();
            }
        }
        //out有元素了,那么就pop
        int result = stOut.top();
        stOut.pop();
        return result;
    }

private:
    stack<int> stIn;
    stack<int> stOut;
};

7、142. 环形链表 II

环形链表 II
使用快慢指针:
通过数学证明,x = z;
即:从头节点出发一个index,从相遇地点出发一个index,每次移动一点,直到两者相遇,相遇地点就是入口
这里给出牛客网同样题目的代码:
https://www.nowcoder.com/practice/6e630519bf86480296d0f1c868d425ad?tpId=117&tqId=37713&companyId=134&rp=1&ru=%2Fcompany%2Fhome%2Fcode%2F134&qru=%2Fta%2Fjob-code-high%2Fquestion-ranking&tab=answerKey

class Solution {
    
    
public:
    ListNode *detectCycle(ListNode *head) {
    
    
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast != nullptr && fast->next != nullptr)
        {
    
    
            slow = slow->next;
            fast = fast->next->next;
            //当两者相遇的时候,开始计算入口地点
            while(slow == fast)
            {
    
    
                ListNode* index1 = slow;
                ListNode* index2 = head;
                while(index1 != index2)
                {
    
    
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index1;
            }
        }
        //如果遇到nullptr,说明没有环
        return nullptr;
    }
};

8、20. 有效的括号

https://leetcode-cn.com/problems/valid-parentheses/
使用栈的时候三者对应到的栈的情况;
1、已经遍历完字符串,但是栈不为空
2、遍历字符串匹配的过程中,栈已经为空了
3、再遍历字符串匹配的过程中,发现栈中没有要匹配的字符。

class Solution {
    
    
public:
    bool isValid(string s) {
    
    
        stack<char> st;
        for(int i = 0; i < s.size(); i++)
        {
    
    
            
            if(s[i] == '(') st.push(')');
            else if(s[i] == '[') st.push(']');
            else if(s[i] == '{') st.push('}');
            //接下来就是判断,这个是1、3情况
            else if(st.empty() || st.top() != s[i])
            {
    
    
                return false;
            }
            //如果匹配,那么出栈
            else
            {
    
    
                st.pop();
            }
        }
        //第2种情况,遍历完字符串,栈不为空
        return st.empty();
    }
};

9、最长公共子串(动态规划),磕磕绊绊

给定两个字符串str1和str2,输出两个字符串的最长公共子串
题目保证str1和str2的最长公共子串存在且唯一。
1≤∣str1∣,∣str2∣≤5000

“1AB2345CD”,“12345EF”
“2345”

1、把两个字符串分别以行和列组成一个二维矩阵。

2、比较二维矩阵中每个点对应行列字符中否相等,相等的话值设置为1,否则设置为0。

3、通过查找出值为1的最长对角线就能找到最长公共子串。
在这里插入图片描述

    string LCS(string str1, string str2) {
    
    
        // write code here
        int len1 = str1.size();
        int len2 = str2.size();
        int start_index = 0;
        int res_length = 0;
        if(len1 == 0 || len2 == 0) return "";
        vector<vector<int>> f(len1+1,vector<int>(len2+1,0));
        for(int i=0;i < len1;i++)		//防止数组越界
        {
    
    
            for(int j=0;j < len2;j++)
            {
    
    
                //i-1:text中的第i个字符			
                if(str1[i] == str2[j])
                {
    
    
                    if( i == 0 || j == 0)
                    {
    
    
                        f[i][j] = 1;
                    }
                    else
                    {
    
    
                        f[i][j] = f[i-1][j-1] + 1;
                    }
                }
                else f[i][j] = 0;
                //更新坐标
                if(f[i][j] >= res_length)
                {
    
    
                    start_index = i - res_length;
                    res_length = f[i][j];
                }
            }
        }
//         cout << start_index << endl;
//         cout << res_length << endl;
        return str1.substr(start_index,res_length);
    }

10、二叉树之字形层序遍历

层序遍历+flag标志即可

    vector<vector<int> > zigzagLevelOrder(TreeNode* root) {
    
    
        // write code here
        queue<TreeNode*> que;
        if(root != nullptr) que.push(root);
        vector<vector<int>> result;
        int flag = 0;
        while(!que.empty())
        {
    
    
            int size = que.size();
            vector<int> vec;
            for(int i = 0; i < size; i++)
            {
    
    
                TreeNode* node = que.front();
                que.pop();
                vec.push_back(node->val);
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
            if(flag == 0)
            {
    
    
                result.push_back(vec);
                flag = 1;
            }
            else
            {
    
    
                reverse(vec.begin(),vec.end());
                result.push_back(vec);
                flag = 0;
            }
        }
        return result;
    }

11、重建二叉树

https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
    
    
public:
    TreeNode* traversal(vector<int>& preorder,int prestart,int preend,vector<int>& inorder,int instart,int inend)
    {
    
    
        //首先,检查先序数组大小,根据下标
        if(prestart == preend)
        {
    
    
            return nullptr;
        }
        //先序数组中第一个节点就是当前中间节点,new一个节点
        int rootval = preorder[prestart];
        TreeNode* root = new TreeNode(rootval);
        //如果是叶子节点,就不需要分割了,直接返回该节点
        if(preend - prestart == 1)
        {
    
    
            return root;
        }
        //中序数组找切割点
        int splitIndex = instart;    
        //注意,区间是左闭右开的
        for(splitIndex = instart; splitIndex < inend; splitIndex++)
        {
    
    
            if(inorder[splitIndex] == rootval) break;
        }
        //切割中序数组
        int leftInbegin = instart;
        int leftInend = splitIndex;
        int rightInbegin = splitIndex + 1;
        int rightInend = inend;
        //切割先序数组
        int leftPrebegin = prestart + 1;
        int leftPreend = prestart + leftInend - leftInbegin;
        int rightPrebegin = leftPreend;
        int rightPreend = preend;
        //递归处理左区间和右区间
        root->left = traversal(preorder,leftPrebegin,leftPreend,inorder,leftInbegin,leftInend);
        root->right = traversal(preorder,rightPrebegin,rightPreend,inorder,rightInbegin,rightInend);
        return root;
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
    
    
        if(preorder.empty() || inorder.empty()) return nullptr;
        return traversal(preorder,0,preorder.size(),inorder,0,inorder.size());
    }
};

12、LRU缓存

struct DLinkedNode {
    
    
public:
    int key;
    int value;
    DLinkedNode* prev;
    DLinkedNode* next;
    DLinkedNode() : key(0), value(0), prev(nullptr), next(nullptr) {
    
    };
    DLinkedNode(int _key, int _value) : key(_key), value(_value), prev(nullptr), next(nullptr) {
    
    };
};
class LRUCache {
    
    
private:
    unordered_map<int,DLinkedNode*> cache;
    //dummyhead and dummytail
    DLinkedNode* dummyhead;
    DLinkedNode* dummytail;
    // now size of cache
    int _size;
    // capacity of cache
    int _capacity;
public:
    LRUCache(int capacity) {
    
    
        _capacity = capacity;
        _size = 0;
        dummyhead = new DLinkedNode();
        dummytail = new DLinkedNode();
        dummyhead->next = dummytail;
        dummytail->prev = dummyhead;
    }
    ~LRUCache() {
    
    
		delete dummyhead;
		delete dummytail;
	}
    int get(int key) {
    
    
        // if not find
        if(cache.find(key) == cache.end())
        {
    
    
            return -1;
        }
        //if find
        //haxi 
        DLinkedNode* node = cache[key];
        //move this Node to head
        moveHead(node);
        return node->value;
    }
    
    void put(int key, int value) {
    
    
        // if key not exist ,create it
        if(cache.find(key) == cache.end())
        {
    
    
            // if size > capacity delete tail node
            if(_size + 1 > _capacity)
            {
    
    
                DLinkedNode* deleted_node = deleteTail();
                cache.erase(deleted_node->key);
                delete deleted_node;
                _size--;
            }
            DLinkedNode* newnode = new DLinkedNode(key,value);
            cache[key] = newnode;
            addHead(newnode);
            _size++;
        }
        else        //chage value
        {
    
    
            DLinkedNode* node = cache[key];
            node->value = value;
            moveHead(node);
        }
    }
    void addHead(DLinkedNode* node)
    {
    
    
        //双向指针操作 + 虚拟头节点
        node->prev = dummyhead;
        node->next = dummyhead->next;
        dummyhead->next->prev = node;
        dummyhead->next = node;
    }
    DLinkedNode* deleteTail()
    {
    
    
        //尾节点是虚拟tail前面一个。
        DLinkedNode* node = dummytail->prev;
        deleteNode(node);
        return node;
    }
    void deleteNode(DLinkedNode* node)
    {
    
    
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }
    void moveHead(DLinkedNode* node)
    {
    
    
        //先删除当前节点
        deleteNode(node);
        //然后将它加入头部
        addHead(node);
    }
};

13、合并两个有序链表

https://leetcode-cn.com/problems/merge-two-sorted-lists/

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
    
    
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
    
    
        ListNode* dummyHead = new ListNode(-1);
        ListNode* cur = dummyHead;
        while(l1 != nullptr && l2 != nullptr)
        {
    
    
            if(l1->val > l2->val)
            {
    
    
                cur->next = l2;
                cur = cur->next;
                l2 = l2->next;
            }
            else
            {
    
    
                cur->next = l1;
                cur = cur->next;
                l1 = l1->next;
            }
        } 
        while(l1 != nullptr) 
        {
    
    
            cur->next = l1;
            cur = cur->next;
            l1 = l1->next;
        }
        while(l2 != nullptr)
        {
    
    
            cur->next = l2;
            cur = cur->next;
            l2 = l2->next;
        }
        ListNode* ret = dummyHead->next;
        delete dummyHead;
        return ret;
    }
};

15、大数加法

大数加法

class Solution {
    
    
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 计算两个数之和
     * @param s string字符串 表示第一个整数
     * @param t string字符串 表示第二个整数
     * @return string字符串
     */
    string solve(string s, string t) {
    
    
        // write code here
        if(s.empty()) return t;
        if(t.empty()) return s;
        if(s.size()<t.size()) swap(t,s);
        int tail = s.size() - t.size();
        int tmp = 0;
        //补零操作 t是短的那个
        while(tail--) t = '0'+t;
        //每次只处理1位,从低到高
        for(int i=s.size()-1;i>=0;i--){
    
    
            tmp = s[i]-'0' + t[i] -'0' + tmp;
            s[i] = tmp%10 + '0';
            tmp/=10;
        }
        //最高位是否有进位位
        if(tmp) s = '1'+s;
        return s;
    }
};

16、一个二叉树和一个值sum,请找出所有的根节点到叶子节点的节点值之和等于sum 的路径

一个二叉树和一个值sum…
普通的回溯即可。

class Solution {
    
    
public:
    /**
     * 
     * @param root TreeNode类 
     * @param sum int整型 
     * @return int整型vector<vector<>>
     */
    vector<vector<int> > result;
    vector<int> path;
    void traversal(TreeNode* root,int count)
    {
    
    
        if(root->left == nullptr && root->right == nullptr)
        {
    
    
            if(count == 0) 
            {
    
    
                result.push_back(path);
            }
            return ;
        }
        if(root->left)
        {
    
    
            count -= root->left->val;
            path.push_back(root->left->val);
            traversal(root->left,count);
            path.pop_back();
            count += root->left->val;
        }
        if(root->right)
        {
    
    
            count -= root->right->val;
            path.push_back(root->right->val);
            traversal(root->right,count);
            path.pop_back();
            count += root->right->val;
        }
        return ;
    }
    vector<vector<int> > pathSum(TreeNode* root, int sum) {
    
    
        // write code here
        path.clear();
        result.clear();
        if(root == nullptr) return result;
        path.push_back(root->val);
        traversal(root,sum - root->val);
        return result;
    }
};

17、寻找第K大

18、买卖股票的最佳时机

int maxProfit(vector<int>& prices) {
    
    
        // write code here
        int n = prices.size();
        vector<int> dp(n+1,0);
        int minnum = prices[0];
        for(int i = 1; i < n; i++)
        {
    
    
            minnum = min(minnum,prices[i]);
            dp[i] = max(prices[i] - minnum,dp[i-1]);
        }
        return dp[n-1];
    }

可以使用滚动数组优化:

int maxProfit(vector<int>& prices) {
    
    
        int n=prices.size();
        if(n==0) return 0;
        int ans=0;
        int minnum =prices[0];
        for(int i=1;i<n;i++)
        {
    
    
            minnum=min(minnum,prices[i]);
            ans=max(ans,prices[i]-minnum);
        }
        if(ans<=0)ans=0;
        return ans;
    }

19、二数之和

https://leetcode-cn.com/problems/two-sum/

class Solution {
    
    
public:
    vector<int> twoSum(vector<int>& nums, int target) {
    
    
        unordered_map<int,int > umap;
        for(int i = 0; i < nums.size(); i++)
        {
    
    
            auto iter = umap.find(target - nums[i]);
            //如果找到了
            if(iter != umap.end())
            {
    
    
                return {
    
    i,iter->second};
            }
            //否则,进行插入操作
            umap.insert(pair<int,int>(nums[i],i));
        }
        return {
    
    };
    }
};

20、合并两个有序数组

void merge(int A[], int m, int B[], int n) {
    
    
        int l1 = 0;
        int l2 = 0;
        vector<int> res;
        while(l1 < m && l2 < n)
        {
    
    
            if(A[l1] <= B[l2])
            {
    
    
                res.push_back(A[l1]);
                l1++;
            }
            else
            {
    
    
                res.push_back(B[l2]);
                l2++;
            }
        }
        while(l1 < m) 
        {
    
    
            res.push_back(A[l1]);
            l1++;
        }
        while(l2 < n) 
        {
    
    
            res.push_back(B[l2]);
            l2++;
        }
        for(int i = 0; i < res.size(); i++)
            A[i] = res[i];
    }

21、二叉树的最近公共祖先

NC二叉树的最近公共祖先

    int lowestCommonAncestor(TreeNode* root, int o1, int o2) {
    
    
        // write code here
        if(root == nullptr) return -1;
        if(o1 == root->val || o2 == root->val) return root->val;
        int left = lowestCommonAncestor(root->left,o1,o2);
        int right = lowestCommonAncestor(root->right,o1,o2);
        if(left == -1) return right;
        if(right == -1) return left;
        return root->val;
    }

猜你喜欢

转载自blog.csdn.net/qq_42604176/article/details/114199583